> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dzap.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Approval Mechanisms

> Complete guide to token approval workflows and gas optimization

## Token Approval Mechanism

Before executing any transaction that spends ERC20 tokens (swap, bridge, zap, etc.), a spender must be approved to access those tokens. The SDK provides multiple approval modes to handle different scenarios optimally.

`approve()`'s `mode` param takes an `ApprovalModes` value (`Default`, `PermitSingle`, `PermitWitnessTransferFrom`, `PermitBatchWitnessTransferFrom`, `AutoPermit`). `sign()`'s `permitType` param takes a `PermitTypes` value — the same set, plus `EIP2612Permit` (which has no `approve()`/`ApprovalModes` counterpart, since EIP-2612 tokens never need an on-chain approval transaction).

## Recommended Approval Flow

`getAllowance()` returns `data` keyed by token address, each entry `{ allowance: bigint, type: 'dzap' | 'permit2' | 'eip2612' }`. There is no `approvalNeeded`/`signatureNeeded` field — derive those from `allowance`/`type`:

```typescript theme={null}
import { Services, ApprovalModes, PermitTypes } from "@dzapio/sdk";
import type { HexString } from "@dzapio/sdk";

async function completeApprovalFlow(
  tokens: { address: HexString; amount: string }[],
  service: typeof Services.trade | typeof Services.zap,
  userAddress: HexString,
  chainId: number,
  signer: any,
) {
  // 1. Check current allowances (data is keyed by token address)
  const allowanceCheck = await dZap.getAllowance({
    chainId,
    sender: userAddress,
    tokens,
    service,
    mode: ApprovalModes.AutoPermit,
  });

  const data = allowanceCheck.data ?? {};
  const tokensForApproval = tokens.filter((t) => {
    const entry = data[t.address];
    return entry && entry.type !== "eip2612" && entry.allowance < BigInt(t.amount);
  });
  const tokensForSignature = tokens.filter((t) => data[t.address]?.type !== "dzap");

  // 2. Handle approvals if needed (use mode, not permitType)
  if (tokensForApproval.length > 0) {
    await dZap.approve({
      chainId,
      signer,
      sender: userAddress,
      tokens: tokensForApproval,
      service,
      mode: ApprovalModes.AutoPermit,
      approvalTxnCallback: async ({ txnDetails, address }) => {
        console.log(`Approval tx for ${address}:`, txnDetails);
      },
    });
  }

  // 3. Handle permit signatures if needed
  if (tokensForSignature.length > 0) {
    const result = await dZap.sign({
      chainId,
      sender: userAddress,
      tokens: tokensForSignature.map((t) => ({ address: t.address, amount: t.amount })),
      signer,
      service,
      permitType: PermitTypes.AutoPermit,
    });

    if (result.status === "success") {
      console.log("Permit result:", "batchPermitData" in result ? result.batchPermitData : result.tokens);
    }
    return result;
  }
}
```

## Approval Modes Deep Dive

### 1. Default Mode (Standard ERC20)

Traditional approval directly to the DZap contract.

<Tabs>
  <Tab title="Implementation">
    ```typescript theme={null}
    const tokens = [{ address: tokenAddress as HexString, amount: "1000000" }];
    const allowanceCheck = await dZap.getAllowance({
      chainId: 1,
      sender: userAddress,
      tokens,
      service: Services.trade,
      mode: ApprovalModes.Default,
    });

    const data = allowanceCheck.data ?? {};
    const tokensForApproval = tokens.filter(
      (t) => (data[t.address]?.allowance ?? 0n) < BigInt(t.amount)
    );

    if (tokensForApproval.length > 0) {
      await dZap.approve({
        chainId: 1,
        signer,
        sender: userAddress,
        tokens: tokensForApproval,
        service: Services.trade,
        mode: ApprovalModes.Default,
      });
    }
    ```
  </Tab>

  <Tab title="Characteristics">
    * **Gas Cost**: High (separate approval transaction)
    * **Setup**: None required
    * **Token Support**: All ERC20 tokens
    * **User Experience**: Requires 2 transactions
    * **Security**: Standard ERC20 approval model
  </Tab>

  <Tab title="Best For">
    * One-time transactions
    * Tokens without permit support
    * Legacy systems
    * Maximum compatibility
  </Tab>
</Tabs>

### 2. Permit2 Modes

Uses Uniswap's Permit2 for gas-efficient repeated approvals. There are three `ApprovalModes`/`PermitTypes` variants: `PermitSingle` (one token, standard permit), `PermitWitnessTransferFrom` (one token, witness-bound to the specific trade), and `PermitBatchWitnessTransferFrom` (multiple tokens in one signature).

<Tabs>
  <Tab title="Implementation">
    ```typescript theme={null}
    const tokens = [{ address: tokenAddress as HexString, amount: "1000000" }];
    const allowanceCheck = await dZap.getAllowance({
      chainId: 1,
      sender: userAddress,
      tokens,
      service: Services.trade,
      mode: ApprovalModes.PermitWitnessTransferFrom,
    });

    const data = allowanceCheck.data ?? {};
    const tokensForApproval = tokens.filter(
      (t) => (data[t.address]?.allowance ?? 0n) < BigInt(t.amount)
    );

    if (tokensForApproval.length > 0) {
      await dZap.approve({
        chainId: 1,
        signer,
        sender: userAddress,
        tokens: tokensForApproval,
        service: Services.trade,
        mode: ApprovalModes.PermitWitnessTransferFrom,
      });
    }

    const result = await dZap.sign({
      chainId: 1,
      sender: userAddress,
      tokens: tokens.map((t) => ({ address: t.address, amount: t.amount })),
      signer,
      service: Services.trade,
      permitType: PermitTypes.PermitWitnessTransferFrom,
    });

    if (result.status === "success") {
      console.log("Permit signature data:", result.tokens);
    }
    ```
  </Tab>

  <Tab title="Characteristics">
    * **Gas Cost**: Low for repeated use
    * **Setup**: One-time approval to the Permit2 contract per token
    * **Token Support**: ERC20 tokens with Permit2 support (almost all ERC20 tokens)
    * **User Experience**: Single approval transaction, then gas-less signatures
    * **Security**: Time-bound permits, reduced attack surface
  </Tab>

  <Tab title="Benefits">
    * **Initial Cost**: One-time approval per token
    * **Ongoing Cost**: Gas-less signatures
    * **Flexibility**: `PermitBatchWitnessTransferFrom` covers multiple tokens in one signature
    * **Security**: Time-bound permits
    * **UX**: No repeated approvals
  </Tab>
</Tabs>

### 3. EIP-2612 Permit Mode

Direct permit signatures to the DZap contract for supported tokens — no `ApprovalModes`/`approve()` call at all, only `sign()`.

<Tabs>
  <Tab title="Implementation">
    ```typescript theme={null}
    // Check if token supports EIP-2612 (from @dzapio/sdk)
    import { checkEIP2612PermitSupport } from "@dzapio/sdk";

    const { supportsPermit } = await checkEIP2612PermitSupport({
      address: tokenAddress,
      chainId: 1,
      rpcUrls: [],
      owner: userAddress,
    });

    if (supportsPermit) {
      const result = await dZap.sign({
        chainId: 1,
        sender: userAddress,
        tokens: [{ address: tokenAddress as HexString, amount: "1000000" }],
        signer,
        permitType: PermitTypes.EIP2612Permit,
        service: Services.trade,
      });

      if (result.status === "success" && "tokens" in result) {
        console.log("Permit signature data:", result.tokens);
      }
    } else {
      // Fallback to Default or Permit2 approval
    }
    ```
  </Tab>

  <Tab title="Characteristics">
    * **Gas Cost**: Gas-less signatures
    * **Setup**: None required
    * **Token Support**: ERC20 tokens with EIP-2612 support
    * **User Experience**: Single signature for gas-less approval
    * **Security**: Time-bound permits, reduced attack surface
  </Tab>

  <Tab title="Benefits">
    * **Zero Gas**: No approval transaction needed
    * **Direct**: No intermediate contracts
    * **Immediate**: Works instantly
    * **Secure**: Built into token contract
  </Tab>
</Tabs>

### 4. Auto Permit Mode (Recommended)

Automatically selects the optimal approval method: `getAllowance({ mode: ApprovalModes.AutoPermit })` checks EIP-2612 support first (`type: 'eip2612'` in the response) and falls back to Permit2 (`type: 'permit2'`) otherwise.

<Tabs>
  <Tab title="Usage">
    ```typescript theme={null}
    const allowanceCheck = await dZap.getAllowance({
      chainId: 1,
      sender: userAddress,
      tokens,
      service: Services.trade,
      mode: ApprovalModes.AutoPermit,
    });

    const data = allowanceCheck.data ?? {};
    const needsApproval = tokens.some((t) => {
      const entry = data[t.address];
      return entry && entry.type !== "eip2612" && entry.allowance < BigInt(t.amount);
    });
    const needsSignature = tokens.some((t) => data[t.address]?.type !== "dzap");

    if (needsApproval) {
      await dZap.approve({ chainId: 1, signer, sender: userAddress, tokens, service: Services.trade, mode: ApprovalModes.AutoPermit });
    }
    if (needsSignature) {
      await dZap.sign({ chainId: 1, sender: userAddress, tokens, signer, service: Services.trade, permitType: PermitTypes.AutoPermit });
    }
    ```
  </Tab>
</Tabs>

## approvalTxnCallback

The `approve()` method accepts an optional `approvalTxnCallback` that is invoked for each approval transaction (viem `WalletClient` signers only; e.g. for logging or UI updates):

```typescript theme={null}
await dZap.approve({
  chainId: 1,
  signer,
  sender: userAddress,
  tokens: tokensForApproval,
  service: Services.trade,
  mode: ApprovalModes.Default,
  approvalTxnCallback: async ({ txnDetails, address }) => {
    // txnDetails: { txnHash: string; code: StatusCodes; status: TxnStatus }
    // address: token address being approved
    console.log(`Approval for ${address}:`, txnDetails);
  },
});
```

If the callback returns a `TxnStatus` other than `success`, remaining tokens in the batch are skipped.

## Native token (skip approval)

For the native token (e.g. ETH), skip approval: the SDK's native-token sentinel is `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`. `getAllowance()` already handles this internally (native tokens are returned with `{ allowance: maxUint256, type: 'dzap' }` and never need an on-chain approval), but you can also filter them out yourself before calling `approve`:

```typescript theme={null}
const NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";

const tokensForApproval = buildData
  .filter((d) => d.srcToken.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase())
  .map((d) => ({ address: d.srcToken as HexString, amount: d.amount }));
```

## Security Considerations

<CardGroup cols={2}>
  <Card title="Limit Exposure" icon="shield">
    Use exact amounts for high-value or infrequent transactions
  </Card>

  <Card title="Monitor Allowances" icon="eye">
    Regularly audit and revoke unnecessary approvals
  </Card>

  <Card title="Use Permits When Possible" icon="signature">
    Permits reduce approval attack surface
  </Card>

  <Card title="Set Expiration Times" icon="clock">
    Use reasonable deadlines for permit signatures
  </Card>
</CardGroup>
