TradingClient calls to run a swap: it pulls the input in, routes through Uniswap V3, takes fees, and sweeps the output back to Spark in one call. Most integrators never touch it. Use TradingClient.swap; it picks the right Conductor method, signs, and submits for you.
This page documents the contract surface for callers who bypass the SDK: wiring a swap into a contract you wrote, building a UI that constructs intents by hand, or batching several swaps into one signed transaction.
Swap methods
TradingClient.swap selects one of nine Conductor methods from the input asset, output asset, withdraw flag, and approval mode:
| Input → Output | Withdraw | Approval | Method |
|---|---|---|---|
| token → token | no | n/a | swap |
| BTC → token | no | n/a | swapBTC |
| BTC → token | yes | n/a | swapBTCAndWithdraw |
| token → BTC | yes | exact | swapAndWithdrawBTC |
| token → token | yes | exact | swapAndWithdraw |
| token → BTC | yes | Permit2 | swapAndWithdrawBTCWithPermit2 |
| token → token | yes | Permit2 | swapAndWithdrawWithPermit2 |
| token → BTC | yes | EIP-2612 | swapAndWithdrawBTCWithEIP2612 |
| token → token | yes | EIP-2612 | swapAndWithdrawWithEIP2612 |
PermitTransferFrom. EIP-2612 is per-token, implemented by SparkToken, and is the path useAvailableBalance takes. BTC inputs carry their amount in msg.value, so they never need a token permit, which is why the matrix has no BTC-input permit rows.
The swap call
swap is the base method. Its on-chain signature:
integrator is the Spark-routed fee recipient (zero address for none), integratorBps its cut (0 to 1000), and deadline a unix-seconds expiry. The *AndWithdraw* variants insert a bytes sparkRecipient before integrator. The Permit2 and EIP-2612 variants drop deadline, since the signature carries its own, and append the permit struct.
Encode calldata with viem and the Conductor ABI:
encodeFunctionData returns 0x-prefixed calldata. Wrap it in an EIP-1559 transaction, sign it, and submit through ExecutionClient.execute, which expects the RLP-hex of the signed transaction, not the calldata. For swapBTC and the BTC-input variants, set the transaction value to the input in wei (sats × WEI_PER_SAT, where WEI_PER_SAT = 10^10); for token inputs, set value: 0n.
Fees
The Conductor skims up to three fees on each swap, each a separate basis-point rate capped at 1000 (10%):| Fee | Set by | Read with |
|---|---|---|
| Protocol | Flashnet, global | protocolFeeBps() |
| Host | Pool host, per pool | hostFeeBps(pool) |
| Integrator | You, per call | integratorBps argument |
claim(address token). quote() returns the combined rate as conductorFeeBps and reports amountOut already net of it.
Custom errors
A reverted swap puts the selector in the intent’sstatusMessage. decodeRevertReason maps it back to the error name using the built-in CONDUCTOR_REVERT_ERRORS table:
SlippageExceeded(): output fell belowminAmountOut.DeadlineExpired(uint256): submitted afterdeadline.InvalidIntegrator()/FeeRateTooHigh(uint16,uint16):integratorBpsover 1000, or a non-zero bps with a zero recipient.ZeroAmountIn(): a BTC swap sent with novalue.
Error(string) rather than a Conductor selector. TokensNotSorted(), WBTCNotInPair(), and InsufficientBTCValue() are pool-creation and liquidity errors, not swap errors.