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

# Borrow

> Borrow against a lending position you already hold, or deposit collateral and borrow in the same transaction, with the DZap SDK on EVM.

A borrow takes a loan out against collateral in a lending market and sends the borrowed token to your wallet. There are two ways to do it, and the tabs on this page follow them. **From a position**: you already have collateral in the market, say aWETH on Aave, and you just want to borrow against it. **Deposit & borrow**: you start with a plain token, and one transaction supplies it as collateral and borrows against it right away.

Borrows go through the bundle endpoints (`getZapBundleQuote` / `buildZapBundleTx`), because a borrow is an action you chain with others rather than a single `srcToken` → `destToken` hop. They also need one thing the other zaps don't: a signature from you, made **before** the transaction is built. The steps below show where it fits. [Pre-execution steps](/sdk/zap/pre-execution-steps) explains why it's needed and what to watch for, and it's worth reading before you ship.

Borrowing is EVM-only for now.

## Setup

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

Both examples use Aave v3 on Base, signing with `walletClient`, a viem wallet client built from the private key in `PRIVATE_KEY`. Amounts are strings in the token's smallest unit, and no API key is needed.

```ts theme={null}
import {
  DZapClient,
  Services,
  ApprovalModes,
  PermitTypes,
  TxnStatus,
  type ZapBundleRequest,
  type ZapStatusResponse,
} 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 WETH_BASE: `0x${string}` = '0x4200000000000000000000000000000000000006';
const AWETH_BASE = '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7';   // Aave v3 aBasWETH, the collateral
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';    // what we borrow
const BORROW_AMOUNT = '100000000';                                // 100 USDC (6 decimals)
```

<Note>
  The collateral is named by its **position token**, the aToken here, not by the underlying asset. Find it with [`getZapPools`](/api/fuse/pools), and your current positions with [`getZapPositions`](/api/fuse/user).
</Note>

## Steps

<Tabs>
  <Tab title="From a position">
    <Steps>
      <Step title="Quote">
        A single `borrow` action. `srcToken` is the collateral position you already hold, with no `amount`, because nothing moves out of it: the aWETH stays where it is and backs the loan. `destToken` is what you want to borrow, and `destAmount` how much. Leave `destAmount` out and DZap borrows the most your collateral allows.

        ```ts theme={null}
        const request: ZapBundleRequest = {
          account: account.address,
          recipient: account.address,
          refundee: account.address,
          slippage: 1,                              // 1 = 1%
          actions: [
            {
              action: 'borrow',
              srcChainId: 8453,
              srcToken: { address: AWETH_BASE },    // collateral already in Aave
              destChainId: 8453,
              destToken: USDC_BASE,                 // what you borrow
              destAmount: BORROW_AMOUNT,
            },
          ],
        };

        const quote = await dzap.getZapBundleQuote(request);
        console.log(quote.output.map((o) => `${o.amount} ${o.asset.symbol}`));
        ```
      </Step>

      <Step title="Sign the delegation">
        No token approval here, since your collateral never leaves Aave. The only thing to authorize is the borrow itself: sign the credit delegation the quote asked for (see [Pre-execution steps](/sdk/zap/pre-execution-steps)). `prepareZapPreExecutionData` doesn't throw. Like `approve()` and `zap()`, it hands back a `status`, so if the user rejects the signature you get `rejected` and an `errorMsg` to show.

        ```ts theme={null}
        const prepared = await dzap.prepareZapPreExecutionData({
          preExecutionSteps: quote.preExecutionSteps,
          signer: walletClient,
          account: account.address,
        });
        if (prepared.status !== TxnStatus.success) throw new Error(prepared.errorMsg);
        const { preExecutionStepsData } = prepared;
        ```
      </Step>

      <Step title="Build & send">
        Add the signatures to the request and build. `buildZapBundleTx` checks the borrow against your Aave account, so it throws if you're asking for more than your collateral covers. `zap()` then sends the transaction, and the USDC lands at `recipient`.

        ```ts theme={null}
        const buildRequest: ZapBundleRequest = { ...request, preExecutionStepsData };
        const route = await dzap.buildZapBundleTx(buildRequest);

        const result = await dzap.zap({ request: buildRequest, steps: route.steps, signer: walletClient });
        if (result.status !== TxnStatus.success) throw new Error(result.errorMsg);
        ```
      </Step>

      <Step title="Status">
        Poll `getZapTxnStatus` with the transaction hash until it leaves `PENDING`. DZap answers 404 until it has seen the transaction and 429 when you hit the rate limit, so the `catch` lets only those two through. It gives up after 5 minutes.

        ```ts theme={null}
        let status: ZapStatusResponse | undefined;
        for (let i = 0; i < 20; i++) {  // gives up after 5 minutes
          await new Promise((r) => setTimeout(r, 15_000));
          try {
            status = await dzap.getZapTxnStatus({ chainId: 8453, txnHash: result.txnHash! });
          } catch (e: any) {
            if (![404, 429].includes(e.response?.status)) throw e;
          }
          if (status && status.status !== 'PENDING') break;
        }
        if (status?.status !== 'COMPLETED') throw new Error(`Not completed: ${status?.status ?? 'no status'}`);

        console.log(status.status); // 'COMPLETED'
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="Deposit & borrow">
    <Steps>
      <Step title="Quote">
        Two actions, in order: a `deposit` of WETH into Aave, which mints aWETH to you, then a `borrow` against that aWETH. The deposit carries the `amount`; the borrow's `srcToken` has none, because it uses whatever the deposit just supplied. Both steps run in one transaction, so you never sit with uncollateralized debt or idle collateral in between.

        ```ts theme={null}
        const DEPOSIT_AMOUNT = '100000000000000000';   // 0.1 WETH (18 decimals)

        const request: ZapBundleRequest = {
          account: account.address,
          recipient: account.address,
          refundee: account.address,
          slippage: 1,
          actions: [
            {
              action: 'deposit',
              srcChainId: 8453,
              srcToken: { address: WETH_BASE, amount: DEPOSIT_AMOUNT },
              destChainId: 8453,
              destToken: AWETH_BASE,                // the market you supply into
            },
            {
              action: 'borrow',
              srcChainId: 8453,
              srcToken: { address: AWETH_BASE },    // the collateral the deposit just created
              destChainId: 8453,
              destToken: USDC_BASE,
              destAmount: BORROW_AMOUNT,
            },
          ],
        };

        const quote = await dzap.getZapBundleQuote(request);
        console.log(quote.output.map((o) => `${o.amount} ${o.asset.symbol}`));
        ```

        <Note>
          **Starting from a token that isn't the market's asset?** Add a `swap` in front, so the bundle reads swap → deposit → borrow. Spell the swap out yourself rather than pointing the deposit at a different token and hoping it gets converted. The deposit then takes no `amount`, since it uses the swap's output, and the permit in the next steps goes on the swap, because that's now the action pulling tokens from your wallet.

          ```ts theme={null}
          const CBBTC_BASE = '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf';

          actions: [
            { action: 'swap',    srcChainId: 8453, srcToken: { address: CBBTC_BASE, amount: '100000' }, destChainId: 8453, destToken: WETH_BASE },  // 0.001 cbBTC
            { action: 'deposit', srcChainId: 8453, srcToken: { address: WETH_BASE },  destChainId: 8453, destToken: AWETH_BASE },
            { action: 'borrow',  srcChainId: 8453, srcToken: { address: AWETH_BASE }, destChainId: 8453, destToken: USDC_BASE, destAmount: BORROW_AMOUNT },
          ],
          ```

          The borrow must come straight after the deposit into the same market. Anything else in between and the build is rejected.
        </Note>
      </Step>

      <Step title="Approve">
        This time a token does leave your wallet: the WETH you're supplying. Bundles pull tokens through Permit2, so this approval goes to Permit2 rather than to the zap contract, and you only do it once per token. After that, every bundle just needs a fresh signature (next step).

        ```ts theme={null}
        const tokens = [{ address: WETH_BASE, amount: DEPOSIT_AMOUNT }];

        const { data } = await dzap.getAllowance({
          chainId: 8453,
          sender: account.address,
          tokens,
          service: Services.zap,
          mode: ApprovalModes.PermitSingle,
        });

        if (data[WETH_BASE].allowance < BigInt(DEPOSIT_AMOUNT)) {
          const approval = await dzap.approve({
            chainId: 8453,
            signer: walletClient,
            tokens,
            service: Services.zap,
            mode: ApprovalModes.PermitSingle,
          });
          if (approval.status !== TxnStatus.success) throw new Error('Permit2 approval failed');
        }
        ```
      </Step>

      <Step title="Sign the permit">
        Sign a Permit2 `PermitSingle` that lets the zap contract, the quote's `approvalData[0].approveTo`, move exactly this much WETH. The result is `permitData`, which you attach to the action that spends the token, here the deposit.

        ```ts theme={null}
        const permit = await dzap.sign({
          chainId: 8453,
          sender: account.address,
          signer: walletClient,
          service: Services.zap,
          permitType: PermitTypes.PermitSingle,
          tokens,
          spender: quote.approvalData[0].approveTo,
        });
        if (permit.status !== TxnStatus.success || !('tokens' in permit)) throw new Error('Permit signature failed');
        const permitData = permit.tokens[0].permitData;
        ```
      </Step>

      <Step title="Sign the delegation">
        Same as borrowing from a position: sign whatever `preExecutionSteps` the quote returned. So in this flow the user signs twice, and the two signatures do different jobs. The permit lets DZap take the WETH, and the delegation lets DZap borrow against it.

        ```ts theme={null}
        const prepared = await dzap.prepareZapPreExecutionData({
          preExecutionSteps: quote.preExecutionSteps,
          signer: walletClient,
          account: account.address,
        });
        if (prepared.status !== TxnStatus.success) throw new Error(prepared.errorMsg);
        const { preExecutionStepsData } = prepared;
        ```
      </Step>

      <Step title="Build & send">
        Put the permit on the deposit action and the delegation on the request, then build and send. One transaction supplies the WETH to Aave in your name and borrows the USDC against it.

        ```ts theme={null}
        const [depositAction, borrowAction] = request.actions;
        const buildRequest: ZapBundleRequest = {
          ...request,
          actions: [{ ...depositAction, permitData }, borrowAction],
          preExecutionStepsData,
        };

        const route = await dzap.buildZapBundleTx(buildRequest);
        const result = await dzap.zap({ request: buildRequest, steps: route.steps, signer: walletClient });
        if (result.status !== TxnStatus.success) throw new Error(result.errorMsg);
        ```
      </Step>

      <Step title="Status">
        Identical to the other tab: poll until it leaves `PENDING`.

        ```ts theme={null}
        let status: ZapStatusResponse | undefined;
        for (let i = 0; i < 20; i++) {  // gives up after 5 minutes
          await new Promise((r) => setTimeout(r, 15_000));
          try {
            status = await dzap.getZapTxnStatus({ chainId: 8453, txnHash: result.txnHash! });
          } catch (e: any) {
            if (![404, 429].includes(e.response?.status)) throw e;
          }
          if (status && status.status !== 'PENDING') break;
        }
        if (status?.status !== 'COMPLETED') throw new Error(`Not completed: ${status?.status ?? 'no status'}`);

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

## End-to-end

<Tabs>
  <Tab title="From a position">
    ```ts theme={null}
    import { DZapClient, TxnStatus, type ZapBundleRequest, type ZapStatusResponse } 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 AWETH_BASE = '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7'; // Aave v3 aBasWETH
    const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
    const BORROW_AMOUNT = '100000000'; // 100 USDC

    // 1. Quote
    const request: ZapBundleRequest = {
      account: account.address,
      recipient: account.address,
      refundee: account.address,
      slippage: 1,
      actions: [
        {
          action: 'borrow',
          srcChainId: 8453,
          srcToken: { address: AWETH_BASE },
          destChainId: 8453,
          destToken: USDC_BASE,
          destAmount: BORROW_AMOUNT,
        },
      ],
    };
    const quote = await dzap.getZapBundleQuote(request);
    console.log(quote.output.map((o) => `${o.amount} ${o.asset.symbol}`));

    // 2. Sign the credit delegation (pre-execution steps)
    const prepared = await dzap.prepareZapPreExecutionData({
      preExecutionSteps: quote.preExecutionSteps,
      signer: walletClient,
      account: account.address,
    });
    if (prepared.status !== TxnStatus.success) throw new Error(prepared.errorMsg);
    const { preExecutionStepsData } = prepared;

    // 3. Build & send
    const buildRequest: ZapBundleRequest = { ...request, preExecutionStepsData };
    const route = await dzap.buildZapBundleTx(buildRequest);
    const result = await dzap.zap({ request: buildRequest, steps: route.steps, signer: walletClient });
    if (result.status !== TxnStatus.success) throw new Error(result.errorMsg);

    // 4. Status
    let status: ZapStatusResponse | undefined;
    for (let i = 0; i < 20; i++) {  // gives up after 5 minutes
      await new Promise((r) => setTimeout(r, 15_000));
      try {
        status = await dzap.getZapTxnStatus({ chainId: 8453, txnHash: result.txnHash! });
      } catch (e: any) {
        if (![404, 429].includes(e.response?.status)) throw e;
      }
      if (status && status.status !== 'PENDING') break;
    }
    if (status?.status !== 'COMPLETED') throw new Error(`Not completed: ${status?.status ?? 'no status'}`);
    console.log('settled:', status.status);
    ```
  </Tab>

  <Tab title="Deposit & borrow">
    ```ts theme={null}
    import {
      DZapClient,
      Services,
      ApprovalModes,
      PermitTypes,
      TxnStatus,
      type ZapBundleRequest,
      type ZapStatusResponse,
    } 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 WETH_BASE: `0x${string}` = '0x4200000000000000000000000000000000000006';
    const AWETH_BASE = '0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7'; // Aave v3 aBasWETH
    const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
    const DEPOSIT_AMOUNT = '100000000000000000'; // 0.1 WETH
    const BORROW_AMOUNT = '100000000'; // 100 USDC

    // 1. Quote: deposit WETH into Aave, then borrow USDC against it
    const request: ZapBundleRequest = {
      account: account.address,
      recipient: account.address,
      refundee: account.address,
      slippage: 1,
      actions: [
        { action: 'deposit', srcChainId: 8453, srcToken: { address: WETH_BASE, amount: DEPOSIT_AMOUNT }, destChainId: 8453, destToken: AWETH_BASE },
        { action: 'borrow', srcChainId: 8453, srcToken: { address: AWETH_BASE }, destChainId: 8453, destToken: USDC_BASE, destAmount: BORROW_AMOUNT },
      ],
    };
    const quote = await dzap.getZapBundleQuote(request);
    console.log(quote.output.map((o) => `${o.amount} ${o.asset.symbol}`));

    // 2. Approve Permit2 for WETH (once per token)
    const tokens = [{ address: WETH_BASE, amount: DEPOSIT_AMOUNT }];
    const { data } = await dzap.getAllowance({
      chainId: 8453,
      sender: account.address,
      tokens,
      service: Services.zap,
      mode: ApprovalModes.PermitSingle,
    });
    if (data[WETH_BASE].allowance < BigInt(DEPOSIT_AMOUNT)) {
      const approval = await dzap.approve({ chainId: 8453, signer: walletClient, tokens, service: Services.zap, mode: ApprovalModes.PermitSingle });
      if (approval.status !== TxnStatus.success) throw new Error('Permit2 approval failed');
    }

    // 3. Sign the Permit2 permit for the deposit
    const permit = await dzap.sign({
      chainId: 8453,
      sender: account.address,
      signer: walletClient,
      service: Services.zap,
      permitType: PermitTypes.PermitSingle,
      tokens,
      spender: quote.approvalData[0].approveTo,
    });
    if (permit.status !== TxnStatus.success || !('tokens' in permit)) throw new Error('Permit signature failed');
    const permitData = permit.tokens[0].permitData;

    // 4. Sign the credit delegation (pre-execution steps)
    const prepared = await dzap.prepareZapPreExecutionData({
      preExecutionSteps: quote.preExecutionSteps,
      signer: walletClient,
      account: account.address,
    });
    if (prepared.status !== TxnStatus.success) throw new Error(prepared.errorMsg);
    const { preExecutionStepsData } = prepared;

    // 5. Build & send
    const [depositAction, borrowAction] = request.actions;
    const buildRequest: ZapBundleRequest = {
      ...request,
      actions: [{ ...depositAction, permitData }, borrowAction],
      preExecutionStepsData,
    };
    const route = await dzap.buildZapBundleTx(buildRequest);
    const result = await dzap.zap({ request: buildRequest, steps: route.steps, signer: walletClient });
    if (result.status !== TxnStatus.success) throw new Error(result.errorMsg);

    // 6. Status
    let status: ZapStatusResponse | undefined;
    for (let i = 0; i < 20; i++) {  // gives up after 5 minutes
      await new Promise((r) => setTimeout(r, 15_000));
      try {
        status = await dzap.getZapTxnStatus({ chainId: 8453, txnHash: result.txnHash! });
      } catch (e: any) {
        if (![404, 429].includes(e.response?.status)) throw e;
      }
      if (status && status.status !== 'PENDING') break;
    }
    if (status?.status !== 'COMPLETED') throw new Error(`Not completed: ${status?.status ?? 'no status'}`);
    console.log('settled:', status.status);
    ```
  </Tab>
</Tabs>

## API usage

The same flow over REST, on the bundle endpoints at `https://zap.dzap.io/v1/bundle`. Each response wraps its result in `data`.

<Tabs>
  <Tab title="From a position">
    <CodeGroup>
      ```bash Quote theme={null}
      curl -X POST https://zap.dzap.io/v1/bundle/quote \
        -H "Content-Type: application/json" \
        -d '{
          "account": "0xYourWallet",
          "recipient": "0xYourWallet",
          "refundee": "0xYourWallet",
          "slippage": 1,
          "actions": [{
            "action": "borrow",
            "srcChainId": 8453,
            "srcToken": { "address": "0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7" },
            "destChainId": 8453,
            "destToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
            "destAmount": "100000000"
          }]
        }'
      ```

      ```bash Build theme={null}
      curl -X POST https://zap.dzap.io/v1/bundle/buildTx \
        -H "Content-Type: application/json" \
        -d '{
          "account": "0xYourWallet",
          "recipient": "0xYourWallet",
          "refundee": "0xYourWallet",
          "slippage": 1,
          "actions": [{
            "action": "borrow",
            "srcChainId": 8453,
            "srcToken": { "address": "0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7" },
            "destChainId": 8453,
            "destToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
            "destAmount": "100000000"
          }],
          "preExecutionStepsData": [{
            "id": "<id from the quote>",
            "type": "sign",
            "signature": "0x…",
            "message": { "…": "the step’s data.message, unchanged" }
          }]
        }'
      ```

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

  <Tab title="Deposit & borrow">
    <CodeGroup>
      ```bash Quote theme={null}
      curl -X POST https://zap.dzap.io/v1/bundle/quote \
        -H "Content-Type: application/json" \
        -d '{
          "account": "0xYourWallet",
          "recipient": "0xYourWallet",
          "refundee": "0xYourWallet",
          "slippage": 1,
          "actions": [
            {
              "action": "deposit",
              "srcChainId": 8453,
              "srcToken": { "address": "0x4200000000000000000000000000000000000006", "amount": "100000000000000000" },
              "destChainId": 8453,
              "destToken": "0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7"
            },
            {
              "action": "borrow",
              "srcChainId": 8453,
              "srcToken": { "address": "0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7" },
              "destChainId": 8453,
              "destToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
              "destAmount": "100000000"
            }
          ]
        }'
      ```

      ```bash Build theme={null}
      curl -X POST https://zap.dzap.io/v1/bundle/buildTx \
        -H "Content-Type: application/json" \
        -d '{
          "account": "0xYourWallet",
          "recipient": "0xYourWallet",
          "refundee": "0xYourWallet",
          "slippage": 1,
          "actions": [
            {
              "action": "deposit",
              "srcChainId": 8453,
              "srcToken": { "address": "0x4200000000000000000000000000000000000006", "amount": "100000000000000000" },
              "destChainId": 8453,
              "destToken": "0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7",
              "permitData": "0x…"
            },
            {
              "action": "borrow",
              "srcChainId": 8453,
              "srcToken": { "address": "0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7" },
              "destChainId": 8453,
              "destToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
              "destAmount": "100000000"
            }
          ],
          "preExecutionStepsData": [{
            "id": "<id from the quote>",
            "type": "sign",
            "signature": "0x…",
            "message": { "…": "the step’s data.message, unchanged" }
          }]
        }'
      ```

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

    `permitData` on the deposit is the encoded Permit2 permit. The simplest way to get it is the SDK's `sign()` shown above.
  </Tab>
</Tabs>

Before building, sign each entry in the quote's `data.preExecutionSteps` and send the results as `preExecutionStepsData`. [Pre-execution steps](/sdk/zap/pre-execution-steps#without-the-sdk) shows how to do that without the SDK. Then send the build's `steps[0].data` (`callTo`, `callData`, `value`) from your wallet. Full reference: [Bundle](/api/fuse/bundle), [Status](/api/fuse/status).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.