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

Advanced Transfers

Start with the single-flow approach in Send a Transaction. The features on this page are useful when your product needs batch payments, separate confirmation stages, or recovery after an interruption.

Multiple recipients

Pass transfers instead of the single-recipient fields. The two input forms are mutually exclusive.

const prepared = await prepareTransfer({
  transfers: [
    { tokenAddress: USDC, recipientAddress: ALICE, amount: parseUnits("100", 6) },
    { tokenAddress: USDC, recipientAddress: BOB, amount: parseUnits("250", 6) },
  ],
  walletClient,
  publicClient,
  network: networks.ethereum,
});

Every transfer uses an EscrowBatch, including a transaction with one recipient. Row order is preserved while the SDK groups compatible rows for execution.

Staged execution

Use staged execution when your interface needs separate approval and deployment actions. Otherwise, prefer execute().

const approvals = prepared.approve(walletClient);
let checkpoint;
 
while (true) {
  const next = await approvals.next();
  if (next.done) {
    checkpoint = next.value;
    break;
  }
  console.log(next.value.hash);
}
 
const deployed = await prepared.deploy(walletClient, checkpoint);
await saveSecrets(deployed.secrets);
 
for await (const event of prepared.complete(walletClient, deployed.secrets)) {
  console.log(event.step);
}

ApprovalCheckpoint and TransferSecrets are the serializable boundaries between stages. Persist the secrets before rendering the next action because a reload between stages is an expected product state.

Updating a quote

Before approval begins, you can refresh pricing or replace recipients and amounts.

const fresh = await prepared.refreshFees();
const updated = await prepared.updateTransfers(newRows);

Both methods throw INVALID_STAGE after an approval is broadcast, a checkpoint exists, or the escrow is deployed. At that point the operation is bound to its quote.

Canceling an in-progress operation

Pass an AbortSignal when preparing the transaction.

const controller = new AbortController();
 
const prepared = await prepareTransfer({
  // transaction parameters
  abortSignal: controller.signal,
});
 
controller.abort();

Before any wallet transaction, aborting only stops the operation. After escrow deployment, TransferAbortedError includes the escrow address and the saved secrets determine the available recovery path.

Resuming after interruption

Recreate the prepared transfer with the same parameters and the saved secrets.

const prepared = await prepareTransfer({
  // same transaction parameters
  resume: savedTransferSecrets,
});
 
for await (const event of prepared.execute(walletClient)) {
  console.log(event.step);
}

The SDK resumes at the appropriate post-deployment stage. It requires the saved blinding scalar and sealed pricing authorization.

For recovery branches and typed failures, continue to Error Handling.