Skip to main content

Manual Transactions

Use the txs*() methods when your app needs to sign and broadcast smart-chain transactions outside the SDK's built-in signer flow. This is useful for manual signing flows (e.g. hardware wallets or custom custody solutions) or apps where transaction approval is handled by another application layer.

How Manual Execution Works

Each action method has a transaction-producing counterpart and a matching wait method:

ActionManual transaction methodWait after broadcasting
commit()txsCommit()waitTillCommited()
claim()txsClaim()waitTillClaimed()
refund()txsRefund()waitTillRefunded()
commitAndClaim()txsCommitAndClaim()waitTillClaimed()

The manual transaction flow is always the same:

  1. Create the swap quote normally with swapper.swap(...).
  2. Call the relevant txs*() method to get the smart-chain transactions.
  3. Sign and broadcast the returned transactions with your external wallet flow.
  4. Call the matching waitTill*() method so the SDK can observe the result, update storage, and emit swap state changes.
// 1. Get the smart-chain transactions for the next action
const txsCommit = await swap.txsCommit();

// 2. Sign and broadcast them with your own wallet flow
...

// 3. Let the SDK observe the result and update swap state
await swap.waitTillCommited();
info

txs*() only replaces the smart-chain signing and broadcasting step. Bitcoin-side and Lightning-side steps such as waitForBitcoinTransaction(), getFundedPsbt(), or waitForPayment() stay the same as in the regular swap tutorials.

Sending Transactions by Chain

The returned transaction shape depends on the smart chain. The same pattern applies whether you are calling txsCommit(), txsClaim(), txsRefund(), or txsCommitAndClaim().

For Solana, each returned SolanaTx contains a tx and any additional signers that the SDK needs to attach before your wallet signs.

import {Connection} from "@solana/web3.js";

const connection = new Connection(rpcUrl);

const txsCommit = await swap.txsCommit();

// Sign helper keypairs required by the SDK first
txsCommit.forEach(({tx, signers}) => {
if (signers.length > 0) {
tx.sign(...signers);
}
});

// Then let the external wallet sign the transactions
const signedTransactions = await wallet.signAllTransactions(
txsCommit.map(({tx}) => tx)
);

// IMPORTANT: Broadcast sequentially
for (const tx of signedTransactions) {
const signature = await connection.sendRawTransaction(tx.serialize());
// Wait for confirmation before sending the next transaction
await connection.confirmTransaction(signature);
}

// Make sure you wait till the SDK processes the transaction
await swap.waitTillCommited();
warning

Ensure that you broadcast the transactions sequentially, always waiting for the previous one to confirm before sending the next one. Otherwise, you might expose yourself to the risk of losing funds especially during HTLC swaps where the pre-image must only be revealed after the HTLC is initiated.

Skip Checks for Fresh Quotes

If you call txsCommit() immediately after a fresh quote is created, you can skip the redundant init-signature checks:

const txsCommit = await swap.txsCommit(true);

Use skipChecks=true only when you are executing the quote right away. Do not reuse it for older swaps that may have already expired or changed state.

Common Mistakes

  • forgetting to call the matching waitTill*() method after broadcasting transactions.
  • sending returned transactions out of order when the swap flow depends on sequential confirmation.
  • treating txs*() as a replacement for Bitcoin or Lightning payment steps. It only replaces the smart-chain side.
  • using skipChecks=true on stale quotes.

API Reference

Next Steps

Swap Tutorials

Use the route-specific swap guides to see where each manual transaction step fits into the full swap flow.

Swap Tutorials →


Claiming and Refunds

Manual transaction signing is commonly paired with recovery flows for past swaps.

Swap Management →