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

# Pre-execution Steps

> Handle the signatures some zap routes need before they can be built, such as Aave's credit delegation for a borrow, with the DZap SDK.

Most zaps go quote → approve → build → send. Some routes need something extra in the middle: a signature from the user that has to exist **before** DZap can build the transaction, because it gets embedded in the transaction itself. Those are pre-execution steps.

A borrow is the usual example. The zap contract borrows *on the user's behalf*, and a lending market won't let anyone take on debt in someone's name without permission. On Aave v3 that permission is a credit-delegation signature (`DelegationWithSig`). In plain terms it says "the DZap zap contract may borrow up to this much USDC against my account, until this deadline." DZap can't produce that signature itself, and the transaction can't exist without it, so the quote asks for it first.

## How it works

1. The quote comes back with `preExecutionSteps`, a list of things to sign. Quotes never need the signatures, so you can show a quote before asking the user for anything.
2. You have the user sign each one.
3. You send the signatures back as `preExecutionStepsData` on the build request. The build puts them into the transaction.

```ts theme={null}
const quote = await dzap.getZapBundleQuote(request);

// [{ id: 'aave-delegation-with-sig-8453-0x…', type: 'sign', data: { domain, types, primaryType, message } }]
console.log(quote.preExecutionSteps);

const prepared = await dzap.prepareZapPreExecutionData({
  preExecutionSteps: quote.preExecutionSteps,
  signer: walletClient,
  account: account.address,     // optional, read from the signer when left out
});
if (prepared.status !== TxnStatus.success) throw new Error(prepared.errorMsg);

const buildRequest = { ...request, preExecutionStepsData: prepared.preExecutionStepsData };
const route = await dzap.buildZapBundleTx(buildRequest);
const result = await dzap.zap({ request: buildRequest, steps: route.steps, signer: walletClient });
```

`prepareZapPreExecutionData` asks the wallet to sign each step's typed data, one at a time and in order. On success, `preExecutionStepsData` holds `{ id, type, signature, message }` for every step, which is exactly what the build expects. If the quote asked for nothing, you get `success` with an empty list, so you can call it on every quote without checking first.

It doesn't throw. Like `approve()` and `zap()`, it returns a result with a `status`. If the user cancels a signature you get `status: 'rejected'`, and any other failure comes back as `error`, both with an `errorMsg` you can show and a `code`. It stops at the first step that fails, so a partial list never reaches the build.

<Tip>
  If you don't need to show anything between signing and sending, `zap()` can do all of it in one call. Pass the request and `preExecutionSteps: quote.preExecutionSteps`, leave out `steps`, and it signs, builds and sends.

  ```ts theme={null}
  const result = await dzap.zap({ request, preExecutionSteps: quote.preExecutionSteps, signer: walletClient });
  ```
</Tip>

## Types

All exported from `@dzapio/sdk`.

```ts theme={null}
// What the quote asks for, on quote.preExecutionSteps (and on buildZapTxn / buildZapBundleTx responses)
type ZapPreExecutionStep = {
  id: string;                     // send it back unchanged; it's how the build matches the signature to the step
  type: 'sign';                   // the only kind today
  data: ZapTypedDataPayload;      // { domain, types, primaryType, message }, ready for signTypedData
};

// What you send back, on the request's preExecutionStepsData
type ZapPreExecutionStepData = {
  id: string;
  type: 'sign';
  signature: HexString;
  message: Record<string, unknown>;   // the step's data.message, unchanged
};

// What prepareZapPreExecutionData returns
type ZapPreExecutionResult =
  | { status: TxnStatus.success; code: StatusCodes | number; preExecutionStepsData: ZapPreExecutionStepData[] }
  | { status: Exclude<TxnStatus, TxnStatus.success>; code: StatusCodes; errorMsg: string; error: any };
```

`preExecutionStepsData` is accepted on both `ZapBundleRequest` and `ZapBuildTxnRequest`, so single zaps can use it as well as bundles.

## Things to know before you ship it

* **It's a signature, not a transaction.** No gas, and the wallet shows it as a typed-data request. Nothing happens on-chain until the built transaction is sent.
* **Sign everything in the list, and don't count on its length.** An Aave v3 borrow asks for one signature today. Other markets ask for their own (Aave v4 and Morpho each have an authorization), and most routes return an empty list. If your code loops over whatever comes back, it keeps working when that changes.
* **The signature can pin parts of the route.** For an Aave borrow, the signed `message.value` *is* the amount the build borrows. Change `destAmount` between quote and build and the build is rejected. Quote again and sign again.
* **It expires.** The Aave delegation is valid for an hour and tied to the user's current delegation nonce on that debt token, so any other delegation signed in the meantime invalidates it. Ask for the signature when the user is about to confirm, not when the page loads, and don't reuse it for a later transaction.
* **Building without it fails.** The build won't fall back to something else. It errors until the signatures are there.

## Without the SDK

Over REST, the quote returns the same list in `data.preExecutionSteps`. Each `sign` step's `data` is a ready-made EIP-712 payload (`domain`, `types`, `primaryType`, `message`). Pass it to `eth_signTypedData_v4`, or to viem's `signTypedData`, and add this to the build body:

```json theme={null}
"preExecutionStepsData": [{
  "id": "<the step's id>",
  "type": "sign",
  "signature": "0x…",
  "message": { "…": "the step's data.message, unchanged" }
}]
```

Send the `message` back exactly as you received it, without rebuilding it. The backend reads values like the amount and deadline from it, and anything that differs from what was signed invalidates the signature.

## Errors

* **`No signature provided for borrow delegation`**: the build had no `preExecutionStepsData`, or it had signatures from a different quote. Sign what the latest quote returned.
* **`Signed credit delegation (…) does not match the borrow amount`**: the signed amount and the built amount differ. Either `destAmount` changed after signing, or you left `destAmount` out and the maximum dropped below what was signed. Quote and sign again.
* **The signature stopped working after a while**: it expired, or another delegation used up its nonce. Quote again right before asking the user to sign.

For a full walkthrough of both borrow flows, see the [Borrow](/cookbook/zap/borrow) recipe.


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