> For the complete documentation index, see [llms.txt](https://city-protocol.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://city-protocol.gitbook.io/docs/developers/city-protocol-sdk/swap.md).

# Swap

Quote, build and execute token swaps through an aggregator.

`CitySwapClient` quotes, builds and executes token swaps through an aggregator, with validation utilities that catch invalid inputs before any network call.

## How it works

`CitySwapClient` runs a swap in four steps: quote, approval, build and execute. Price discovery stays separate from execution, so an application prices a swap before it commits and keeps full control of the wallet that signs. The client ships in `@cityprotocol/core`, covers EVM and Solana chains and is transport-agnostic and wallet-neutral: it never holds user keys, and every signature comes from the wallet, relayer or abstraction layer that the application supplies. It calls the aggregator API at `CITY_SWAP_API_URL` by default, and the constructor's `baseUrl` overrides it.

| Component               | Methods                                          | What it does                                                                                                                                                   |
| ----------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Quote discovery         | `getQuote`, `getSupportedChains`, `getLiquidity` | Read-only estimates that simulate a swap against live aggregator routes before any transaction is committed, with the chains and liquidity sources behind them |
| Transaction building    | `buildSwap`                                      | Aggregator-generated swap payloads with configurable slippage, gas level and swap mode                                                                         |
| Approval management     | `checkApprovals`, `getApproveTransaction`        | Allowance checks and ERC-20 approval calldata, with revoke-then-approve for stale USDT-like allowances                                                         |
| Execution orchestration | `executeSwap`                                    | The full flow in one call, through an `executeTransaction` callback that the developer supplies                                                                |

Every swap method takes the amount either as `amount`, an integer in minimal units passed as a `string` or `bigint`, or as `readableAmount`, a decimal string. With `readableAmount`, the application supplies the token's decimals, through `tokenDecimals` on each call or a `resolveTokenDecimals` function in the constructor.

## The four-step flow

<figure><img src="https://3297077862-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FJxNp2waFk6hg1G4lEQZu%2Fuploads%2FiwtSGIZwKwtzuyqPolkq%2Ffig-sdk-swap.png?alt=media" alt="Quote, approval, build and execute in the standard flow, with a one-shot executeSwap path from the quote to execution through the developer&#x27;s executeTransaction callback"><figcaption><p>Quote, approval, build and execute. In the standard flow the application sends each transaction; in the one-shot path executeSwap runs approval, build and execution through the developer's callback.</p></figcaption></figure>

{% stepper %}
{% step %}

### Quote

`getQuote` returns the expected output, the price impact and the route, and changes no state. The SDK resolves the common symbols `USDC`, `USDT`, `ETH`, `SOL` and `native` into chain-specific addresses before it calls the aggregator.
{% endstep %}

{% step %}

### Approval

`checkApprovals` reads the wallet's ERC-20 allowance against the aggregator spender, or against a `spender` passed in the call. When the allowance falls short, `getApproveTransaction` returns the approval calldata for the token and amount, ready for the user's wallet to sign.
{% endstep %}

{% step %}

### Build

`buildSwap` fetches the full swap transaction from the aggregator with the wallet address, the gas level, the swap mode and the slippage. When `slippage` is omitted, the SDK requests auto-slippage and sets `slippagePercent` to `0.5`, matching the Rust reference flow.
{% endstep %}

{% step %}

### Execute

The built transaction goes to the user's wallet, a relayer or an abstraction layer, which signs and broadcasts it. In the one-shot path, `executeSwap` hands each prepared transaction to the `executeTransaction` callback.
{% endstep %}
{% endstepper %}

## Approvals and one-shot execution

Approval handling is ERC-20 specific. Tokens such as USDT enforce revoke-then-approve semantics on stale allowances, so the existing allowance is revoked before a new approval is set. `isAllowanceInsufficient` exposes the allowance check, and `executeSwap` runs the revoke cycle itself. The application triggers each approval as its own transaction, which the user's wallet signs.

`executeSwap` runs the whole pipeline in one call: approval lookup, revoke-then-approve for stale allowances, swap build and final execution. The SDK hardcodes no wallet logic. The developer supplies an `executeTransaction` callback that wraps a wallet or relayer, signs and broadcasts each prepared request and returns the result.

* **Callback request:** `to`, `chainIndex`, `value`, `data`, `unsignedTx`, `gasLimit`, `aaDexTokenAddress`, `aaDexTokenAmount`, `mevProtection` and `jitoUnsignedTx`, mirroring the Rust `wallet_contract_call` flow.
* **Response:** `approveTxHash` (null when no approval was sent) and `swapTxHash`, with the swap's `fromToken`, `toToken`, `fromAmount`, `toAmount`, `priceImpact` and `gasUsed`.

Every other method, for quotes, builds, approvals, chains and liquidity, returns the aggregator's `data` payload directly. The quoting methods drive pre-trade interface state and route previews, the build and approval methods give fine-grained control of the approval experience and the transaction lifecycle, and `executeSwap` hands the whole flow to the SDK.

## Methods

| Method                  | Scope                | What it does                                                                                                         |
| ----------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `getQuote`              | Price discovery      | Returns a read-only quote with the output amount, price impact and aggregator route, and commits no state change     |
| `buildSwap`             | Transaction building | Fetches the swap transaction payload with the slippage, gas level and swap mode, `exactIn` or `exactOut`             |
| `getApproveTransaction` | Allowance management | Returns ERC-20 approval calldata for a token and amount, ready for the user's wallet to sign                         |
| `checkApprovals`        | Allowance management | Checks a wallet's allowance against the aggregator spender, or against an explicit `spender` for custom integrations |
| `getSupportedChains`    | Network discovery    | Lists every chain the aggregator supports, for chain pickers                                                         |
| `getLiquidity`          | Network discovery    | Lists the liquidity sources on a chain index                                                                         |
| `executeSwap`           | End-to-end execution | Runs approval lookup, stale-approval handling, the swap build and final execution through the developer's callback   |

## Chain abstraction

One integration covers EVM and Solana without chain-specific integration code. The SDK resolves common token symbols into chain-specific addresses, validates address formats for each chain type and handles the specifics of each environment. On EVM chains, `executeSwap` builds in approval orchestration, with revoke-then-approve for the tokens that require it. On Solana, execution passes through `unsignedTx`, with `jitoUnsignedTx` as the optional MEV-protected variant for bundled execution, and the `executeTransaction` callback signs and submits the variant that the user prefers.

## Validation and safety

The swap client ports the pre-flight checks of the Rust reference flow into TypeScript, so malformed or invalid parameters are rejected before any network call.

* **Input validation:** `validateAmount`, `validateApproveAmount`, `validateSlippage`, `validateSwapMode`, `validateGasLevel` and `validateTips` check amounts, approval amounts, slippage, swap mode, gas level and tips against their expected ranges and formats.
* **Token resolution:** `resolveSwapTokenAddress` resolves chain-specific token aliases, and same-token swaps are rejected as no-ops.
* **Address safety:** `validateAddressForChain` catches an EVM address used on Solana or a Solana address used on EVM, which prevents cross-ecosystem routing errors.
* **Precision safety:** `readableToMinimalString` converts human-readable amounts into minimal units without floating-point math, so decimal-sensitive tokens carry no rounding error.
* **Allowance check:** `isAllowanceInsufficient` reports whether the current allowance covers the swap amount.

## Swap modes

* **Exact input:** With `swapMode` set to `exactIn`, the user fixes the input amount, and the aggregator returns the best achievable output for it within the slippage tolerance.
* **Exact output:** With `swapMode` set to `exactOut`, the user fixes the output amount, and the aggregator calculates the input required to meet it.
* **MEV-protected swaps:** On Solana, the optional `jitoUnsignedTx` carries a bundled transaction for protected submission, which guards against sandwich attacks and front-running.

{% content-ref url="/pages/xvYkQZDuodnE2KrbDhXi" %}
[Getting started](/docs/developers/city-protocol-sdk/swap/getting-started.md)
{% endcontent-ref %}

{% content-ref url="/pages/17W9nIYZqPinMGoqUWuu" %}
[Read methods](/docs/developers/city-protocol-sdk/swap/read-methods.md)
{% endcontent-ref %}

{% content-ref url="/pages/gjukK9fbhSM5XUX705h1" %}
[Build methods](/docs/developers/city-protocol-sdk/swap/build-methods.md)
{% endcontent-ref %}

{% content-ref url="/pages/7QphHrknYyP42EK8kjBM" %}
[Execution and validation](/docs/developers/city-protocol-sdk/swap/execution-and-validation.md)
{% endcontent-ref %}

{% content-ref url="/pages/yH2NyE1TXELetzy9oLEe" %}
[Common frontend flows](/docs/developers/city-protocol-sdk/swap/common-frontend-flows.md)
{% endcontent-ref %}

{% content-ref url="/pages/VWdpDpDoo0uwroXkOrL1" %}
[Recommended swap composition](/docs/developers/city-protocol-sdk/swap/recommended-swap-composition.md)
{% endcontent-ref %}
