# Aave V4 Swap Features

Alongside the [Position Swaps](../positions/swaps/supply) feature, AaveKit provides the following tools for swapping:

- **[Swap Tokens](#swap-tokens):** exchange tokens instantly at market price or set conditions with a limit order.
- **[Monitor Swap Status](#monitor-swap-status):** track the status of a swap order.
- **[User Swaps](#user-swaps):** list and filter user orders.
- **[Cancel a Swap](#cancel-a-swap):** cancel a swap order in progress.

> [!WARNING]
> The swap API is deprecated. Move to the Order API:
> - `prepareTokenSwap` → `prepareOrder`
> - `swap` → `submitOrder`
> - `swapStatus` → `orderStatus`
> - `userSwaps` → `pendingOrders`
> - `prepareSwapCancel` → `prepareCancelOrder`
> - `cancelSwap` → `cancelOrder`
> - React: `useTokenSwap` → `useTokenSwapOrder`
> - React: `useSwapStatus` → `useOrderStatus`
> - React: `useUserSwaps` → `usePendingOrders`
> - React: `useCancelSwap` → `useCancelOrder`

Swaps are implemented using a modular provider architecture. Currently, [CoW Protocol](https://cow.fi/) is the swap provider, offering MEV protection and optimal execution through batch auctions.

> [!NOTE]
> See [fees](#fees) for swap costs.

## Swap Tokens

Token swaps can be executed in two ways:

- **Intent-based**: The swap is signed off-chain and submitted as an intent, which is then executed by a solver. This is the preferred method as it is gasless for users.
- **Transaction-based**: The swap is executed directly on-chain via a transaction. This method is used when intent-based execution is not available.

> [!NOTE]
> Native token swaps are always transaction-based since users must send funds
> directly.

Swapping tokens involves the following steps:

1. Select the token to swap from
2. Select the token to swap to
3. Choose between market order and limit order
4. Execute the swap operation

### Source Token

Fetch user token balances that can be swapped. This allows you to display only tokens the user owns that are swappable, making source token selection easier.

Use the `useUserBalances` hook in React, the `userBalances` action in TypeScript, or the `userBalances` query in GraphQL with the `swappable` filter to list user tokens that can be swapped.

<tabgroup>
<tab label="React">

```tsx title="Loading State"
import type { UserBalancesRequest, TokenAmount } from "@aave/react";
import { useUserBalances } from "@aave/react";

function SwapFromToken({
  request,
  onSelect,
}: {
  request: UserBalancesRequest;
  onSelect: (amount: TokenAmount) => void;
}) {
  const { data, loading, error } = useUserBalances(request);

  if (loading) return Loading…;
  if (error) return {error.message};

  return (
    <select
      onChange={(e) => {
        const balance = data.find((b) => b.id === e.target.value);
        if (balance) onSelect(balance.balances[0]);
      }}
    >
      {data.map((balance) => (
        <option key={balance.id} value={balance.id}>
          {balance.info.symbol} ({balance.totalAmount.value.toDisplayString(2)})
        </option>
      ))}
    </select>
  );
}
```

```tsx title="React Suspense"
import {
  type UserBalancesRequest,
  type TokenAmount,
  useUserBalances,
} from "@aave/react";

function SwapFromToken({
  request,
  onSelect,
}: {
  request: UserBalancesRequest;
  onSelect: (amount: TokenAmount) => void;
}) {
  const { data } = useUserBalances({
    ...request,
    suspense: true,
  });

  if (data.length === 0) return No swappable tokens found;

  return (
    <select
      onChange={(e) => {
        const balance = data.find((b) => b.id === e.target.value);
        if (balance) onSelect(balance.balances[0]);
      }}
    >
      {data.map((balance) => (
        <option key={balance.id} value={balance.id}>
          {balance.info.symbol} ({balance.totalAmount.value.toDisplayString(2)})
        </option>
      ))}
    </select>
  );
}
```

</tab>
<tab label="TypeScript">

```ts title="By Chain IDs"
import { userBalances } from "@aave/client/actions";
import { evmAddress, chainId, OrderDirection } from "@aave/client";

import { client } from "./client";

const result = await userBalances(client, {
  user: evmAddress("0x456…"),
  filter: {
    swappable: {
      chainIds: [chainId(1)],
    },
  },
  orderBy: { balance: OrderDirection.Desc },
});

if (result.isErr()) {
  console.error(result.error);
} else {
  // result.value: UserBalance[]
  result.value.forEach((balance) => {
    console.log(
      `${balance.info.symbol}: ${balance.totalAmount.value.toDisplayString(2)}`,
    );
  });
}
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
query ($request: UserBalancesRequest!) {
  userBalances(request: $request) {
    info {
      __typename
      ... on Erc20Token {
        symbol
        name
        address
        chainId
      }
      ... on NativeToken {
        symbol
        name
        chainId
      }
    }
    balances {
      ...TokenAmount
    }
    totalAmount {
      value
    }
  }
}
```

```graphql title="TokenAmount"
fragment TokenAmount on TokenAmount {
  __typename

  ... on Erc20Amount {
    amount {
      value
    }
    token {
      address
      chainId
    }
  }
  ... on NativeAmount {
    amount {
      value
    }
    token {
      chainId
    }
  }
}
```

</tab>
</tabgroup>
- **React**: You can query for swappable token balances as follows.
- **GraphQL**: Use the `filter.swappable` field to filter by swappable tokens.

<tabgroup>
<tab label="React">

```tsx title="By Chain IDs"
import { evmAddress, chainId, OrderDirection } from "@aave/react";

const request: UserBalancesRequest = {
  user: evmAddress("0x456…"),
  filter: {
    swappable: {
      chainIds: [chainId(1)],
    },
  },
  orderBy: { balance: OrderDirection.Desc },
};
```

```tsx title="Include Zero Balances"
import { evmAddress, chainId, OrderDirection } from "@aave/react";

const request: UserBalancesRequest = {
  user: evmAddress("0x456…"),
  filter: {
    swappable: {
      chainIds: [chainId(1)],
    },
  },
  orderBy: { balance: OrderDirection.Desc },
  includeZeroBalances: true,
};
```

</tab>
<tab label="GraphQL">

```json title="By Chain IDs"
{
  "request": {
    "user": "0x456…",
    "filter": {
      "swappable": {
        "chainIds": [1]
      }
    },
    "orderBy": {
      "balance": "DESC"
    }
  }
}
```

```json title="Include Zero Balances"
{
  "request": {
    "user": "0x456…",
    "filter": {
      "swappable": {
        "chainIds": [1]
      }
    },
    "orderBy": {
      "balance": "DESC"
    },
    "includeZeroBalances": true
  }
}
```

</tab>
</tabgroup>
> [!NOTE]
> See [User Balances](./balances#fetching-user-balances) for filtering and
> sorting.

### Destination Token

Once you’ve selected a source token, fetch the tokens you can swap TO. This ensures you only display valid swap destinations for the selected source token.

Use `useSwappableTokens` in React, `swappableTokens` in TypeScript, or the `swappableTokens` query in GraphQL to list destination tokens based on your selected source token.

<tabgroup>
<tab label="React">

```tsx title="Loading State"
import type { SwappableTokensRequest, Token } from "@aave/react";
import { useSwappableTokens } from "@aave/react";

function SwapToToken({
  request,
  onSelect,
}: {
  request: SwappableTokensRequest;
  onSelect: (token: Token) => void;
}) {
  const { data, loading, error } = useSwappableTokens(request);

  if (loading) return Loading…;
  if (error) return {error.message};

  return (
    <select
      onChange={(e) => {
        const token = data.find((t) => t.info.address === e.target.value);
        if (token) onSelect(token);
      }}
    >
      {data.map((token) => (
        <option key={token.info.address} value={token.info.address}>
          {token.info.symbol} ({token.info.name})
        </option>
      ))}
    </select>
  );
}
```

```tsx title="React Suspense"
import type { SwappableTokensRequest, Token } from "@aave/react";
import { useSwappableTokens } from "@aave/react";

function SwapToToken({
  request,
  onSelect,
}: {
  request: SwappableTokensRequest;
  onSelect: (token: Token) => void;
}) {
  const { data } = useSwappableTokens({
    ...request,
    suspense: true,
  });

  return (
    <select
      disabled={data.length === 0}
      onChange={(e) => {
        const token = data.find((t) => t.info.address === e.target.value);
        if (token) onSelect(token);
      }}
    >
      {data.map((token) => (
        <option key={token.info.address} value={token.info.address}>
          {token.info.symbol} ({token.info.name})
        </option>
      ))}
    </select>
  );
}
```

</tab>
<tab label="TypeScript">

```ts title="By Chain IDs"
import { chainId } from "@aave/client";
import { swappableTokens } from "@aave/client/actions";

import { client } from "./client";

const result = await swappableTokens(client, {
  query: { chainIds: [chainId(1)] },
});

if (result.isErr()) {
  console.error(result.error);
} else {
  // result.value: Token[]
  result.value.forEach((token) => {
    console.log(`Token: ${token.info.symbol} (${token.info.name})`);
  });
}
```

```ts title="For Selected Source Token"
import { chainId, evmAddress } from "@aave/client";
import { swappableTokens } from "@aave/client/actions";

import { client } from "./client";

const result = await swappableTokens(client, {
  query: {
    from: {
      erc20: { address: evmAddress("0xA0b86991…"), chainId: chainId(1) },
    },
  },
});

if (result.isErr()) {
  console.error(result.error);
} else {
  // result.value: Token[]
  result.value.forEach((token) => {
    console.log(`Token: ${token.info.symbol} (${token.info.name})`);
  });
}
```

```ts title="By Specific Tokens"
import { chainId, evmAddress } from "@aave/client";
import { swappableTokens } from "@aave/client/actions";

import { client } from "./client";

const result = await swappableTokens(client, {
  query: {
    tokens: [
      { address: evmAddress("0xA0b86991…"), chainId: chainId(1) },
      { address: evmAddress("0x6B175474E…"), chainId: chainId(1) },
    ],
  },
});

if (result.isErr()) {
  console.error(result.error);
} else {
  // result.value: Token[]
  result.value.forEach((token) => {
    console.log(`Token: ${token.info.symbol} (${token.info.name})`);
  });
}
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

```graphql
query ($request: SwappableTokensRequest!) {
  swappableTokens(request: $request) {
    __typename
    ... on Erc20Token {
      ...Erc20Token
    }
    ... on NativeToken {
      ...NativeToken
    }
  }
}
```

</tab>
</tabgroup>
- **React**: You can query for a list of swappable tokens as follows.
- **GraphQL**: Use the `query` field to select destination tokens by different criteria.

<tabgroup>
<tab label="React">

```tsx title="By Chain IDs"
const request: SwappableTokensRequest = {
  query: {
    chainIds: [chainId(1)],
  },
};
```

```tsx title="For Selected Source Token"
const request: SwappableTokensRequest = {
  query: {
    from: {
      erc20: { address: evmAddress("0xA0b86991…"), chainId: chainId(1) },
    },
  },
};
```

```tsx title="By Specific Tokens"
const request: SwappableTokensRequest = {
  query: {
    tokens: [
      { address: evmAddress("0xA0b86991…"), chainId: chainId(1) },
      { address: evmAddress("0x6B175474E…"), chainId: chainId(1) },
    ],
  },
};
```

</tab>
<tab label="GraphQL">

```json title="By Chain IDs"
{
  "request": {
    "query": {
      "chainIds": [1]
    }
  }
}
```

```json title="For Selected Source Token"
{
  "request": {
    "query": {
      "from": {
        "erc20": { "address": "0xA0b86991…", "chainId": 1 }
      }
    }
  }
}
```

```json title="By Specific Tokens"
{
  "request": {
    "query": {
      "tokens": [
        { "address": "0xA0b86991…", "chainId": 1 },
        { "address": "0x6B175474E…", "chainId": 1 }
      ]
    }
  }
}
```

</tab>
</tabgroup>
### Market or Limit Order

Once you’ve identified the tokens to swap, the next step is choosing an order type. Market orders execute immediately at the current rate, while limit orders wait until your specified price conditions are met.

#### Market Orders

To place a market order, specify the token pair, amount, and swap direction.

```ts title="TypeScript"
const request: TokenSwapQuoteRequest = {
  market: {
    chainId: chainId(1),
    buy: { erc20: evmAddress("0xA0b86991…") },
    sell: { erc20: evmAddress("0x6B175474E…") },
    amount: bigDecimal("1000"),
    kind: TokenSwapKind.Buy,
    user: evmAddress("0x742d35cc…"), // Your address
    receiver: evmAddress("0x742d35cc…"), // In case the receiver is different from the user
    selectedSlippage: bigDecimal("2"), // 2%
  },
};
```

```jsonc title="GraphQL Variables"
{
  "request": {
    "market": {
      "chainId": 1,
      "buy": { "erc20": "0xA0b86991…" },
      "sell": { "erc20": "0x6B175474E…" },
      "amount": "1000",
      "kind": "BUY",
      "user": "0x742d35cc…", // Your address
      "receiver": "0x742d35cc…", // In case the receiver is different from the user
      "selectedSlippage": "2" // 2%
    }
  }
}
```

The request takes the following parameters:

- `chainId`: The target chain
- `sell`: The token to sell (`native` or `erc20`)
- `buy`: The token to receive (`native` or `erc20`)
- `amount`: The swap amount, interpreted based on `kind`
- `kind`: `TokenSwapKind.Sell` or `TokenSwapKind.Buy`, required; determines how `amount` is interpreted
- `user`: The sender address
- `receiver`: The recipient address (defaults to `user`)
- `selectedSlippage`: Optional slippage tolerance to use instead of the suggested slippage

Since only one amount is provided, the swap kind determines what gets calculated:

| Swap Kind | User inputs…                                                 | System calculates…                               |
| --------- | ------------------------------------------------------------ | ------------------------------------------------ |
| **Sell**      | How much of the token to _sell_ and which token to _buy_ | The amount of the buy token you’ll receive       |
| **Buy**       | How much of the token to _buy_ and which token to _sell_ | The amount of the sell token you need to provide |

Depending on the token pair, swaps can be performed in different ways:

```ts title="Erc20 → Erc20"
const request: TokenSwapQuoteRequest = {
  market: {
    chainId: chainId(1),
    buy: { erc20: evmAddress("0xA0b86991…") },
    sell: { erc20: evmAddress("0x6B175474E…") },
    amount: bigDecimal("1000"),
    kind: TokenSwapKind.Sell,
    user: evmAddress("0x742d35cc…"), // Your address
  },
};
```

```ts title="Native → Erc20"
const request: TokenSwapQuoteRequest = {
  market: {
    chainId: chainId(1),
    sell: { native: true },
    buy: { erc20: evmAddress("0x6B175474E…") },
    amount: bigDecimal("1000"),
    kind: TokenSwapKind.Sell,
    user: evmAddress("0x742d35cc…"), // Your address
  },
};
```

```ts title="Erc20 → Native"
const request: TokenSwapQuoteRequest = {
  market: {
    chainId: chainId(1),
    sell: { erc20: evmAddress("0x6B175474E…") },
    buy: { native: true },
    amount: bigDecimal("1000"),
    kind: TokenSwapKind.Sell,
    user: evmAddress("0x742d35cc…"), // Your address
  },
};
```

#### Limit Orders

To place a limit order, specify exact amounts for both the sell and buy tokens.

```ts title="TypeScript"
const request: TokenSwapQuoteRequest = {
  limit: {
    chainId: chainId(1),
    sell: {
      erc20: {
        address: evmAddress("0x6B175474E…"),
        value: bigDecimal("1000"),
      },
    },
    buy: {
      erc20: {
        address: evmAddress("0xA0b86991…"),
        value: bigDecimal("1000"),
      },
    },
    user: evmAddress("0x742d35cc…"),
    receiver: evmAddress("0x742d35cc…"),
    // kind: TokenSwapKind.Buy, default is TokenSwapKind.Sell
    // previousQuoteId: quote.quoteId, if available
  },
};
```

```json title="GraphQL Variables"
{
  "request": {
    "limit": {
      "chainId": 1,
      "sell": {
        "erc20": {
          "address": "0x6B175474E…",
          "value": "1000"
        }
      },
      "buy": {
        "erc20": {
          "address": "0xA0b86991…",
          "value": "1000"
        }
      },
      "user": "0x742d35cc…",
      "receiver": "0x742d35cc…",
      "kind": "SELL"
    }
  }
}
```

The request takes the following parameters:

- `chainId`: The target chain
- `sell`: The token and amount to sell
- `buy`: The token and amount to receive
- `user`: The sender address
- `receiver`: The recipient address (defaults to `user`)
- `kind`: Sell (default) or Buy, determines which amount is honored
- `previousQuoteId`: Optional quote ID from a prior market order
- `deadline`: Optional deadline as DateTime; if not provided, uses default expiry

With both amounts specified, the swap kind determines which amount takes priority:

| Swap Kind | User specifies…                                        | What happens with the order                                                                                         |
| --------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| **Sell**      | The exact _sell_ amount and a minimum _buy_ amount | The order is fulfilled when the buy price is large enough to accommodate the user’s ask and the swap costs          |
| **Buy**       | The exact _buy_ amount and a maximum _sell_ amount | The order is fulfilled when the sell price is small enough to accommodate the user’s desired buy and the swap costs |

Depending on the token pair, limit orders can be configured in different ways:

```ts title="Erc20 → Erc20"
const request: TokenSwapQuoteRequest = {
  limit: {
    chainId: chainId(1),
    sell: {
      erc20: {
        address: evmAddress("0x6B175474E…"),
        value: bigDecimal("1000"),
      },
    },
    buy: {
      erc20: {
        address: evmAddress("0xA0b86991…"),
        value: bigDecimal("950"),
      },
    },
    user: evmAddress("0x742d35cc…"),
  },
};
```

```ts title="Erc20 → Native"
const request: TokenSwapQuoteRequest = {
  limit: {
    chainId: chainId(1),
    sell: {
      erc20: {
        address: evmAddress("0xA0b86991…"),
        value: bigDecimal("3000"),
      },
    },
    buy: {
      native: bigDecimal("1"),
    },
    user: evmAddress("0x742d35cc…"),
  },
};
```

> [!NOTE]
> Limit orders from native tokens are currently not supported via CoW Protocol
> to avoid holding user funds.

### Execute Swaps

To execute the swap operation with AaveKit, follow these steps.

#### 1. Get a Swap Quote

First, use the `useTokenSwapQuote` hook (or the imperative `useTokenSwapQuoteAction` variant) in React, the `tokenSwapQuote` action in TypeScript, or the `tokenSwapQuote` query in GraphQL to get a swap quote. The quote’s `orderQuoteId` is the ID you pass to the Order API.

<tabgroup>
<tab label="React">

```tsx title="Loading State"
import { type TokenSwapQuoteRequest, useTokenSwapQuote } from "@aave/react";

function DisplayQuote({ request }: { request: TokenSwapQuoteRequest }) {
  const { data, loading, error } = useTokenSwapQuote(request);

  if (loading) return Loading…;

  if (error) {
    if (error.name === "ValidationError") {
      switch (error.cause.__typename) {
        case "InsufficientLiquidityError":
          return Not enough liquidity for this swap;
      }
    }
    return {error.message};
  }

  return (
    <div>
      <h3>Swap Quote</h3>
      <h4>{data.quoteId}</h4>
      Suggested slippage: {data.suggestedSlippage.normalized.toFixed(2)}%
      Network costs: {data.costs.networkCosts.amount.value.toDisplayString(2)}
    </div>
  );
}
```

```tsx title="React Suspense"
import { type TokenSwapQuoteRequest, useTokenSwapQuote } from "@aave/react";

function DisplayQuote({ request }: { request: TokenSwapQuoteRequest }) {
  const { data } = useTokenSwapQuote({
    ...request,
    suspense: true,
  });

  if (!data) return No quote found;

  return (
    <div>
      <h3>Swap Quote</h3>
      <h4>{data.quoteId}</h4>
      Suggested slippage: {data.suggestedSlippage.normalized.toFixed(2)}%
      Network costs: {data.costs.networkCosts.amount.value.toDisplayString(2)}
    </div>
  );
}
```

```tsx title="Imperative Read"
import {
  type TokenSwapQuoteRequest,
  useTokenSwapQuoteAction,
} from "@aave/react";

// …

const [getQuote, { called, data, error, loading }] = useTokenSwapQuoteAction();

const handler = async (request: TokenSwapQuoteRequest) => {
  const result = await getQuote(request);

  if (result.isOk()) {
    console.log(result.value); // SwapQuote
  } else {
    console.error(result.error);
  }
};
```

</tab>
<tab label="TypeScript">

```ts title="Get Quote"
import { tokenSwapQuote } from "@aave/client/actions";

import { client } from "./client";

// …

const result = await tokenSwapQuote(client, request);

if (result.isErr()) {
  console.error(result.error);
  return;
}

// result.value.quote: SwapQuote
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

  
```graphql title="Query"
query ($request: TokenSwapQuoteRequest!, $currency: Currency = USD) {
  tokenSwapQuote(request: $request) {
    __typename
    ... on SwapByIntent {
      ...SwapByIntent
    }
    ... on SwapByIntentWithApprovalRequired {
      ...SwapByIntentWithApprovalRequired
    }
    ... on SwapByTransaction {
      ...SwapByTransaction
    }
    ... on SwapByTransactionWithApprovalRequired {
      ...SwapByTransactionWithApprovalRequired
    }
    ... on InsufficientLiquidityError {
      reason
    }
  }
}
```

```graphql title="SwapByIntent"
fragment SwapByIntent on SwapByIntent {
  __typename
  quote {
    ...SwapQuote
  }
}
```

```graphql title="SwapByIntentWithApprovalRequired"
fragment SwapByIntentWithApprovalRequired on SwapByIntentWithApprovalRequired {
  __typename
  approvals {
    ...Erc20Approval
  }
  quote {
    ...SwapQuote
  }
}
```

```graphql title="SwapByTransaction"
fragment SwapByTransaction on SwapByTransaction {
  __typename
  quote {
    ...SwapQuote
  }
}
```

```graphql title="SwapByTransactionWithApprovalRequired"
fragment SwapByTransactionWithApprovalRequired on SwapByTransactionWithApprovalRequired {
  __typename
  quote {
    ...SwapQuote
  }
  approvals {
    ...Erc20Approval
  }
}
```

```graphql title="SwapQuote"
fragment SwapQuote on SwapQuote {
  __typename
  accuracy
  quoteId
  suggestedSlippage {
    ...PercentNumber
  }
  selectedSlippage {
    ...PercentNumber
  }
  buy {
    ...TokenAmount
  }
  sell {
    ...TokenAmount
  }
  costs {
    __typename
    networkCosts {
      ...TokenAmount
    }
    partnerFee {
      ...TokenAmount
    }
    flashloanFee {
      ...TokenAmount
    }
    providerFee {
      ...TokenAmount
    }
  }
  finalBuy {
    ...TokenAmount
  }
  finalSell {
    ...TokenAmount
  }
  orderQuoteId
}
```

</tab>
</tabgroup>
> [!NOTE]
> `useTokenSwapQuoteAction` doesn’t watch for updates. Use it for on-demand
> fresh data, such as in an event handler.

You can specify a different currency to return fiat amounts in.

<tabgroup>
<tab label="React">

```tsx title="Declarative Read"
import { Currency } from "@aave/react";

const { data, error, loading } = useTokenSwapQuote({
  ...request,
  currency: Currency.Eur,
});
```

```tsx title="Imperative Read"
import { Currency } from "@aave/react";

const [getQuote, { called, data, error, loading }] = useTokenSwapQuoteAction({
  currency: Currency.Eur,
});
```

</tab>
<tab label="TypeScript">

```ts title="Custom Currency"
import { Currency } from "@aave/client";

// …

const result = await tokenSwapQuote(client, request, {
  currency: Currency.Eur,
});
```

</tab>
<tab label="GraphQL">

```json title="Custom Currency"
{
  "request": {
    "market": {
      "chainId": 1,
      "buy": { "erc20": "0xA0b86991…" },
      "sell": { "native": true },
      "amount": "1000",
      "kind": "SELL",
      "user": "0x742d35cc…"
    }
  },
  "currency": "EUR"
}
```

</tab>
</tabgroup>
> [!NOTE]
> For limit orders, fetch a market quote first and use its `buy` and `sell`
> values as a baseline.

In GraphQL, `kind` works as described in [Market Orders](#market-orders).

In GraphQL, depending on the token pair, swaps can be performed in different ways:

<tabgroup>
<tab label="GraphQL">

```jsonc title="Erc20 → Erc20"
{
  "request": {
    "market": {
      "chainId": 1,
      "buy": { "erc20": "0xA0b86991…" },
      "sell": { "erc20": "0x6B175474E…" },
      "amount": "1000",
      "kind": "SELL", // SELL, BUY (see table above)
      "user": "0x742d35cc…"
    }
  }
}
```

```jsonc title="Native → Erc20"
{
  "request": {
    "market": {
      "chainId": 1,
      "sell": { "native": true },
      "buy": { "erc20": "0xA0b86991…" },
      "amount": "1000",
      "kind": "BUY", // SELL, BUY (see table above)
      "user": "0x742d35cc…"
    }
  }
}
```

```jsonc title="Erc20 → Native"
{
  "request": {
    "market": {
      "chainId": 1,
      "sell": { "erc20": "0xA0b86991…" },
      "buy": { "native": true },
      "amount": "1000",
      "kind": "SELL", // SELL, BUY (see table above)
      "user": "0x742d35cc…"
    }
  }
}
```

</tab>
</tabgroup>
#### 2. Configure Wallet Integration

Then, in React, instantiate the hooks for the [wallet library of your choice](../getting-started/react#integrations):

- `useSendTransaction`: used to send ERC-20 approval and swap transactions when needed
- `useSignTypedData`: used to sign ERC-20 permits and swap order typed data

<tabgroup>
<tab label="React">

```tsx title="Viem"
import { useWalletClient } from "wagmi";
import { useSendTransaction, useSignTypedData } from "@aave/react/viem";

// …

const { data: wallet } = useWalletClient();
const [sendTransaction] = useSendTransaction(wallet);
const [signTypedData] = useSignTypedData(wallet);
```

</tab>
</tabgroup>
#### 3. Define the Swap Flow

Then, in React, use the `useTokenSwapOrder` hook to handle the operation. It fetches the quote, handles any ERC-20 approval, and prepares, signs, and submits the order.

<tabgroup>
<tab label="React">

```tsx title="Basic Usage"
import { useTokenSwapOrder } from "@aave/react";

// …

const [swap, { loading, error }] = useTokenSwapOrder((plan) => {
  switch (plan.__typename) {
    case "Erc20Approval":
      if (plan.bySignature) {
        return signTypedData(plan.bySignature);
      }
      return sendTransaction(plan.byTransaction);

    case "OrderTransactionRequest":
      return sendTransaction(plan.transaction);

    case "OrderTypedData":
      return signTypedData(plan);
  }
});
```

```tsx title="Update Component State"
import { useTokenSwapOrder } from "@aave/react";

// …

const [status, setStatus] = useState("");

const [swap, { loading, error }] = useTokenSwapOrder((plan) => {
  switch (plan.__typename) {
    case "Erc20Approval":
      setStatus("Please approve the token in your wallet");
      if (plan.bySignature) {
        return signTypedData(plan.bySignature).andTee(() =>
          setStatus("Signing permit…"),
        );
      }
      return sendTransaction(plan.byTransaction).andTee(() =>
        setStatus("Approving…"),
      );

    case "OrderTransactionRequest":
      setStatus("Please sign the Swap transaction in your wallet");
      return sendTransaction(plan.transaction).andTee(() =>
        setStatus("Swapping…"),
      );

    case "OrderTypedData":
      setStatus("Please sign the Swap Order in your wallet");
      return signTypedData(plan).andTee(() => setStatus("Signing…"));
  }
});
```

```tsx title="Cancel Flow"
import { useTokenSwapOrder } from "@aave/react";

// …

const [swap, { loading, error }] = useTokenSwapOrder((plan, { cancel }) => {
  if (window.confirm("Are you sure you want to continue?") === false) {
    return cancel("User cancelled the operation");
  }

  switch (plan.__typename) {
    case "Erc20Approval":
      if (plan.bySignature) {
        return signTypedData(plan.bySignature);
      }
      return sendTransaction(plan.byTransaction);

    case "OrderTransactionRequest":
      return sendTransaction(plan.transaction);

    case "OrderTypedData":
      return signTypedData(plan);
  }
});
```

</tab>
</tabgroup>
#### 4. Handle Approvals

Then, in TypeScript, using helpers from the [wallet library of your choice](../getting-started/typescript#integrations), use `signTypedDataWith` and `sendWith` to handle ERC-20 approvals, and `prepareOrder` with `signTypedDataWith` to sign intent-based swap orders. Each branch resolves to a `SubmitOrderRequest` for the next step.

<tabgroup>
<tab label="TypeScript">

```ts title="Handle Approvals"
import { tokenSwapQuote } from "@aave/client/actions";

import { client } from "./client";
import { handleApprovals } from "./handleApprovals";

// …

const result = await tokenSwapQuote(client, request) //
  .andThen(handleApprovals);
```

```ts title="handleApprovals.ts"
import { type TokenSwapQuoteResult, okAsync } from "@aave/client";
import { signTypedDataWith, sendWith } from "@aave/client/viem";

import { wallet } from "./wallet";
import { signSwapOrder } from "./signSwapOrder";

// …

export function handleApprovals(result: TokenSwapQuoteResult) {
  switch (result.__typename) {
    case "SwapByTransaction":
      return okAsync({
        byTransaction: { quoteId: result.quote.orderQuoteId },
      });

    case "SwapByTransactionWithApprovalRequired":
      return result.approvals
        .reduce(
          (acc, approval) =>
            acc.andThen(() => sendWith(wallet, approval.byTransaction)),
          okAsync<unknown, unknown>(undefined),
        )
        .map(() => ({
          byTransaction: { quoteId: result.quote.orderQuoteId },
        }));

    case "SwapByIntent":
      return signSwapOrder({ quoteId: result.quote.orderQuoteId });

    case "SwapByIntentWithApprovalRequired":
      // Permit-based approval (only available for single approval)
      if (result.approvals.length === 1 && result.approvals[0].bySignature) {
        const permitTypedData = result.approvals[0].bySignature;
        return signTypedDataWith(wallet, permitTypedData)
          .map((signature) => ({
            deadline: permitTypedData.message.deadline,
            value: signature,
          }))
          .andThen((permitSig) =>
            signSwapOrder({ quoteId: result.quote.orderQuoteId, permitSig }),
          );
      }

      // Transaction-based approval (handles multiple approvals for USDT-like tokens)
      return result.approvals
        .reduce(
          (acc, approval) =>
            acc.andThen(() => sendWith(wallet, approval.byTransaction)),
          okAsync<unknown, unknown>(undefined),
        )
        .andThen(() => signSwapOrder({ quoteId: result.quote.orderQuoteId }));

    case "InsufficientLiquidityError":
      throw new Error(`Insufficient liquidity: ${result.reason}`);
  }
}
```

```ts title="signSwapOrder.ts"
import type { PrepareOrderRequest } from "@aave/client";
import { prepareOrder } from "@aave/client/actions";
import { signTypedDataWith } from "@aave/client/viem";

import { client } from "./client";
import { wallet } from "./wallet";

export function signSwapOrder(request: PrepareOrderRequest) {
  return prepareOrder(client, request).andThen(({ newQuoteId, data }) =>
    signTypedDataWith(wallet, data).map((signature) => ({
      bySignature: { quoteId: newQuoteId, signature },
    })),
  );
}
```

```ts title="wallet.ts"
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

export const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
  chain: mainnet,
  transport: http(),
});
```

</tab>
</tabgroup>
> [!NOTE]
> For tokens that need an allowance reset, like [USDT on Ethereum
> Mainnet](https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7),
> `sendWith` sends both approvals before the swap: reset to 0, then the new
> allowance.

#### 5. Handle Swap Quote Response

In GraphQL, depending on the `TokenSwapQuoteResult` type, you’ll need to follow different actions:

| Response Type                             | Action                                                                                                            |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `SwapByIntent`                          | Pass the `quote.orderQuoteId` to the `prepareOrder` query.                                                         |
| `SwapByIntentWithApprovalRequired`      | Send each transaction in `approvals` from the `user` account, then pass the `quote.orderQuoteId` to `prepareOrder`. |
| `SwapByTransaction`                     | Keep a note of the `quote.orderQuoteId`.                                                                           |
| `SwapByTransactionWithApprovalRequired` | Send each transaction from `approvals` in sequence, then keep a note of the `quote.orderQuoteId`.                  |
| `InsufficientLiquidityError`            | Not enough liquidity available for this swap. Display the error to the user or suggest a smaller amount.          |

Below are examples illustrating the response for each response type.

<tabgroup>
<tab label="GraphQL">

```jsonc title="SwapByIntent"
{
  "data": {
    "tokenSwapQuote": {
      "__typename": "SwapByIntent",
      "quote": {
        "orderQuoteId": "0x8f3…"
        // …
      }
    }
  }
}
```

```jsonc title="SwapByIntentWithApprovalRequired"
{
  "data": {
    "tokenSwapQuote": {
      "__typename": "SwapByIntentWithApprovalRequired",
      "approvals": [
        {
          // …
        }
      ],
      "quote": {
        "orderQuoteId": "0x8f3…"
        // …
      }
    }
  }
}
```

```jsonc title="SwapByTransaction"
{
  "data": {
    "tokenSwapQuote": {
      "__typename": "SwapByTransaction",
      "quote": {
        "orderQuoteId": "0x8f3…"
        // …
      }
    }
  }
}
```

```jsonc title="SwapByTransactionWithApprovalRequired"
{
  "data": {
    "tokenSwapQuote": {
      "__typename": "SwapByTransactionWithApprovalRequired",
      "approvals": [
        {
          // …
        }
      ],
      "quote": {
        "orderQuoteId": "0x8f3…"
        // …
      }
    }
  }
}
```

```json title="InsufficientLiquidityError"
{
  "data": {
    "tokenSwapQuote": {
      "__typename": "InsufficientLiquidityError",
      "reason": "Not enough liquidity available for this swap amount"
    }
  }
}
```

</tab>
</tabgroup>
#### 6. Prepare the Swap

Then, in GraphQL, use the `prepareOrder` query to prepare the swap order.

<tabgroup>
<tab label="GraphQL">

```graphql title="Query"
query ($request: PrepareOrderRequest!) {
  prepareOrder(request: $request) {
    __typename
    ... on PreparedOrder {
      ...PreparedOrder
    }
    ... on InsufficientBalanceError {
      ...InsufficientBalanceError
    }
  }
}
```

```graphql title="PreparedOrder"
fragment PreparedOrder on PreparedOrder {
  __typename
  newQuoteId
  data {
    ...OrderTypedData
  }
}
```

```graphql title="OrderTypedData"
fragment OrderTypedData on OrderTypedData {
  __typename
  types
  primaryType
  domain {
    ...DomainData
  }
  message
}
```

</tab>
</tabgroup>
Define `PrepareOrderRequest` taking the `quote.orderQuoteId` from the previous step and the permit signature when needed.

<tabgroup>
<tab label="GraphQL">

```json title="QuoteId"
{
  "request": {
    "quoteId": "<quote.orderQuoteId>"
  }
}
```

```json title="QuoteId and Signature"
{
  "request": {
    "quoteId": "<quote.orderQuoteId>",
    "permitSig": { "value": "<signature>", "deadline": <approval.bySignature.message.deadline> }
  }
}
```

</tab>
</tabgroup>
#### 7. Handle Prepare Swap Response

In GraphQL, depending on the `PrepareOrderResult` type, you’ll need to follow different actions:

| Response Type                | Action                                                                  |
| ---------------------------- | ----------------------------------------------------------------------- |
| `PreparedOrder`            | Sign the typed data with the account performing the swap.               |
| `InsufficientBalanceError` | Notify the user that their balance is insufficient to perform the swap. |

Below are examples illustrating the response for each response type.

<tabgroup>
<tab label="GraphQL">

```jsonc title="PreparedOrder"
{
  "data": {
    "prepareOrder": {
      "__typename": "PreparedOrder",
      "newQuoteId": "0x8f3…",
      "data": {
        // …
      }
    }
  }
}
```

```json title="Error"
{
  "data": {
    "prepareOrder": {
      "__typename": "InsufficientBalanceError",
      "required": {
        "value": "150.000000",
        "decimals": 6
      },
      "available": {
        "value": "75.500000",
        "decimals": 6
      }
    }
  }
}
```

</tab>
</tabgroup>
#### 8. Execute the Swap

Then, execute the swap. In React, the approach differs based on order type: market orders execute immediately at current prices, while limit orders let you set specific price conditions.

For market orders, call `swap` with the `quoteId` from the last market order quote.

```tsx title="Basic Usage"
const execute = async (quote: SwapQuote) => {
  const result = await swap({
    fromQuote: {
      quoteId: quote.quoteId,
    },
  });

  if (result.isErr()) {
    console.error(result.error);
    return;
  }

  // result.value: OrderReceipt
  console.log("Swap order placed:", result.value);
};
```

For limit orders, call `swap` with a `limit` request that includes the `previousQuoteId` from the last swap quote.

```tsx title="Execute Limit Order Request"
const execute = async ({ limit }: TokenSwapQuoteRequest) => {
  const result = await swap({
    limit: {
      ...limit,
      previousQuoteId: quote.quoteId, // from prior market order quote
    },
  });

  if (result.isErr()) {
    console.error(result.error);
    return;
  }

  // result.value: OrderReceipt
  console.log("Swap order placed:", result.value);
};
```

- **TypeScript**: Use the `submitOrder` action to submit the swap order. For transaction-based swaps, use the `sendWith` helper to send the transaction.
- **GraphQL**: Submit the swap order using the `submitOrder` mutation.

<tabgroup>
<tab label="TypeScript">

```ts title="Execute Swap"
import { okAsync } from "@aave/client";
import { submitOrder, tokenSwapQuote } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";

import { client } from "./client";
import { handleApprovals } from "./handleApprovals";
import { wallet } from "./wallet";

const result = await tokenSwapQuote(client, request)
  .andThen(handleApprovals)
  .andThen((request) => submitOrder(client, request))
  .andThen((plan) => {
    switch (plan.__typename) {
      case "OrderReceipt":
        return okAsync(plan);

      case "OrderTransactionRequest":
        return sendWith(wallet, plan.transaction).map(() => plan.orderReceipt);
    }
  });

// …
```

```ts title="wallet.ts"
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

export const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
  chain: mainnet,
  transport: http(),
});
```

</tab>
<tab label="GraphQL">

```graphql title="Mutation"
mutation ($request: SubmitOrderRequest!) {
  submitOrder(request: $request) {
    __typename
    ... on OrderTransactionRequest {
      ...OrderTransactionRequest
    }
    ... on OrderReceipt {
      ...OrderReceipt
    }
    ... on InsufficientBalanceError {
      ...InsufficientBalanceError
    }
  }
}
```

```graphql title="OrderTransactionRequest"
fragment OrderTransactionRequest on OrderTransactionRequest {
  __typename
  transaction {
    ...TransactionRequest
  }
  orderReceipt {
    ...OrderReceipt
  }
}
```

```graphql title="OrderReceipt"
fragment OrderReceipt on OrderReceipt {
  __typename
  orderId
}
```

</tab>
</tabgroup>
For intent swaps, pass the `newQuoteId` from `prepareOrder` and the signature in `bySignature`. For transaction swaps, pass the `quote.orderQuoteId` from `tokenSwapQuote` in `byTransaction`.

<tabgroup>
<tab label="GraphQL">

```json title="Swap Intent"
{
  "request": {
    "bySignature": {
      "quoteId": "<prepareOrder.newQuoteId>",
      "signature": "0x456…"
    }
  }
}
```

```json title="Swap Transaction"
{
  "request": {
    "byTransaction": {
      "quoteId": "<tokenSwapQuote.quote.orderQuoteId>"
    }
  }
}
```

</tab>
</tabgroup>
#### 9. Handle the Result

Finally, handle the result.

<tabgroup>
<tab label="React">

```tsx title="Handle Result"
if (result.isErr()) {
  switch (result.error.name) {
    case "CancelError":
      // The user cancelled the operation
      return;

    case "SigningError":
      console.error(`Failed to sign: ${result.error.message}`);
      break;

    case "TimeoutError":
      console.error(`Transaction timed out: ${result.error.message}`);
      break;

    case "TransactionError":
      console.error(`Transaction failed: ${result.error.message}`);
      break;

    case "ValidationError":
      switch (result.error.cause.__typename) {
        case "InsufficientBalanceError":
          console.error(
            `Insufficient balance: ${result.error.cause.required.value.toDisplayString(2)} required.`,
          );
          break;
        case "InsufficientLiquidityError":
          console.error(`Insufficient liquidity: ${result.error.cause.reason}`);
          break;
      }
      break;

    case "UnexpectedError":
      console.error(result.error.message);
      break;
  }
  return;
}
// result.value: OrderReceipt
console.log("Swap order placed:", result.value.orderId);
```

</tab>
<tab label="TypeScript">

```ts title="Handle Result"
// …

if (result.isErr()) {
  switch (result.error.name) {
    case "CancelError":
      console.error("Operation cancelled by user wallet");
      break;

    case "SigningError":
      console.error("Signing failed:", result.error.message);
      break;

    case "TransactionError":
      console.error("Transaction failed:", result.error.message);
      break;

    case "ValidationError":
      switch (result.error.cause.__typename) {
        case "InsufficientBalanceError":
          console.error(
            `Insufficient balance: ${result.error.cause.required.value.toDisplayString(2)} required.`,
          );
          break;
        case "InsufficientLiquidityError":
          console.error(`Insufficient liquidity: ${result.error.cause.reason}`);
          break;
      }
      break;

    case "UnexpectedError":
      console.error("Unexpected error:", result.error.message);
      break;
  }
  return;
}

// result.value: OrderReceipt
console.log("Swap order placed:", result.value.orderId);
```

</tab>
</tabgroup>
In GraphQL, depending on the `OrderExecutionPlan` type, you’ll need to follow different actions:

| Response Type                | Action                                                                  |
| ---------------------------- | ----------------------------------------------------------------------- |
| `OrderTransactionRequest`  | Send the transaction with the account performing the swap.              |
| `OrderReceipt`             | The swap order has been placed.                                         |
| `InsufficientBalanceError` | Notify the user that their balance is insufficient to perform the swap. |

Below are examples illustrating the response for each response type.

<tabgroup>
<tab label="GraphQL">

```jsonc title="OrderTransactionRequest"
{
  "data": {
    "submitOrder": {
      "__typename": "OrderTransactionRequest",
      "transaction": {
        // …
      },
      "orderReceipt": {
        "__typename": "OrderReceipt",
        "orderId": "0x123…"
      }
    }
  }
}
```

```json title="OrderReceipt"
{
  "data": {
    "submitOrder": {
      "__typename": "OrderReceipt",
      "orderId": "0x123…"
    }
  }
}
```

```json title="Error"
{
  "data": {
    "submitOrder": {
      "__typename": "InsufficientBalanceError",
      "required": {
        "value": "150.000000",
        "decimals": 6
      },
      "available": {
        "value": "75.500000",
        "decimals": 6
      }
    }
  }
}
```

</tab>
</tabgroup>
You placed your first swap order. Keep track of its status in the [Monitor Swap Status](#monitor-swap-status) section.

## Monitor Swap Status

Once the swap order is submitted, an `OrderReceipt` object is returned with an `orderId` field. Use this `orderId` to track the order status.
For more details, see the [Execute Swaps](#execute-swaps) section.

- **React**: Use the `useOrderStatus` hook to monitor the status of an order in real-time.
- **TypeScript**: Use the `orderStatus` action to check the current status of an order, or `waitForOrderOutcome` to poll until it reaches a terminal state.
- **GraphQL**: Use the `orderStatus` query to track the order status.

<tabgroup>
<tab label="React">

```tsx title="Loading State"
import { useOrderStatus, type OrderReceipt } from "@aave/react";

function MonitorSwap({ orderReceipt }: { orderReceipt: OrderReceipt }) {
  const { data, loading, error } = useOrderStatus({
    id: orderReceipt.orderId,
  });

  if (loading) return Monitoring swap…;

  if (error) return Error: {error.message};

  switch (data.__typename) {
    case "OrderOpen":
      return Swap is open, waiting for execution…;

    case "OrderPendingSignature":
      return Waiting for signature…;

    case "OrderFulfilled":
      return (
        <div>
          Swap completed successfully!
          <a href={data.explorerUrl}>View transaction</a>
        </div>
      );

    case "OrderCancelled":
      return Swap was cancelled at {data.cancelledAt};

    case "OrderExpired":
      return Swap expired at {data.expiredAt};
  }
}
```

```tsx title="React Suspense"
import { useOrderStatus, type OrderReceipt } from "@aave/react";

function MonitorSwap({ orderReceipt }: { orderReceipt: OrderReceipt }) {
  const { data } = useOrderStatus({
    id: orderReceipt.orderId,
    suspense: true,
  });

  switch (data.__typename) {
    case "OrderOpen":
      return Swap is open, waiting for execution…;

    case "OrderPendingSignature":
      return Waiting for signature…;

    case "OrderFulfilled":
      return (
        <div>
          Swap completed successfully!
          <a href={data.explorerUrl}>View transaction</a>
        </div>
      );

    case "OrderCancelled":
      return Swap was cancelled at {data.cancelledAt};

    case "OrderExpired":
      return Swap expired at {data.expiredAt};
  }
}
```

</tab>
<tab label="TypeScript">

```ts title="Check Status"
import { orderStatus } from "@aave/client/actions";

import { client } from "./client";

const result = await orderStatus(client, { id: orderReceipt.orderId });

if (result.isErr()) {
  console.error(result.error);
} else {
  // result.value: OrderStatus | null
  switch (result.value?.__typename) {
    case "OrderOpen":
      console.log("Swap is open, waiting for execution…");
      break;
    case "OrderPendingSignature":
      console.log("Waiting for signature…");
      break;
    case "OrderFulfilled":
      console.log("Swap completed:", result.value.txHash);
      break;
    case "OrderCancelled":
      console.log("Swap was cancelled:", result.value.cancelledAt);
      break;
    case "OrderExpired":
      console.log("Swap expired:", result.value.expiredAt);
      break;
  }
}
```

```ts title="Poll Until Terminal State"
import { waitForOrderOutcome } from "@aave/client/actions";

import { client } from "./client";

const result = await waitForOrderOutcome(client)(orderReceipt);

// result.value: OrderFulfilled | OrderCancelled | OrderExpired
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
query {
  orderStatus(request: { id: "<orderReceipt.orderId>" }) {
    __typename
    ... on OrderOpen {
      ...OrderOpen
    }
    ... on OrderPendingSignature {
      ...OrderPendingSignature
    }
    ... on OrderCancelled {
      ...OrderCancelled
    }
    ... on OrderExpired {
      ...OrderExpired
    }
    ... on OrderFulfilled {
      ...OrderFulfilled
    }
  }
}
```

</tab>
</tabgroup>
In React, the hook automatically polls for status updates until the order reaches a terminal state: fulfilled, cancelled, or expired.

In GraphQL, the `OrderStatus` object can be one of the following:

<tabgroup>
<tab label="GraphQL">

```graphql title="OrderOpen"
fragment OrderOpen on OrderOpen {
  __typename
  orderId
  createdAt
  deadline
  explorerUrl
  operation {
    ...OrderOperation
  }
}
```

```graphql title="OrderPendingSignature"
fragment OrderPendingSignature on OrderPendingSignature {
  __typename
  orderId
  createdAt
  deadline
  explorerUrl
  operation {
    ...OrderOperation
  }
}
```

```graphql title="OrderCancelled"
fragment OrderCancelled on OrderCancelled {
  __typename
  orderId
  createdAt
  cancelledAt
  explorerUrl
  operation {
    ...OrderOperation
  }
}
```

```graphql title="OrderExpired"
fragment OrderExpired on OrderExpired {
  __typename
  orderId
  createdAt
  expiredAt
  explorerUrl
  operation {
    ...OrderOperation
  }
}
```

```graphql title="OrderFulfilled"
fragment OrderFulfilled on OrderFulfilled {
  __typename
  orderId
  createdAt
  fulfilledAt
  txHash
  explorerUrl
  operation {
    ...OrderOperation
  }
}
```

```graphql title="OrderOperation"
fragment OrderOperation on OrderOperation {
  __typename
  ... on TokenSwap {
    sell {
      ...TokenAmount
    }
    buy {
      ...TokenAmount
    }
    kind
    orderClass
  }
  ... on SupplySwap {
    sell {
      ...PositionAmount
    }
    buy {
      ...PositionAmount
    }
    kind
    orderClass
  }
  ... on BorrowSwap {
    sell {
      ...PositionAmount
    }
    buy {
      ...PositionAmount
    }
    kind
    orderClass
  }
  ... on RepayWithSupply {
    repay {
      ...PositionAmount
    }
    supply {
      ...PositionAmount
    }
    kind
    orderClass
  }
  ... on WithdrawSwap {
    withdraw {
      ...PositionAmount
    }
    buy {
      ...TokenAmount
    }
    kind
    orderClass
  }
  ... on Leverage {
    collateral {
      ...PositionAmount
    }
    debt {
      ...PositionAmount
    }
    multiplier
  }
}
```

</tab>
</tabgroup>
## User Swaps

List and filter user orders with built-in pagination support.

Use the `usePendingOrders` hook in React, the `pendingOrders` action in TypeScript, or the `pendingOrders` query in GraphQL to list user orders. Use `filterBy` to narrow the list by order status.

<tabgroup>
<tab label="React">

```tsx title="Loading State"
import { usePendingOrders, type PendingOrdersRequest } from "@aave/react";

function ListUserOrders({ request }: { request: PendingOrdersRequest }) {
  const { data, loading, error } = usePendingOrders(request);

  if (loading) return <div>Loading…</div>;

  if (error) return <div>Error: {error.message}</div>;

  // data.items: Array<OrderStatus>
  return (
    <div>
      <h3>User Orders</h3>
      {data.items.map((order) => (
        <div key={order.orderId}>
          <p>
            Status - {order.__typename} - {order.explorerUrl}
          </p>
        </div>
      ))}
    </div>
  );
}
```

```tsx title="React Suspense"
import { usePendingOrders, type PendingOrdersRequest } from "@aave/react";

function ListUserOrders({ request }: { request: PendingOrdersRequest }) {
  const { data } = usePendingOrders({
    ...request,
    suspense: true,
  });

  if (data.items.length === 0) return <div>No orders found</div>;

  return (
    <div>
      <h3>User Orders</h3>
      {data.items.map((order) => (
        <div key={order.orderId}>
          <p>
            Status - {order.__typename} - {order.explorerUrl}
          </p>
        </div>
      ))}
    </div>
  );
}
```

</tab>
<tab label="TypeScript">

```ts title="Basic Usage"
import { chainId, evmAddress } from "@aave/client";
import { pendingOrders } from "@aave/client/actions";

import { client } from "./client";

const result = await pendingOrders(client, {
  chainId: chainId(1),
  user: evmAddress("0x742d35cc…"),
});

if (result.isErr()) {
  console.error(result.error);
}
```

```ts title="Filter by Status"
import { chainId, evmAddress, OrderStatusFilter } from "@aave/client";
import { pendingOrders } from "@aave/client/actions";

import { client } from "./client";

const result = await pendingOrders(client, {
  chainId: chainId(1),
  user: evmAddress("0x742d35cc…"),
  filterBy: [OrderStatusFilter.Open, OrderStatusFilter.PendingSignature],
});

if (result.isErr()) {
  console.error(result.error);
}
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
query ($request: PendingOrdersRequest!) {
  pendingOrders(request: $request) {
    __typename
    items {
      ...OrderStatus
    }
    pageInfo {
      next
      prev
    }
  }
}
```

```graphql title="OrderStatus"
fragment OrderStatus on OrderStatus {
  __typename
  ... on OrderOpen {
    ...OrderOpen
  }
  ... on OrderPendingSignature {
    ...OrderPendingSignature
  }
  ... on OrderCancelled {
    ...OrderCancelled
  }
  ... on OrderExpired {
    ...OrderExpired
  }
  ... on OrderFulfilled {
    ...OrderFulfilled
  }
}
```

</tab>
</tabgroup>
In React, the hook automatically polls for status updates until all listed orders reach a terminal state: fulfilled, cancelled, or expired.

The `OrderStatus` fragments are listed under [Monitor Swap Status](#monitor-swap-status).

- **React**: You can filter orders as follows.
- **GraphQL**: The `PendingOrdersRequest` object should be defined as follows.

<tabgroup>
<tab label="React">

```tsx title="By Chain ID"
import { evmAddress, chainId } from "@aave/react";

const request: PendingOrdersRequest = {
  user: evmAddress("0x742d35cc…"),
  chainId: chainId(1),
};
```

```tsx title="By Status"
import { evmAddress, chainId, OrderStatusFilter } from "@aave/react";

const request: PendingOrdersRequest = {
  user: evmAddress("0x742d35cc…"),
  chainId: chainId(1),
  filterBy: [OrderStatusFilter.Fulfilled, OrderStatusFilter.Cancelled],
};
```

</tab>
<tab label="GraphQL">

```json title="Basic Request"
{
  "request": {
    "chainId": 1,
    "user": "0x742d35cc…"
  }
}
```

```json title="Request with Status Filter"
{
  "request": {
    "chainId": 1,
    "user": "0x742d35cc…",
    "filterBy": ["FULFILLED"]
  }
}
```

</tab>
</tabgroup>
## Cancel a Swap

Before cancelling, identify the swap order by its status.

> [!NOTE]
> Only orders with status `OrderOpen` or `OrderPendingSignature` can be
> cancelled.

Pick the `OrderOpen` item (`order`) to cancel.

```ts title="User Orders"
const orderItems: OrderStatus[] = [
  {
    __typename: "OrderOpen",
    orderId: "0x123…",
    // …
  },
  {
    __typename: "OrderPendingSignature",
    // …
  },
  {
    __typename: "OrderCancelled",
    // …
  },
  {
    __typename: "OrderExpired",
    // …
  },
  {
    __typename: "OrderFulfilled",
    // …
  },
];
```

After identifying the order, follow the steps below to cancel it with AaveKit.

### 1. Configure Wallet Integration

First, in React, instantiate the `useSendTransaction` and `useSignTypedData` hooks for the [wallet library of your choice](../getting-started/react#integrations).

<tabgroup>
<tab label="React">

```tsx title="Viem"
import { useWalletClient } from "wagmi";
import { useSendTransaction, useSignTypedData } from "@aave/react/viem";

// …

const { data: wallet } = useWalletClient();
const [sendTransaction] = useSendTransaction(wallet);
const [signTypedData, signing] = useSignTypedData(wallet);
```

</tab>
</tabgroup>
### 2. Define the Cancel Flow

Then, in React, use the `useCancelOrder` hook to prepare the cancel order operation. The hook checks the order status first, then prepares, signs, and submits the cancellation.

<tabgroup>
<tab label="React">

```tsx title="Basic Usage"
import { useCancelOrder } from "@aave/react";

const [cancelOrder, { loading, error }] = useCancelOrder((plan) => {
  switch (plan.__typename) {
    case "TransactionRequest":
      return sendTransaction(plan);

    case "OrderTypedData":
      return signTypedData(plan);
  }
});
```

```tsx title="Update Component State"
import { useCancelOrder } from "@aave/react";

const [status, setStatus] = useState("");

const [cancelOrder, { loading, error }] = useCancelOrder((plan) => {
  switch (plan.__typename) {
    case "TransactionRequest":
      setStatus("Please send the Cancel Order transaction in your wallet");
      return sendTransaction(plan).andTee(() => setStatus("Cancelling…"));

    case "OrderTypedData":
      setStatus("Please sign the Cancel Order data in your wallet");
      return signTypedData(plan).andTee(() => setStatus("Cancelling…"));
  }
});
```

```tsx title="Cancel Flow"
import { useCancelOrder } from "@aave/react";

const [cancelOrder, { loading, error }] = useCancelOrder((plan, { cancel }) => {
  if (window.confirm("Are you sure you want to cancel the swap?") === false) {
    return cancel("User cancelled the operation");
  }

  switch (plan.__typename) {
    case "TransactionRequest":
      return sendTransaction(plan);

    case "OrderTypedData":
      return signTypedData(plan);
  }
});
```

</tab>
</tabgroup>
### 3. Prepare Cancel Swap

- **TypeScript**: Prepare the cancel typed data with `prepareCancelOrder`.
- **GraphQL**: Use the `prepareCancelOrder` query to prepare the cancel.

<tabgroup>
<tab label="TypeScript">

```ts title="Prepare Cancel Swap"
import { prepareCancelOrder } from "@aave/client/actions";

import { client } from "./client";

const result = await prepareCancelOrder(client, {
  id: order.orderId,
});
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
query {
  prepareCancelOrder(request: { id: "<order.orderId>" }) {
    __typename
    data {
      ...OrderTypedData
    }
  }
}
```

```graphql title="OrderTypedData"
fragment OrderTypedData on OrderTypedData {
  __typename
  types
  primaryType
  domain {
    ...DomainData
  }
  message
}
```

</tab>
</tabgroup>
- **TypeScript**: Use `signTypedDataWith` for the [wallet library of your choice](../getting-started/typescript#integrations) to sign the typed data in `result.data`.
- **GraphQL**: Sign the `data` of the `PrepareCancelOrderResult` response with the account that placed the order.

<tabgroup>
<tab label="TypeScript">

```ts title="Sign Cancel Swap"
import { prepareCancelOrder } from "@aave/client/actions";
import { signTypedDataWith } from "@aave/client/viem";

import { client } from "./client";
import { wallet } from "./wallet";

const result = await prepareCancelOrder(client, {
  id: order.orderId,
}).andThen((prepared) => signTypedDataWith(wallet, prepared.data));

if (result.isErr()) {
  console.error(result.error);
}
```

```ts title="wallet.ts"
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

export const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
  chain: mainnet,
  transport: http(),
});
```

</tab>
<tab label="GraphQL">

```jsonc title="PrepareCancelOrderResult"
{
  "data": {
    "prepareCancelOrder": {
      "__typename": "PrepareCancelOrderResult",
      "data": {
        // …
      }
    }
  }
}
```

</tab>
</tabgroup>
### 4. Execute Cancel Swap

Then, execute the cancel order operation.

- **TypeScript**: Execute the cancel with the `cancelOrder` action.
- **GraphQL**: Use the `cancelOrder` mutation to execute the cancel.

<tabgroup>
<tab label="React">

```tsx title="Cancel Swap"
const execute = async () => {
  const result = await cancelOrder({
    id: order.orderId,
  });
};
```

</tab>
<tab label="TypeScript">

```ts title="Execute Cancel Swap"
import { prepareCancelOrder, cancelOrder } from "@aave/client/actions";
import { signTypedDataWith } from "@aave/client/viem";

import { client } from "./client";
import { wallet } from "./wallet";

const result = await prepareCancelOrder(client, {
  id: order.orderId,
})
  .andThen((prepared) => signTypedDataWith(wallet, prepared.data))
  .andThen((signature) =>
    cancelOrder(client, {
      bySignature: {
        id: order.orderId,
        signature,
      },
    }),
  );
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
mutation ($request: CancelOrderRequest!) {
  cancelOrder(request: $request) {
    __typename
    ... on TransactionRequest {
      ...TransactionRequest
    }
    ... on OrderCancelledResult {
      ...OrderCancelledResult
    }
  }
}
```

```graphql title="OrderCancelledResult"
fragment OrderCancelledResult on OrderCancelledResult {
  __typename
  orderId
}
```

</tab>
</tabgroup>
In TypeScript, use `sendWith` for the [wallet library of your choice](../getting-started/typescript#integrations) to send transactions in the plan.

<tabgroup>
<tab label="TypeScript">

```ts title="Send Transaction"
import { okAsync } from "@aave/client";
import {
  prepareCancelOrder,
  cancelOrder,
  waitForOrderOutcome,
} from "@aave/client/actions";
import { signTypedDataWith, sendWith } from "@aave/client/viem";

import { client } from "./client";
import { wallet } from "./wallet";

const result = await prepareCancelOrder(client, {
  id: order.orderId,
})
  .andThen((prepared) => signTypedDataWith(wallet, prepared.data))
  .andThen((signature) =>
    cancelOrder(client, {
      bySignature: {
        id: order.orderId,
        signature,
      },
    }),
  )
  .andThen((plan) => {
    switch (plan.__typename) {
      case "TransactionRequest":
        return sendWith(wallet, plan).andThen(() =>
          waitForOrderOutcome(client)({
            __typename: "OrderReceipt",
            orderId: order.orderId,
          }),
        );

      case "OrderCancelledResult":
        return okAsync(plan);
    }
  });
```

</tab>
</tabgroup>
In GraphQL, the `CancelOrderRequest` object takes the signed typed data from the `prepareCancelOrder` query in `bySignature`, or the order ID in `byTransaction`.

<tabgroup>
<tab label="GraphQL">

```json title="Cancel Swap Intent"
{
  "request": {
    "bySignature": {
      "id": "<order.orderId>",
      "signature": "0x456…"
    }
  }
}
```

```json title="Cancel Swap Transaction"
{
  "request": {
    "byTransaction": "<order.orderId>"
  }
}
```

</tab>
</tabgroup>
### 5. Handle the Result

Finally, handle the result.

<tabgroup>
<tab label="React">

```tsx title="Handle Result"
if (result.isErr()) {
  switch (result.error.name) {
    case "CancelError":
      // The user cancelled the operation
      return;

    case "SigningError":
      console.error(`Failed to sign the transaction: ${result.error.message}`);
      break;

    case "TimeoutError":
      console.error(`Transaction timed out: ${result.error.message}`);
      break;

    case "TransactionError":
      console.error(`Transaction failed: ${result.error.message}`);
      break;

    case "CannotCancelOrderError":
      console.error(`Cannot cancel: ${result.error.message}`);
      break;

    case "UnexpectedError":
      console.error(result.error.message);
      break;
  }
  return;
}

// result.value: OrderCancelledResult
console.log("Order cancelled:", result.value.orderId);
```

</tab>
<tab label="TypeScript">

```ts title="Handle Result"
if (result.isErr()) {
  switch (result.error.name) {
    case "CancelError":
      console.error("Operation cancelled by user wallet");
      break;

    case "SigningError":
      console.error(`Failed to sign the transaction: ${result.error.message}`);
      break;

    case "TimeoutError":
      console.error(`Transaction timed out: ${result.error.message}`);
      break;

    case "TransactionError":
      console.error(`Transaction failed: ${result.error.message}`);
      break;

    case "UnexpectedError":
      console.error(result.error.message);
      break;
  }
  return;
}

// result.value: OrderCancelledResult | OrderCancelled | OrderExpired | OrderFulfilled
console.log("Order status:", result.value.__typename);
```

</tab>
</tabgroup>
In TypeScript, when the cancellation is sent as a transaction, `waitForOrderOutcome` resolves to the final order status. Check for `OrderCancelled`, since the order may be fulfilled or expire before the cancellation takes effect.

In GraphQL, depending on the `CancelOrderExecutionPlan` type, you’ll need to follow different actions:

| Response Type          | Action                                                                                   |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| `TransactionRequest`   | Send the transaction with the wallet associated with the account that placed the order.  |
| `OrderCancelledResult` | Order was cancelled successfully.                                                        |

Below are examples illustrating the response for each response type.

<tabgroup>
<tab label="GraphQL">

```jsonc title="TransactionRequest"
{
  "data": {
    "cancelOrder": {
      "__typename": "TransactionRequest"
      // …
    }
  }
}
```

```json title="OrderCancelledResult"
{
  "data": {
    "cancelOrder": {
      "__typename": "OrderCancelledResult",
      "orderId": "0x123…"
    }
  }
}
```

</tab>
</tabgroup>
You cancelled your first swap order. Keep track of its status in the [Monitor Swap Status](#monitor-swap-status) section.

## Fees

Swaps include a network fee, partner fee, provider fee, and in the case of [Position Swaps](../positions/swaps/supply) a flash loan fee.

### Network Fee

The network fee covers gas for executing the swap. Transaction-based swaps pay it in the chain’s native token (e.g., ETH on Ethereum). Intent-based swaps are gasless for the user: the solver’s cost is included in the quote under `costs.networkCosts`.

### Partner Fee

Aave Labs applies a fee to swaps done through the Aave Labs API. The fee depends on the [swap operation](../positions/swaps/supply) and token pair:

- **Swap Borrow Asset**: 0% fee across all pairs.
- **All other swap operations**: a discounted fee of **0.15%** is applied to swaps between correlated assets (e.g., ETH/wstETH), while **0.25%** is applied to all other pairs.

> [!NOTE]
> Correlated assets keep a stable price relationship, usually pegged to the same
> asset or index. Their lower price risk earns the discounted fee.

The following asset groups are considered correlated (list reviewed and updated periodically):

- **Stablecoins**: USDC, USDT, DAI, GHO, EURC, USDbC, USDe, USDS, sUSDe, RLUSD, PYUSD, LUSD, sDAI, crvUSD, USD₮0, USDC.e, EURe, xDAI, wxDAI
- **ETH correlated**: weETH, ETH, WETH, wstETH, cbETH, ezETH, wrsETH, osETH, rETH, ETHx
- **BTC correlated**: cbBTC, WBTC, LBTC, tBTC, eBTC

### Provider Fee

CoW Protocol charges a fee for swap execution: **0.02%** for most pairs, reduced to **0.003%** for tokens in their [Reduced Fee Tokens](https://forum.cow.fi/t/cip-74-retrospective-aligning-rewards-with-revenue/3358) list (a dynamic registry of major stablecoins and RWAs based on price volatility). The fee is included in the swap quote.

### Flash Loan Fee

Some of the [Position Swaps](../positions/swaps/supply) require flash loans to execute atomically. When applicable, a flash loan fee of **0.05%** of the flash-loaned amount is charged. This fee is collected by the protocol treasury and controlled by the DAO through governance.

Flash loan fees apply to operations that require borrowing assets temporarily (e.g., Swap Borrow Asset). Operations that use existing positions (e.g., Withdraw and Swap) typically do not incur flash loan fees.
