Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Swap – 1auth
Skip to content

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 viem

Create client

client.ts
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 shapeUse it forWhat to send
Plain swapUser receives an output tokensendIntent({ calls: [], tokenRequests })
ERC20 paymentUser sends tokens to a recipientReal ERC20.transfer(...) calldata plus matching tokenRequests
Contract action requiring tokensContract consumes tokens during executionReal 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