> ## 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.

# Withdraw

> Zap out of an LP or vault position back into a plain token, on any chain.

A withdraw (Zap Out) is the reverse of a [deposit](/cookbook/zap/deposit): the router exits your position and returns a plain token. The source is the position (`srcToken = position.address`); the destination is any token on any chain (`destToken` / `destChainId`). NFT-based LP positions (Uniswap V3 and similar) are identified by `positionDetails.nftId`. Pick a chain below; the tabs stay in sync across Setup, Steps, and End-to-end.

<Note>EVM withdrawals authorize the position token through Permit2 (no EIP-2612). Solana withdrawals carry no approval step, you sign and send the built transactions directly.</Note>

## Setup

<Snippet file="install-sdk.mdx" />

<Tabs>
  <Tab title="EVM">
    Exit a Uniswap V3 position on Base back into USDC.

    ```ts theme={null}
    import { DZapClient, Services, ApprovalModes, PermitTypes, TxnStatus } from '@dzapio/sdk';
    import { createWalletClient, http } from 'viem';
    import { base } from 'viem/chains';
    import { privateKeyToAccount } from 'viem/accounts';

    const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
    const walletClient = createWalletClient({ account, chain: base, transport: http() });
    const dzap = DZapClient.getInstance();

    const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';   // token you want back
    ```

    <Note>
      Protocol IDs (`provider`) come from [`getZapProviders`](/sdk/zap/request-quotes), scoped per chain by [`getZapChains`](/sdk/zap/request-quotes).
    </Note>
  </Tab>

  <Tab title="Solana">
    Exit a Jupiter Lend position on Solana back into USDC. Needs `@solana/web3.js` alongside the SDK.

    ```ts theme={null}
    import { DZapClient } from '@dzapio/sdk';
    import { Connection, VersionedTransaction } from '@solana/web3.js';

    const dzap = DZapClient.getInstance();
    const connection = new Connection('https://api.mainnet-beta.solana.com');

    // your Solana wallet adapter (wallet-standard, Keypair, etc.)
    const account = wallet.publicKey.toBase58();
    const USDC_SOL = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';   // token you want back
    ```

    <Note>
      Protocol IDs (`provider`) come from [`getZapProviders`](/sdk/zap/request-quotes), scoped per chain by [`getZapChains`](/sdk/zap/request-quotes).
    </Note>
  </Tab>
</Tabs>

## Steps

<Tabs>
  <Tab title="EVM">
    <Steps>
      <Step title="Find the position">
        List the account's open positions for a protocol, then pick the one to exit. Each position carries its `address`, `chainId`, `amount`, and optional `nftDetails`.

        ```ts theme={null}
        const { positions } = await dzap.getZapPositions({
          account: account.address,
          chainId: 8453,
          provider: 'uniswap',
        });

        const position = positions[0];   // assumes at least one open position
        ```

        <Note>
          Withdraw the full balance with `position.amount`, or pass any smaller amount for a partial exit.
        </Note>
      </Step>

      <Step title="Quote">
        Build the exit route. The source token is the position; the destination is whatever token and chain you want back. Read-only.

        ```ts theme={null}
        const request = {
          srcChainId: position.chainId,
          srcToken: position.address,   // the position you are exiting
          amount: position.amount,
          destChainId: 8453,
          destToken: USDC_BASE,         // token you want back
          account: account.address,
          recipient: account.address,
          refundee: account.address,
          slippage: 1,                  // 1 = 1%
          ...(position.nftDetails && {
            positionDetails: { nftId: position.nftDetails.id },
          }),
        };

        const quote = await dzap.getZapQuote(request);
        console.log('Route:', quote.path.map((p) => p.protocol.name).join(' -> '));
        console.log('Expected:', quote.output[0]?.amount, quote.output[0]?.asset.symbol);
        ```
      </Step>

      <Step title="Approve">
        Authorize the position token through Permit2, approving once on-chain only if the allowance is short.

        ```ts theme={null}
        const { data } = await dzap.getAllowance({
          chainId: position.chainId,
          sender: account.address,
          tokens: [{ address: position.address, amount: position.amount }],
          service: Services.zap,
          mode: ApprovalModes.PermitSingle,
        });

        if (data[position.address].allowance < BigInt(position.amount)) {
          await dzap.approve({
            chainId: position.chainId,
            signer: walletClient,
            sender: account.address,
            tokens: [{ address: position.address, amount: position.amount }],
            service: Services.zap,
            mode: ApprovalModes.PermitSingle,
          });
        }
        ```
      </Step>

      <Step title="Build & execute">
        Sign the Permit2 `PermitSingle`, attach `permitData`, build the route, and execute.

        ```ts theme={null}
        const permit = await dzap.sign({
          chainId: position.chainId,
          sender: account.address,
          service: Services.zap,
          signer: walletClient,
          tokens: [{ address: position.address, amount: position.amount }],
          permitType: PermitTypes.PermitSingle,
        });

        const fullRequest = { ...request, permitData: permit.tokens[0].permitData };
        const route = await dzap.buildZapTxn(fullRequest);
        const result = await dzap.zap({ request: fullRequest, steps: route.steps, signer: walletClient });
        console.log(`Withdrew: ${result.txnHash}`);
        ```
      </Step>

      <Step title="Status">
        Poll to a terminal state. Zap status is uppercase: `PENDING`, `COMPLETED`, `FAILED`, or `REFUNDED`.

        ```ts theme={null}
        const status = await dzap.getZapTxnStatus({
          chainId: position.chainId,
          txnHash: result.txnHash!,
        });

        console.log(status.status);   // PENDING -> COMPLETED
        ```

        <Note>
          Cross-chain withdrawals (`destChainId` different from the position's chain) run exit-then-bridge and settle slower. `status.steps` shows each leg.
        </Note>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Solana">
    <Steps>
      <Step title="Find the position">
        List the account's open positions for a protocol on Solana (chain ID `7565164`), then pick the one to exit. Each position carries its `address`, `chainId`, and `amount`.

        ```ts theme={null}
        const { positions } = await dzap.getZapPositions({
          account,
          chainId: 7565164,
          provider: 'jupiterlend',
        });

        const position = positions[0];   // assumes at least one open position
        ```

        <Note>
          Withdraw the full balance with `position.amount`, or pass any smaller amount for a partial exit.
        </Note>
      </Step>

      <Step title="Quote">
        Build the exit route with base58 addresses. The source is the position; the destination is the SPL token you want back. Read-only.

        ```ts theme={null}
        const request = {
          srcChainId: position.chainId,
          srcToken: position.address,   // the position you are exiting
          amount: position.amount,
          destChainId: 7565164,
          destToken: USDC_SOL,          // token you want back
          account,
          recipient: account,
          refundee: account,
          slippage: 1,                  // 1 = 1%
        };

        const quote = await dzap.getZapQuote(request);
        console.log('Route:', quote.path.map((p) => p.protocol.name).join(' -> '));
        ```

        <Note>
          Set `destChainId` to an EVM chain and `destToken`/`recipient` to `0x` addresses to receive the token on an EVM chain instead. Keep `refundee` as your Solana address.
        </Note>
      </Step>

      <Step title="Build">
        No approval on Solana, go straight to build. The step data carries an array of base64 transactions and a `txnId`.

        ```ts theme={null}
        const route = await dzap.buildZapTxn(request);
        const step = route.steps[0].data;   // SVM step: { data: string[], txnId, isJitoTx }
        ```
      </Step>

      <Step title="Sign & send">
        Deserialize each base64 transaction, sign with your wallet, and broadcast. Jito-routed zaps go through the Jito block engine.

        ```ts theme={null}
        const txns = step.data.map((b64) =>
          VersionedTransaction.deserialize(Buffer.from(b64, 'base64')),
        );

        if (step.isJitoTx) {
          const signed = await wallet.signTransaction(txns[0]);
          await fetch('https://mainnet.block-engine.jito.wtf/api/v1/transactions', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              jsonrpc: '2.0',
              id: 1,
              method: 'sendTransaction',
              params: [Buffer.from(signed.serialize()).toString('base64'), { encoding: 'base64' }],
            }),
          });
        } else {
          const signedAll = await wallet.signAllTransactions(txns);
          for (const tx of signedAll) {
            const sig = await connection.sendRawTransaction(tx.serialize());
            await connection.confirmTransaction(sig, 'confirmed');
          }
        }
        ```
      </Step>

      <Step title="Status">
        Track it with the `txnId` from the build step.

        ```ts theme={null}
        const status = await dzap.getZapTxnStatus({
          chainId: 7565164,
          txnHash: step.txnId,
        });

        console.log(status.status);   // PENDING -> COMPLETED
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

## End-to-end

<Tabs>
  <Tab title="EVM">
    ```ts theme={null}
    import { DZapClient, Services, ApprovalModes, PermitTypes, TxnStatus } from '@dzapio/sdk';
    import { createWalletClient, http } from 'viem';
    import { base } from 'viem/chains';
    import { privateKeyToAccount } from 'viem/accounts';

    const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
    const walletClient = createWalletClient({ account, chain: base, transport: http() });
    const dzap = DZapClient.getInstance();

    const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';

    // 1. Pick a position
    const { positions } = await dzap.getZapPositions({
      account: account.address,
      chainId: 8453,
      provider: 'uniswap',
    });
    const position = positions[0];

    // 2. Quote the exit
    const request = {
      srcChainId: position.chainId,
      srcToken: position.address,
      amount: position.amount,
      destChainId: 8453,
      destToken: USDC_BASE,
      account: account.address,
      recipient: account.address,
      refundee: account.address,
      slippage: 1,
      ...(position.nftDetails && { positionDetails: { nftId: position.nftDetails.id } }),
    };
    const quote = await dzap.getZapQuote(request);
    console.log('Route:', quote.path.map((p) => p.protocol.name).join(' -> '));

    // 3. Approve (Permit2, on-chain only if short)
    const { data } = await dzap.getAllowance({
      chainId: position.chainId,
      sender: account.address,
      tokens: [{ address: position.address, amount: position.amount }],
      service: Services.zap,
      mode: ApprovalModes.PermitSingle,
    });
    if (data[position.address].allowance < BigInt(position.amount)) {
      await dzap.approve({
        chainId: position.chainId,
        signer: walletClient,
        sender: account.address,
        tokens: [{ address: position.address, amount: position.amount }],
        service: Services.zap,
        mode: ApprovalModes.PermitSingle,
      });
    }

    // 4. Sign permit, build, execute
    const permit = await dzap.sign({
      chainId: position.chainId,
      sender: account.address,
      service: Services.zap,
      signer: walletClient,
      tokens: [{ address: position.address, amount: position.amount }],
      permitType: PermitTypes.PermitSingle,
    });
    const fullRequest = { ...request, permitData: permit.tokens[0].permitData };
    const route = await dzap.buildZapTxn(fullRequest);
    const result = await dzap.zap({ request: fullRequest, steps: route.steps, signer: walletClient });
    if (result.status !== TxnStatus.success) throw new Error(result.errorMsg ?? 'withdraw failed');

    // 5. Status
    const status = await dzap.getZapTxnStatus({ chainId: position.chainId, txnHash: result.txnHash! });
    console.log('settled:', status.status);
    ```
  </Tab>

  <Tab title="Solana">
    ```ts theme={null}
    import { DZapClient } from '@dzapio/sdk';
    import { Connection, VersionedTransaction } from '@solana/web3.js';

    const dzap = DZapClient.getInstance();
    const connection = new Connection('https://api.mainnet-beta.solana.com');

    // your Solana wallet adapter (wallet-standard, Keypair, etc.)
    const account = wallet.publicKey.toBase58();
    const USDC_SOL = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';

    // 1. Pick a position
    const { positions } = await dzap.getZapPositions({
      account,
      chainId: 7565164,
      provider: 'jupiterlend',
    });
    const position = positions[0];

    // 2. Quote the exit
    const request = {
      srcChainId: position.chainId,
      srcToken: position.address,
      amount: position.amount,
      destChainId: 7565164,
      destToken: USDC_SOL,
      account,
      recipient: account,
      refundee: account,
      slippage: 1,
    };
    const quote = await dzap.getZapQuote(request);
    console.log('Route:', quote.path.map((p) => p.protocol.name).join(' -> '));

    // 3. Build
    const route = await dzap.buildZapTxn(request);
    const step = route.steps[0].data;

    // 4. Sign and send
    const txns = step.data.map((b64) =>
      VersionedTransaction.deserialize(Buffer.from(b64, 'base64')),
    );
    if (step.isJitoTx) {
      const signed = await wallet.signTransaction(txns[0]);
      await fetch('https://mainnet.block-engine.jito.wtf/api/v1/transactions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          id: 1,
          method: 'sendTransaction',
          params: [Buffer.from(signed.serialize()).toString('base64'), { encoding: 'base64' }],
        }),
      });
    } else {
      const signedAll = await wallet.signAllTransactions(txns);
      for (const tx of signedAll) {
        const sig = await connection.sendRawTransaction(tx.serialize());
        await connection.confirmTransaction(sig, 'confirmed');
      }
    }

    // 5. Status
    const status = await dzap.getZapTxnStatus({ chainId: 7565164, txnHash: step.txnId });
    console.log('settled:', status.status);
    ```
  </Tab>
</Tabs>

## API usage

The zap API lives on `https://zap.dzap.io/v1`. List positions and check status with `GET`; quote and build are `POST`. The position address is the `srcToken`, and NFT LPs add `positionDetails`.

<CodeGroup>
  ```bash Positions theme={null}
  curl "https://zap.dzap.io/v1/user/positions?account=0xUser&chainId=8453&provider=uniswap"
  ```

  ```bash Quote theme={null}
  curl -X POST https://zap.dzap.io/v1/quote \
    -H "Content-Type: application/json" \
    -d '{
      "srcChainId": 8453,
      "destChainId": 8453,
      "account": "0xUser",
      "srcToken": "0xPositionAddress",
      "destToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "<position amount>",
      "recipient": "0xUser",
      "refundee": "0xUser",
      "slippage": 1,
      "positionDetails": { "nftId": "<nft id, LP positions only>" }
    }'
  ```

  ```bash Build theme={null}
  curl -X POST https://zap.dzap.io/v1/buildTx \
    -H "Content-Type: application/json" \
    -d '{
      "srcChainId": 8453,
      "destChainId": 8453,
      "account": "0xUser",
      "srcToken": "0xPositionAddress",
      "destToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "<position amount>",
      "recipient": "0xUser",
      "refundee": "0xUser",
      "slippage": 1,
      "permitData": "0x...",
      "positionDetails": { "nftId": "<nft id, LP positions only>" }
    }'
  ```

  ```bash Status theme={null}
  curl "https://zap.dzap.io/v1/status?chainId=8453&txnHash=0xabc..."
  ```
</CodeGroup>

## Notes

* **NFT LP positions**: pass `positionDetails.nftId`. Fungible LP/vault tokens omit it.
* **Native destination** (ETH, SOL): the router unwraps to the native asset, nothing extra to configure.
* **Partial exit**: pass any amount up to `position.amount`.
* **Cross-chain**: set `destChainId` to receive the token on another chain; expect bridge latency.

Read more in [Fuse execution flow](/products/dzap-fuse/execution-flow).
