Swap
Swaps are modeled as intents with tokenRequests. A token request describes
the output token and amount the orchestrator must deliver on the target chain.
For a plain swap where the user only receives the output token, send no
calldata:
calls: []Do not add a dummy token transfer, { data: '0x' }, or a self-transfer to make
the intent look like a transaction. Placeholder calldata changes the review UI
and can cause the quoter to reject the route as unsupported destination
calldata.
Install the SDK
npm install @rhinestone/1auth viemCreate client
import { OneAuthClient } from '@rhinestone/1auth'
export const client = new OneAuthClient({
providerUrl: 'https://passkey.1auth.app',
})Send a swap intent
import { resolveTokenAddress } from '@rhinestone/1auth'
import { parseUnits } from 'viem'
const usdcOnBase = resolveTokenAddress('USDC', 8453)
const result = await client.sendIntent({
accountAddress: '0x...',
targetChain: 8453,
calls: [],
tokenRequests: [{
token: usdcOnBase,
amount: parseUnits('0.1', 6),
}],
sourceAssets: ['ETH'],
})
if (result.success) {
console.log('Swap intent:', result.intentId)
}Done
The orchestrator routes the input asset into the requested output token.
Cross-chain swap
Use the same shape for cross-chain routes. Constrain the source chain when your source asset address only exists on one chain:
import { resolveTokenAddress } from '@rhinestone/1auth'
import { parseUnits } from 'viem'
const BASE_SEPOLIA = 84532
const ARB_SEPOLIA = 421614
const mUSDOnBaseSepolia = '0x2f6fdE5E2AeAB6335d8f978B4d8B2a9c1129AcFb'
const usdcOnArbitrumSepolia = resolveTokenAddress('USDC', ARB_SEPOLIA)
await client.sendIntent({
accountAddress,
targetChain: ARB_SEPOLIA,
calls: [],
tokenRequests: [{
token: usdcOnArbitrumSepolia,
amount: parseUnits('0.1', 6),
}],
sourceAssets: [mUSDOnBaseSepolia],
sourceChainId: BASE_SEPOLIA,
})The orchestrator finds the route, bridges if needed, and delivers the requested USDC on the target chain.
Token requests vs calls
Use this distinction when deciding what to send:
| Intent shape | Use it for | What to send |
|---|---|---|
| Plain swap | User receives an output token | sendIntent({ calls: [], tokenRequests }) |
| ERC20 payment | User sends tokens to a recipient | Real ERC20.transfer(...) calldata plus matching tokenRequests |
| Contract action requiring tokens | Contract consumes tokens during execution | Real app calldata plus matching tokenRequests |
tokenRequests answer "what token output must exist on the target chain?"
calls answer "what contract action should execute with that output?" Plain
swaps only need the first layer. Payments, deposits, approvals, and marketplace
actions need both layers.
Funding target-chain execution
When the delivered token must be consumed by a contract call, include the real call and the token request together:
import { encodeFunctionData, parseUnits } from 'viem'
import { resolveTokenAddress } from '@rhinestone/1auth'
const USDC_BASE = resolveTokenAddress('USDC', 8453)
const amount = parseUnits('100', 6)
const transferData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [recipientAddress, amount],
})
await client.sendIntent({
accountAddress,
targetChain: 8453,
calls: [{
to: USDC_BASE,
data: transferData,
label: 'Send USDC',
sublabel: '100 USDC',
}],
tokenRequests: [{
token: USDC_BASE,
amount,
}],
})Constraining source assets
If you omit sourceAssets, the orchestrator can choose from available balances.
Pass sourceAssets and optionally sourceChainId to restrict the input side:
await client.sendIntent({
accountAddress,
targetChain: 8453,
calls: [],
tokenRequests: [{
token: resolveTokenAddress('USDC', 8453),
amount: parseUnits('100', 6),
}],
sourceAssets: ['ETH', 'WETH'],
})Use token addresses when constraining to a specific source chain. Use symbols only when the token symbol is supported by the registry for the route you want.
Tracking status
sendIntent returns an intent id. Pass waitForHash: true when you need the
transaction hash before the promise resolves:
const result = await client.sendIntent({
accountAddress,
targetChain: 8453,
calls: [],
tokenRequests: [{
token: resolveTokenAddress('USDC', 8453),
amount: parseUnits('0.1', 6),
}],
waitForHash: true,
closeOn: 'completed',
})
if (result.success) {
console.log(result.intentId)
console.log(result.transactionHash)
}You can also poll later with getIntentStatus(result.intentId).
Next steps
- Token Requests - Request specific token outputs
- Batch Transactions - Queue multiple operations