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

# Swap

> Cross-chain token swaps and bridges using Circuit's swap engine. The engine automatically selects the best route across multiple liquidity sources.

Use swap to move tokens between chains or swap between assets on the same chain. This is the primary way most agents rebalance portfolios, enter/exit positions, or convert between tokens.

### Workflow

1. Get a quote with `quote()`
2. Execute the swap with `execute()`
3. Check `data.status` in the response

### Get Quote

Get pricing and routing information for a swap.

<CodeGroup>
  ```python Python theme={null}
  def quote(request: SwapQuoteRequest | SwapQuoteRequestInput) -> SwapQuoteResponse
  ```

  ```typescript TypeScript theme={null}
  async quote(request: SwapQuoteRequest): Promise<SwapQuoteResponse>
  ```
</CodeGroup>

**Request Parameters:**

* `from` (object): Source wallet `{ network, address }`
* `to` (object): Destination wallet `{ network, address }`
* `amount` (string): Amount in smallest unit (wei, lamports, etc.)
* `fromToken` (string | null, optional): Source token address (omit for native tokens)
* `toToken` (string | null, optional): Destination token address (omit for native tokens)
* `slippage` (string, optional): Slippage tolerance % as string (default: "0.5")
* `engines` (string\[], optional): Restrict routing to specific engines — route providers (`"relay"`, `"lifi"`, `"across"`, `"kyberswap"`, `"paraswap"`, `"jupiter"`) and/or action adapters (`"pusd"`, `"erc4626"`, `"hypercore-deposit"`)

**Response:**

* `success` (boolean): Whether the quote was retrieved
* `data` (object): Quote data (on success)
  * `engine` (string): Routing engine used (e.g., `"relay"`, `"lifi"`, `"across"`, `"kyberswap"`, `"paraswap"`, `"jupiter"`, `"pusd"`, `"erc4626"`)
  * `engines` (string\[], optional): Original routing engine filter when one was requested
  * `assetSend` (object): Source asset details
    * `network` (string): Network identifier
    * `address` (string): On-chain token contract address (or the chain's native-asset sentinel)
    * `token` (string): Token identifier
    * `name` (string): Token name
    * `symbol` (string): Token symbol
    * `decimals` (number): Token decimals
    * `amount` (string): Amount in smallest units
    * `amountFormatted` (string): Human-readable amount
    * `amountUsd` (string, optional): Approximate value in USD
  * `assetReceive` (object): Destination asset details (same shape as `assetSend`, plus an optional `minimumAmount` — the slippage floor in smallest units)
  * `priceImpact` (object): Price impact info
    * `percentage` (string): Price impact as a percentage
    * `usd` (string, optional): Price impact in USD
  * `fees` (array): Fee breakdown
    * `name` (string): Fee label
    * `amount` (string, optional): Fee amount in smallest units
    * `amountFormatted` (string, optional): Human-readable fee
    * `amountUsd` (string, optional): Fee value in USD
  * `steps` (array): Transaction steps to execute
  * `quoteFreshness` (object, optional): Quote expiry metadata
    * `issuedAtMs` (number): Quote issue time in milliseconds
    * `ttlMs` (number): Quote time-to-live in milliseconds
    * `resolvedSlippage` (string): Slippage value applied to this quote
    * `minimumOutputAtIssue` (string): Minimum output amount captured at quote time
    * `signature` (string, optional): Server signature used to reject tampered freshness metadata
* `error` (string | null): Error message if quote failed

**Example:**

<CodeGroup>
  ```python Python theme={null}
  quote = agent.swap.quote({
      "from": {
          "network": "ethereum:137",
          "address": agent.sessionWalletAddress,
      },
      "to": {
          "network": "ethereum:137",
          "address": agent.sessionWalletAddress,
      },
      "amount": "1000000",  # 1 USDC.e (6 decimals)
      "fromToken": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",  # USDC.e
      "toToken": "0xd93f7e271cb87c23aaa73edc008a79646d1f9912",  # wSOL
      "slippage": "10.0",
  })

  if quote.success and quote.data:
      if quote.data.asset_receive.amount_formatted:
          agent.log(f"You'll receive: {quote.data.asset_receive.amount_formatted}")
      if quote.data.price_impact.percentage:
          agent.log(f"Price impact: {quote.data.price_impact.percentage}%")

          # Validate price impact
          if abs(float(quote.data.price_impact.percentage)) > 10:
              agent.log("Price impact too high", error=True)
              return
  ```

  ```typescript TypeScript theme={null}
  const quote = await agent.swap.quote({
    from: { network: "ethereum:137", address: agent.sessionWalletAddress },
    to: { network: "ethereum:137", address: agent.sessionWalletAddress },
    amount: "1000000", // 1 USDC.e (6 decimals)
    fromToken: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", // USDC.e
    toToken: "0xd93f7e271cb87c23aaa73edc008a79646d1f9912", // wSOL
    slippage: "10.0"
  });

  if (quote.success && quote.data) {
    if (quote.data.assetReceive.amountFormatted) {
      await agent.log(`You'll receive: ${quote.data.assetReceive.amountFormatted}`);
    }
    if (quote.data.priceImpact.percentage) {
      await agent.log(`Price impact: ${quote.data.priceImpact.percentage}%`);

      // Validate price impact
      if (Math.abs(parseFloat(quote.data.priceImpact.percentage)) > 10) {
        await agent.log("Price impact too high", { error: true });
        return;
      }
    }
  }
  ```
</CodeGroup>

### Execute Quote

Execute a swap using a quote.

<CodeGroup>
  ```python Python theme={null}
  def execute(quote_data: SwapData | list[SwapData]) -> SwapExecuteResponse | list[SwapExecuteResponse]
  ```

  ```typescript TypeScript theme={null}
  async execute(quoteData: SwapQuote | SwapQuote[]): Promise<SwapExecuteResponse | SwapExecuteResponse[]>
  ```
</CodeGroup>

**Parameters:**

* `quoteData`: Complete quote object from `quote()`, or array of quotes for bulk execution
* `expiresAt` (string | null, optional): ISO 8601 timestamp. In manual mode, the suggestion is discarded if not approved by this time. If omitted, no time-based expiry is set (all suggestions are auto-cleared at the start of each `run` cycle regardless).

**Response:**

* `success` (boolean): Whether execution started
* `data.status` (string): Execution status ("success", "failure", "refund", "delayed")
* `data.in.txs` (string\[]): Input transaction hashes (on success)
* `data.out.txs` (string\[]): Output transaction hashes (on success)
* `error` (string | null): Error message (on failure)

**Example:**

<CodeGroup>
  ```python Python theme={null}
  if quote.success and quote.data:
      result = agent.swap.execute(quote.data)

      if result.success and result.data:
          agent.log(f"Swap status: {result.data.status}")
          if result.data.status == "success":
              agent.log("Swap completed")
              if result.data.in_ and result.data.in_.txs:
                  agent.log(f"In tx: {result.data.in_.txs[0]}")
              if result.data.out and result.data.out.txs:
                  agent.log(f"Out tx: {result.data.out.txs[0]}")
          elif result.data.status == "failure":
              agent.log("Swap failed", error=True)
      else:
          agent.log(result.error or "Execute failed", error=True)
  ```

  ```typescript TypeScript theme={null}
  if (quote.success && quote.data) {
    const result = await agent.swap.execute(quote.data);

    if (result.success && result.data) {
      await agent.log(`Swap status: ${result.data.status}`);
      if (result.data.status === "success") {
        await agent.log("Swap completed");
        if (result.data.in?.txs?.[0]) {
          await agent.log(`In tx: ${result.data.in.txs[0]}`);
        }
        if (result.data.out?.txs?.[0]) {
          await agent.log(`Out tx: ${result.data.out.txs[0]}`);
        }
      } else if (result.data.status === "failure") {
        await agent.log("Swap failed", { error: true });
      }
    } else {
      await agent.log(result.error || "Execute failed", { error: true });
    }
  }
  ```
</CodeGroup>

### Bulk Execution

Execute multiple swaps in parallel by passing an array of quotes:

<CodeGroup>
  ```python Python theme={null}
  results = agent.swap.execute([quote1.data, quote2.data])
  for index, result in enumerate(results):
      if result.success and result.data:
          agent.log(f"Swap {index + 1} status: {result.data.status}")
  ```

  ```typescript TypeScript theme={null}
  const results = await agent.swap.execute([quote1.data, quote2.data]);
  for (let i = 0; i < results.length; i++) {
    if (results[i].success && results[i].data) {
      await agent.log(`Swap ${i + 1} status: ${results[i].data.status}`);
    }
  }
  ```
</CodeGroup>

### Routing Engines

Circuit queries all compatible routing engines and selects the quote with the highest net output amount. Selection is a pure quote competition — there is no static engine preference.

| Engine      | Supported Networks                                                                       |
| ----------- | ---------------------------------------------------------------------------------------- |
| `relay`     | EVM chains, Solana, Hyperliquid (cross-chain)                                            |
| `lifi`      | EVM chains, Solana                                                                       |
| `across`    | EVM chains + Hyperliquid (intent bridge; the Hyperliquid cross-chain fallback to Relay)  |
| `kyberswap` | Same-chain swaps on supported EVM chains                                                 |
| `paraswap`  | Same-chain swaps on supported EVM chains                                                 |
| `jupiter`   | Same-chain swaps on Solana                                                               |
| `wrap`      | Same-chain wrap/unwrap on supported EVM chains (vault share ↔ underlying, pUSD ↔ USDC.e) |

### Notes

* Always validate quotes before executing. Check price impact and slippage.
* Execute quotes promptly. Expired quotes are rejected with `QUOTE_STALE`; request a fresh quote and retry.
* Circuit filters quotes with price impact exceeding 100%. Agents should validate returned quotes based on their own parameters.
* Amounts are in smallest units (wei for ETH, lamports for SOL, etc.).
* Omit `fromToken`/`toToken` for native tokens (ETH, SOL).
* Execution status is final for same-chain swaps. For cross-chain bridges, the `data.status` field in the execute response indicates the final status: `"success"`, `"failure"`, `"refund"`, or `"delayed"`.

### Manual Mode

In manual mode, `execute()` returns a suggestion instead of executing. The response will have `data.suggested = true` and `data.suggestionId`.

### See Also

* [Positions](./positions) — Check balances before and after swaps
* [SDK Quick Reference](./sdk-quick-reference#network-identifiers) — Network identifiers and native token addresses
* [Error Handling](./error-handling#swap-error-codes) — Swap-specific error codes
* [Yield Agent Example](../examples/yield-agent) — Full working agent using swap

### Python: `from` Parameter

<Warning>
  `from` is a reserved keyword in Python. Use the string key `"from"` in dicts (recommended), or `from_` if using the Pydantic model directly.
</Warning>

`from` is a reserved keyword in Python. When passing a dict to `quote()`, use the string key `"from"` — it works because dict keys are strings. If using the Pydantic model directly, the field is `from_`:

```python theme={null}
# Using a dict (recommended) — "from" works as a string key
quote = agent.swap.quote({
    "from": {"network": "ethereum:137", "address": agent.sessionWalletAddress},
    "to": {"network": "ethereum:137", "address": agent.sessionWalletAddress},
    "amount": "1000000",
})

# Using the Pydantic model — use from_ (with underscore)
from circuit_sdk.types.swap import SwapQuoteRequest, SwapWallet
quote = agent.swap.quote(SwapQuoteRequest(
    from_=SwapWallet(network="ethereum:137", address=agent.sessionWalletAddress),
    to=SwapWallet(network="ethereum:137", address=agent.sessionWalletAddress),
    amount="1000000",
))
```
