Methods

Find Routes

findRoutes(intent)

Returns available routes to fulfill the intent.

Parameters

1

intent

Defines the transfer details: origin, destination, amount, sender/recipient addresses, and optional gas drop-off.

const routes = await stable.findRoutes({
  sourceChain: string,
  targetChain: string,
  sender: string | EvmAddress,
  amount: string | bigint | Amount,
  recipient: string, // 0x-prefixed address
  gasDropoffDesired?: bigint,  // Optional. Native gas token amount to include on the destination chain
  paymentToken?: "usdc" | "native",
  usePermit?: true, // Optional. Defaults to true
});

// You can also use utility classes like Amount and EvmAddress
const routes = await stable.findRoutes({
  sender: new EvmAddress("0x..."), // or just a string
  amount: usdc(0.5),               // or string "0.5" or bigint in atomic units
  ...
});

// Access different route options
console.log("Fastest route:", routes.fastest);
console.log("Cheapest route:", routes.cheapest);
console.log("All routes:", routes.all);

Returns

A routes object containing:

  • all: list of route objects

  • fastest: reference to the fastest route

  • cheapest: reference to the cheapest route

Each route includes:

  • intent: original intent used to compute the route

  • estimatedDuration: number (in seconds)

  • estimatedTotalCost: cost in USD

  • fees: detailed fee breakdown

  • corridor: route type (e.g., "v1", "v2Direct", "avaxHop")

  • steps: transaction actions (e.g., sign-and-send-transaction, sign-message, sign-permit)

  • requiresMessageSignature: whether the route uses permit (vs approval)

  • transactionListener: emits blockchain tx-level events

  • progress: emits high-level transfer lifecycle events

  • workflow: async generator for manual execution flows

Example return
{
  all: [
    {
      intent: {
        sourceChain: "Ethereum",
        targetChain: "Arbitrum",
        amount: "0.01",
        sender: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
        recipient: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
      },
      estimatedDuration: 66,
      fees: [
        {
          amount: {
            n: 26541n,
            d: 400000n,
          },
          kind: {
            name: "Usdc",
            units: [
              {
                symbol: "USDC",
                scale: 1,
              },
              {
                symbol: "µUSDC",
                scale: {
                  n: 1n,
                  d: 1000000n,
                },
              },
            ],
            human: "USDC",
            atomic: "µUSDC",
            stringify: function (val) {
              return val.ge(oom(-2))
                  ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                  : `${val.div(oom(-6)).toString()} µUSDC`;
            },
          },
        },
      ],
      corridor: "v1",
      requiresMessageSignature: false,
      steps: [
        {
          platform: "Evm",
          chain: "Ethereum",
          type: "transfer",
          gasCostEstimation: 120000n,
        },
      ],
      transactionListener: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      progress: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      estimatedTotalCost: {
        amount: {
          n: 163205967n,
          d: 50000000n,
        },
        kind: {
          name: "Usd",
          units: [
            {
              symbol: "$",
              scale: 1n,
            },
            {
              symbol: "¢",
              scale: {
                n: 1n,
                d: 100n,
              },
            },
          ],
          human: "$",
          atomic: "¢",
          stringify: function (val) {
            return val.ge(1)
                ? `$${val.eq(val.floor()) ? val.toString() : val.toFixed(2)}`
                : `${val.mul(100).toString()}¢`;
          },
        },
      },
      workflow: {
      },
    },
    {
      intent: {
        sourceChain: "Ethereum",
        targetChain: "Arbitrum",
        amount: "0.01",
        sender: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
        recipient: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
      },
      estimatedDuration: 66,
      fees: [
        {
          amount: {
            n: 26541n,
            d: 400000n,
          },
          kind: {
            name: "Usdc",
            units: [
              {
                symbol: "USDC",
                scale: 1,
              },
              {
                symbol: "µUSDC",
                scale: {
                  n: 1n,
                  d: 1000000n,
                },
              },
            ],
            human: "USDC",
            atomic: "µUSDC",
            stringify: function (val) {
              return val.ge(oom(-2))
                  ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                  : `${val.div(oom(-6)).toString()} µUSDC`;
            },
          },
        },
      ],
      corridor: "v1",
      requiresMessageSignature: true,
      steps: [
        {
          platform: "Evm",
          type: "sign-permit",
          chain: "Ethereum",
          gasCostEstimation: 0n,
        },
        {
          platform: "Evm",
          chain: "Ethereum",
          type: "transfer",
          gasCostEstimation: 120000n,
        },
      ],
      estimatedTotalCost: {
        amount: {
          n: 163205967n,
          d: 50000000n,
        },
        kind: {
          name: "Usd",
          units: [
            {
              symbol: "$",
              scale: 1n,
            },
            {
              symbol: "¢",
              scale: {
                n: 1n,
                d: 100n,
              },
            },
          ],
          human: "$",
          atomic: "¢",
          stringify: function (val) {
            return val.ge(1)
                ? `$${val.eq(val.floor()) ? val.toString() : val.toFixed(2)}`
                : `${val.mul(100).toString()}¢`;
          },
        },
      },
      transactionListener: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      progress: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      workflow: {
      },
    },
    {
      intent: {
        sourceChain: "Ethereum",
        targetChain: "Arbitrum",
        amount: "0.01",
        sender: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
        recipient: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
      },
      estimatedDuration: 14,
      fees: [
        {
          amount: {
            n: 35187n,
            d: 500000n,
          },
          kind: {
            name: "Usdc",
            units: [
              {
                symbol: "USDC",
                scale: 1,
              },
              {
                symbol: "µUSDC",
                scale: {
                  n: 1n,
                  d: 1000000n,
                },
              },
            ],
            human: "USDC",
            atomic: "µUSDC",
            stringify: function (val) {
              return val.ge(oom(-2))
                  ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                  : `${val.div(oom(-6)).toString()} µUSDC`;
            },
          },
        },
        {
          amount: {
            n: 1n,
            d: 1000000n,
          },
          kind: {
            name: "Usdc",
            units: [
              {
                symbol: "USDC",
                scale: 1,
              },
              {
                symbol: "µUSDC",
                scale: {
                  n: 1n,
                  d: 1000000n,
                },
              },
            ],
            human: "USDC",
            atomic: "µUSDC",
            stringify: function (val) {
              return val.ge(oom(-2))
                  ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                  : `${val.div(oom(-6)).toString()} µUSDC`;
            },
          },
        },
      ],
      corridor: "v2Direct",
      requiresMessageSignature: false,
      steps: [
        {
          platform: "Evm",
          chain: "Ethereum",
          type: "transfer",
          gasCostEstimation: 200000n,
        },
      ],
      transactionListener: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      progress: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      estimatedTotalCost: {
        amount: {
          n: 6749983n,
          d: 1250000n,
        },
        kind: {
          name: "Usd",
          units: [
            {
              symbol: "$",
              scale: 1n,
            },
            {
              symbol: "¢",
              scale: {
                n: 1n,
                d: 100n,
              },
            },
          ],
          human: "$",
          atomic: "¢",
          stringify: function (val) {
            return val.ge(1)
                ? `$${val.eq(val.floor()) ? val.toString() : val.toFixed(2)}`
                : `${val.mul(100).toString()}¢`;
          },
        },
      },
      workflow: {
      },
    },
    {
      intent: {
        sourceChain: "Ethereum",
        targetChain: "Arbitrum",
        amount: "0.01",
        sender: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
        recipient: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
      },
      estimatedDuration: 14,
      fees: [
        {
          amount: {
            n: 35187n,
            d: 500000n,
          },
          kind: {
            name: "Usdc",
            units: [
              {
                symbol: "USDC",
                scale: 1,
              },
              {
                symbol: "µUSDC",
                scale: {
                  n: 1n,
                  d: 1000000n,
                },
              },
            ],
            human: "USDC",
            atomic: "µUSDC",
            stringify: function (val) {
              return val.ge(oom(-2))
                  ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                  : `${val.div(oom(-6)).toString()} µUSDC`;
            },
          },
        },
        {
          amount: {
            n: 1n,
            d: 1000000n,
          },
          kind: {
            name: "Usdc",
            units: [
              {
                symbol: "USDC",
                scale: 1,
              },
              {
                symbol: "µUSDC",
                scale: {
                  n: 1n,
                  d: 1000000n,
                },
              },
            ],
            human: "USDC",
            atomic: "µUSDC",
            stringify: function (val) {
              return val.ge(oom(-2))
                  ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                  : `${val.div(oom(-6)).toString()} µUSDC`;
            },
          },
        },
      ],
      corridor: "v2Direct",
      requiresMessageSignature: true,
      steps: [
        {
          platform: "Evm",
          type: "sign-permit",
          chain: "Ethereum",
          gasCostEstimation: 0n,
        },
        {
          platform: "Evm",
          chain: "Ethereum",
          type: "transfer",
          gasCostEstimation: 200000n,
        },
      ],
      estimatedTotalCost: {
        amount: {
          n: 6749983n,
          d: 1250000n,
        },
        kind: {
          name: "Usd",
          units: [
            {
              symbol: "$",
              scale: 1n,
            },
            {
              symbol: "¢",
              scale: {
                n: 1n,
                d: 100n,
              },
            },
          ],
          human: "$",
          atomic: "¢",
          stringify: function (val) {
            return val.ge(1)
                ? `$${val.eq(val.floor()) ? val.toString() : val.toFixed(2)}`
                : `${val.mul(100).toString()}¢`;
          },
        },
      },
      transactionListener: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      progress: {
        _events: {
        },
        _eventsCount: 0,
        _maxListeners: undefined,
      },
      workflow: {
      },
    },
  ],
  fastest: {
    intent: {
      sourceChain: "Ethereum",
      targetChain: "Arbitrum",
      amount: "0.01",
      sender: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
      recipient: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
    },
    estimatedDuration: 14,
    fees: [
      {
        amount: {
          n: 35187n,
          d: 500000n,
        },
        kind: {
          name: "Usdc",
          units: [
            {
              symbol: "USDC",
              scale: 1,
            },
            {
              symbol: "µUSDC",
              scale: {
                n: 1n,
                d: 1000000n,
              },
            },
          ],
          human: "USDC",
          atomic: "µUSDC",
          stringify: function (val) {
            return val.ge(oom(-2))
                ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                : `${val.div(oom(-6)).toString()} µUSDC`;
          },
        },
      },
      {
        amount: {
          n: 1n,
          d: 1000000n,
        },
        kind: {
          name: "Usdc",
          units: [
            {
              symbol: "USDC",
              scale: 1,
            },
            {
              symbol: "µUSDC",
              scale: {
                n: 1n,
                d: 1000000n,
              },
            },
          ],
          human: "USDC",
          atomic: "µUSDC",
          stringify: function (val) {
            return val.ge(oom(-2))
                ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                : `${val.div(oom(-6)).toString()} µUSDC`;
          },
        },
      },
    ],
    corridor: "v2Direct",
    requiresMessageSignature: false,
    steps: [
      {
        platform: "Evm",
        chain: "Ethereum",
        type: "transfer",
        gasCostEstimation: 200000n,
      },
    ],
    transactionListener: {
      _events: {
      },
      _eventsCount: 0,
      _maxListeners: undefined,
    },
    progress: {
      _events: {
      },
      _eventsCount: 0,
      _maxListeners: undefined,
    },
    estimatedTotalCost: {
      amount: {
        n: 6749983n,
        d: 1250000n,
      },
      kind: {
        name: "Usd",
        units: [
          {
            symbol: "$",
            scale: 1n,
          },
          {
            symbol: "¢",
            scale: {
              n: 1n,
              d: 100n,
            },
          },
        ],
        human: "$",
        atomic: "¢",
        stringify: function (val) {
          return val.ge(1)
              ? `$${val.eq(val.floor()) ? val.toString() : val.toFixed(2)}`
              : `${val.mul(100).toString()}¢`;
        },
      },
    },
    workflow: {
    },
  },
  cheapest: {
    intent: {
      sourceChain: "Ethereum",
      targetChain: "Arbitrum",
      amount: "0.01",
      sender: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
      recipient: "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
    },
    estimatedDuration: 66,
    fees: [
      {
        amount: {
          n: 26541n,
          d: 400000n,
        },
        kind: {
          name: "Usdc",
          units: [
            {
              symbol: "USDC",
              scale: 1,
            },
            {
              symbol: "µUSDC",
              scale: {
                n: 1n,
                d: 1000000n,
              },
            },
          ],
          human: "USDC",
          atomic: "µUSDC",
          stringify: function (val) {
            return val.ge(oom(-2))
                ? `${val.eq(val.floor()) ? val.toString() : val.toFixed(2)} USDC`
                : `${val.div(oom(-6)).toString()} µUSDC`;
          },
        },
      },
    ],
    corridor: "v1",
    requiresMessageSignature: false,
    steps: [
      {
        platform: "Evm",
        chain: "Ethereum",
        type: "transfer",
        gasCostEstimation: 120000n,
      },
    ],
    transactionListener: {
      _events: {
      },
      _eventsCount: 0,
      _maxListeners: undefined,
    },
    progress: {
      _events: {
      },
      _eventsCount: 0,
      _maxListeners: undefined,
    },
    estimatedTotalCost: {
      amount: {
        n: 163205967n,
        d: 50000000n,
      },
      kind: {
        name: "Usd",
        units: [
          {
            symbol: "$",
            scale: 1n,
          },
          {
            symbol: "¢",
            scale: {
              n: 1n,
              d: 100n,
            },
          },
        ],
        human: "$",
        atomic: "¢",
        stringify: function (val) {
          return val.ge(1)
              ? `$${val.eq(val.floor()) ? val.toString() : val.toFixed(2)}`
              : `${val.mul(100).toString()}¢`;
        },
      },
    },
    workflow: {
    },
  },
}

Authorization Methods

Each route either uses:

  • Approval: Requires an on-chain approval transaction. Suitable for users who trust the CCTPR contract and interact frequently — often done outside the SDK for better gas optimization.

  • Permit: Requires a signed message. Preferable for users who interact less frequently or want to minimize trust assumptions. Slightly more secure for one-off interactions.

You can detect this via:

route.requiresMessageSignature // true = uses permit


Gasless Transfers

Allow users to pay transaction fees using USDC instead of native tokens like ETH or MATIC. No need to hold gas tokens to complete transfers.

const routes = await stable.findRoutes({
  sourceChain: "Ethereum",
  targetChain: "Polygon",
  amount: "100",
  sender: account.address,
  recipient: account.address,
  paymentToken: "usdc", // Automatically includes gasless routes
});

// Gasless routes handle all gas payments with USDC
const gaslessRoute = routes.all.find(route => 
  route.steps.some(step => step.type === "gasless-transfer")
);


Check if sender has enough funds

checkHasEnoughFunds(route)

Checks whether the sender has enough USDC/native tokens to cover all required fees for the given route.

Parameters

1

route

One of the objects returned from findRoutes.

Returns

Boolean: true if sufficient funds, otherwise false.

Example return
true


Execute route

executeRoute(route)

Executes the given route. Automatically handles pre-approvals or permits.

Parameters

1

route

One of the objects returned from findRoutes.

Returns

An object describing the full lifecycle of the transfer:

  • transferHash: hash of the transaction that originated the transfer on the source chain

  • redeemHash: hash of the transaction that redeemed the transfer on the target chain

  • transactions: array of transaction hashes signed and paid by the user (e.g. approval, transfer)

  • attestations: Circle attestations of the transfer (can include two if using Avax Hop)

  • redeems: redemption transaction data (can include two if using Avax Hop)

Example return
{
  "transferHash": "0x286957f21375a8ca64b7be32a2abfbd69263fea2d56b2926a023bcb4bb01e8cc",
  "redeemHash": "0x2ed285bc5dcec8123251896d2cd77a78414abc4eae7a9bd60868027cc6a50114",
  "transactions": [
    "0x286957f21375a8ca64b7be32a2abfbd69263fea2d56b2926a023bcb4bb01e8cc"
  ],
  "attestations": [
    {
      "cctpVersion": 2,
      "nonce": "0x776b5ec8fbf5c7178291ae48d2a61eb8b4b490668b16f5bdef9b01d937cb6128",
      "sender": "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
      "recipient": "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",
      "destinationCaller": "0x0000000000000000000000000000000000000000",
      "sourceDomain": "Ethereum",
      "targetDomain": "Arbitrum",
      "minFinalityThreshold": 500,
      "finalityThresholdExecuted": 1000,
      "messageBody": {
        "burnToken": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
        "mintRecipient": "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
        "amount": "0.01 USDC",
        "messageSender": "0xd60c838bcF9585B549f78bcf4903C950782DA13c",
        "maxFee": "1 µUSDC",
        "feeExecuted": "1 µUSDC",
        "expirationBlock": "8469774",
        "hookData": ""
      }
    }
  ],
  "redeems": [
    {
      "destinationDomain": "Arbitrum",
      "transactionHash": "0x2ed285bc5dcec8123251896d2cd77a78414abc4eae7a9bd60868027cc6a50114"
    }
  ]
}

executeRoute() resolves after the transfer is redeemed on the destination chain. To monitor transfer progress, use route.progress. To observe signed blockchain transactions, use route.transactionListener.


Transaction tracking

route.progress

route.progress emits events representing each step in the lifecycle of the transfer.

route.progress.on("transfer-initiated", (data) => {
  console.log("Transfer started:", data);
});

route.progress.on("transfer-confirmed", (data) => {
  console.log("Transfer confirmed on source chain:", data);
});

Lifecycle Events

1

message-signed

The user signed a message to authorize part of the transfer (e.g., permit or other message types).

Example
{
  "signature": "0x2861af67c9f3888ab7b336ec9908facb84400f67048c6850fa0ebaacc17dc10e4d185272f390448683aaa8a36ca9668466e76530b074fcf4872a18d88cfbab4f1b",
  "value": "76352",
  "deadline": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
}
2

approval-sent

The user signed and sent an approval transaction.

Example
{
  "transactionHash": "0x4925e8dc4925f656207bd4e4f167b0a0d22338f6b5cfabc137cf7a74849ac516",
  "approvalAmount": "80374"
}
3

transfer-initiated

The transfer process has started. This event signals that the SDK is ready to submit the transfer transaction. Useful for UI state changes (e.g., loading spinners).

4

transfer-sent

The user signed and sent the transfer transaction.

Example
{
  "transactionHash": "0xe276e736f17c5f65c7709ecf1f1048ca77ccfd01f84909a4339ae28f0d211b40",
  "approvalType": "Preapproval",
  "gasDropOff": "0",
  "usdcAmount": 0.01,
  "recipient": "0x000000000000000000000000245ba3fe701c0d70bd42f3951d08f3a59914a627",
  "quoted": "onChainUsdc"
}
5

transfer-confirmed

Circle attestation service confirmed the transfer (v1 or v2 format).

Example
{
  "cctpVersion": 1,
  "nonce": "315698",
  "sender": "0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5",
  "recipient": "0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5",
  "destinationCaller": "0x0000000000000000000000000000000000000000",
  "sourceDomain": "Ethereum",
  "targetDomain": "Optimism",
  "messageBody": {
    "burnToken": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
    "mintRecipient": "0x245bA3fE701C0D70BD42f3951d08F3A59914a627",
    "amount": "0.01 USDC",
    "messageSender": "0xd60c838bcF9585B549f78bcf4903C950782DA13c"
  }
}
6

hop-redeemed (If using Avax Hop)

The first transfer was redeemed on avalanche.

7

hop-confirmed (If using Avax Hop)

The second transfer was confirmed by circle

8

transfer-redeemed

The transfer was redeemed on the destination chain.

Example
{
  "destinationDomain": "Arbitrum",
  "transactionHash": "0x9f838028cf149b24b05302086734eec7fab8d05186e94241bb523707f3c4e1f8"
}
9

step-completed

Special event that fires on every step. The payload includes name and data.

Example
{
  "name": "permit-signed",
  "data": {
    "signature": "0x2861af67c9f3888ab7b336ec9908facb84400f67048c6850fa0ebaacc17dc10e4d185272f390448683aaa8a36ca9668466e76530b074fcf4872a18d88cfbab4f1b",
    "value": "76352",
    "deadline": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
  }
}

route.transactionListener

These events monitor wallet transactions, specifically those the user signs and pays for.

route.transactionListener.on("transaction-sent", (txData) => {
  console.log("Transaction sent:", txData.hash);
});

route.transactionListener.on("transaction-included", (receipt) => {
  console.log("Transaction included in block:", receipt.blockNumber);
});

Lifecycle Events

1

transaction-sent

The transaction was sent to the blockchain.

Example
{
  "transactionHash": "0x0d275e1745e84db0d07ddaf50bb7cbef36b91bec9310099577c05a755962dbfd",
  "parameters": {
    "to": "0xd60c838bcF9585B549f78bcf4903C950782DA13c",
    "data": "0x00000eb602000000000000271002000000000000000000000000245ba3fe701c0d70bd42f3951d08f3a59914a627000000000001000000000000029700",
    "value": "0",
    "gas": "10000000",
    "maxFeePerGas": "40000000000",
    "maxPriorityFeePerGas": "20000000000"
  }
}
2

transaction-included

The transaction was included in a block.

Example
{
  "blockHash": "0xc9c402370917c4420e657f98b7cc76a8834b3989a4cc958a8cef729b12978257",
  "blockNumber": "8462734",
  "contractAddress": null,
  "cumulativeGasUsed": "156250",
  "effectiveGasPrice": "20000000020",
  "from": "0x245ba3fe701c0d70bd42f3951d08f3a59914a627",
  "gasUsed": "156250",
  "logs": [
    {
      "address": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000245ba3fe701c0d70bd42f3951d08f3a59914a627",
        "0x00000000000000000000000053207e216540125e322cda8a693b0b89576deb46"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000000001ba",
      ...
    },
    ...
  ],
  "status": "success",
  "to": "0xd60c838bcf9585b549f78bcf4903c950782da13c",
  "transactionHash": "0x0d275e1745e84db0d07ddaf50bb7cbef36b91bec9310099577c05a755962dbfd",
  "transactionIndex": 0,
  "type": "eip1559"
}


Error Handling

Handle transfer errors using the progress event system:

route.progress.on("error", (errorData) => {
  switch (errorData.type) {
    case "transfer-failed":
      // Transaction failed, tokens remained in source account
      console.error("Transfer transaction failed");
      // errorData.details is undefined for transfer-failed
      break;
      
    case "attestation-failed":
      // Circle's attestation service issues
      console.error("Circle attestation failed for transaction:", errorData.details.txHash);
      // Retry logic could be implemented here
      break;
      
    case "receive-failed":
      // Destination chain receive transaction failed
      console.error("Destination receive failed for transaction:", errorData.details.txHash);
      // Manual intervention may be required
      break;
  }
});


Advanced: Custom Step Execution

Each route exposes an async generator via route.workflow. This allows advanced developers to handle signing and transaction submission step-by-step.

for await (const tx of route.workflow) {
  // tx is the next transaction to sign and send
  await tx.signAndSend();
}

Useful for advanced integrations that require granular control over the execution flow, including special handling of permit transactions.

Last updated

Was this helpful?