--- title: Same-chain swaps description: Swapping two tokens on one chain, settled inside the deposit transaction. --- When `src_chain` equals `dst_chain`, Hular quotes a swap instead of a bridge. There is no destination leg, no relayer, and no inventory involved: the router swaps through an allowlisted aggregator and pays the recipient in the same transaction. Same-chain swaps are EVM-only; on Solana the pair is rejected with `swap quotes unsupported on destination chain`. `GetQuote` returns the `same_chain_swap` case of the response, and `PreviewQuote` sets `same_chain_swap: true`. ```ts import { isSameChainSwap, quoteOf } from "@mayanfinance/hular-sdk"; const response = await hular.getQuote({ ...request, srcChain: "arbitrum", dstChain: "arbitrum" }); const sameChain = isSameChainSwap(response); const quote = quoteOf(response); ``` ## What differs from a bridge | | Cross-chain | Same-chain | | --- | --- | --- | | Settlement | Destination transaction by an executor | Inside the deposit transaction | | Spread component | `bps_spread` | `same_chain_swap_fee` | | Gas fees in the quote | `dst_fulfill_gas`, `src_refund_gas` | none | | Gas drop | Supported | Always zero | | Gasless | Supported | Not available | | `bridge_token_src`, `bridge_token_dst` | The assets moved between chains | Both equal to `dst_token` | Quoting a pair where `src_token` and `dst_token` are the same token on the same chain is rejected as `unsupported pair`. There is nothing to do. ## Executing it Same-chain swaps use `swapAndForward` on the router, which swaps and forwards to the recipient in one call, and emits `SameChainSwap`. ```solidity function swapAndForward( bytes32 quoteHash, address srcToken, uint256 amountIn, address aggregator, bytes calldata swapData, address dstToken, uint256 minAmountOut, uint256 amountOut, uint16 referrerBps, address recipient ) external payable; ``` Build the arguments from the quote: | Argument | Source | | --- | --- | | `quoteHash` | `quote.quote_hash` | | `srcToken`, `amountIn` | `order_params.src_token`, `order_params.amount_in` | | `aggregator`, `swapData` | `GetSwapInstructions` on the quote hash, `payload.evm` | | `dstToken` | `order_params.dst_token` | | `minAmountOut` | `order_params.min_amount_out` | | `amountOut` | `order_params.amount_out` | | `referrerBps` | The total basis points of `order_params.referrers` | | `recipient` | `order_params.recipient` | The delivered amount is floored at `minAmountOut` and capped at `amountOut`; anything above the cap stays in the solver vault. Below the floor, the call reverts and the user keeps their funds. ERC-20 sources need an allowance for `amountIn` first, or use `swapAndForwardWithPermit` to sign instead of approving. The chain adapter builds this for you. It recognizes a same-chain quote from its parameters and emits `swapAndForward` rather than a deposit, including the approval step when the source token needs one: ```ts const adapter = createChainAdapter(client, chainStatus, chainId); const steps = await adapter.steps(wallet, quote.quoteHash, quote.orderParams); for (const step of steps) { await step.run(); } ``` ## Tracking The `SameChainSwap` event is indexed like a deposit. Once it reaches the required confirmations the order is finalized as `fulfilled`; there is no `fulfilling` phase, because settlement already happened. Refunds do not apply in the normal sense: a swap that cannot clear its floor reverts rather than taking the user's funds. A deposit that does not match a same-chain quote terminates as `unsupported_deposit`, which needs manual handling. See [Refunds](/concepts/refunds).