Track Butter Network Settlement
Resume status checks from a source transaction hash, bound polling, and investigate partial execution without duplicate submissions.
Community modules are developed and maintained independently by third-party contributors.
Tether and the WDK Team do not endorse or assume responsibility for their code, security, or maintenance. Use your own judgment and proceed at your own risk.
Track an existing operation by retaining its identifiers, connecting a status client, and polling with a limit. Then interpret the status and investigate partial execution before deciding whether further action is needed. For support, see Need Help?.
Retain the operation identifiers
This guide uses the source snapshot and runtime prerequisites for source 0.2.0 at revision c1f373d. It does not apply to the older npm 0.1.0 API.
Before leaving the execution flow, retain:
- The source transaction hash returned as both
result.idandresult.hashbyswidge(). - The source and destination chain IDs from the submitted intent.
- Every transaction hash and role in
result.transactionsorButterPartialExecutionError.transactions.
result.id is a source transaction hash, not a Butter order ID. Pass it to getSwidgeStatus() with the chain context. Set byOrderId: true only when you independently obtained a Butter order ID; this module does not return one.
A saved routeHash cannot restore an executable quote after a restart. Status lookup uses the submitted transaction hash instead.
Connect a status client
The following example resumes an operation whose source chain was Ethereum. Copy SOURCE_TRANSACTION_HASH and DESTINATION_CHAIN_ID from your saved operation record. Set RPC_URL and your Butter-issued BUTTER_ENTRANCE. Use the EVM dependencies installed by the execution guide.
- Validate the source hash and check the RPC's chain ID.
- Create an accountless
ButterSwidgeProtocolwith the original source chain. - Adapt the read client with
toEvmPublicClient(), which supplies transaction and receipt lookups.
Save these JavaScript blocks, in order, in track-butter.mjs. This setup requires no signing key:
import ButterSwidgeProtocol, {
toEvmPublicClient,
} from '@butternetwork/wdk-protocol-swidge-butter'
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { setTimeout as delay } from 'node:timers/promises'
function requireEnv(name) {
const value = process.env[name]?.trim()
if (!value) throw new Error(`Set ${name}`)
return value
}
const sourceHash = requireEnv('SOURCE_TRANSACTION_HASH')
if (!/^0x[0-9a-fA-F]{64}$/.test(sourceHash)) {
throw new Error('Use the saved Ethereum source transaction hash')
}
const toChain = requireEnv('DESTINATION_CHAIN_ID')
const rpcUrl = requireEnv('RPC_URL')
try {
if (new URL(rpcUrl).protocol !== 'https:') throw new Error()
} catch {
throw new Error('Set a valid HTTPS RPC URL')
}
const publicClient = createPublicClient({
chain: mainnet,
transport: http(rpcUrl, { timeout: 10_000, retryCount: 0 }),
})
const chainId = await publicClient.getChainId().catch(() => {
throw new Error('RPC chain check failed; verify endpoint access without logging credentials')
})
if (chainId !== 1) throw new Error('RPC must use Ethereum chain 1')
const protocol = new ButterSwidgeProtocol(undefined, {
sourceChainId: '1',
entrance: requireEnv('BUTTER_ENTRANCE'),
evm: { publicClient: toEvmPublicClient(publicClient) },
})
const operation = Object.freeze({ sourceHash, fromChain: '1', toChain })For an authenticated integration, add the same server-side API credentials used during execution.
For a remembered same-chain operation, the existing instance can use its account or public receipt reader. A fresh EVM instance must also retrieve the transaction and attribute it to a configured Butter Router and recognized function. Chain hints do not bypass that check. Missing attribution produces an error; do not switch to order-ID mode to bypass it.
Cross-chain status comes from Butter's source-hash lookup. It remains distinct from same-chain receipt status. Preserve the original chain context when resuming either flow.
Poll with a limit
Bound the number of status requests and the period in which new polls can start. A local deadline stops polling; it does not cancel an on-chain transaction or establish failure.
- Query
getSwidgeStatus()with the retained source hash and chain context. - Stop for a terminal status or a state requiring investigation.
- On a lookup error or local limit, preserve the operation record and resume status checks later.
Add this bounded polling loop. It sends no transaction and makes no automatic execution retry:
const terminal = new Set(['completed', 'refunded', 'failed', 'cancelled', 'expired'])
const needsReview = new Set(['action-required', 'partial'])
const deadline = Date.now() + 180_000
let stopReason = 'Local polling limit reached; retain the operation for later checks'
for (let attempt = 0; attempt < 12 && Date.now() < deadline; attempt++) {
let result
try {
result = await protocol.getSwidgeStatus(operation.sourceHash, {
fromChain: operation.fromChain,
toChain: operation.toChain,
})
} catch {
stopReason = 'Status lookup failed; inspect RPC or provider access and resume later'
break
}
console.log('Status:', result.status)
if (result.transactions) console.log('Reported transactions:', result.transactions)
if (terminal.has(result.status)) {
stopReason = `Reported terminal status: ${result.status}`
break
}
if (needsReview.has(result.status)) {
stopReason = `Inspect the operation before further action: ${result.status}`
break
}
const remaining = deadline - Date.now()
if (attempt < 11 && remaining > 0) await delay(Math.min(10_000, remaining))
}
console.log(stopReason)
console.log('Retain the original operation record:', operation)Run the assembled file after setting the environment:
node track-butter.mjsAn in-flight lookup can finish after the local deadline. RPC and Butter request timeouts limit individual requests; configure them for your application's operating conditions. Keep the original operation record even if a lookup temporarily returns no record or an unknown status.
Interpret the status
| Status | Application action |
|---|---|
pending | Continue bounded status checks. An unknown provider state can map to pending; it does not prove that settlement is progressing. |
refund-pending | Continue bounded checks and retain the original operation. A pending refund is not a completed refund. |
completed | Verify the relevant transaction receipts and received assets before marking your own accounting complete. |
refunded | Inspect the refund transaction and asset balance. |
failed, cancelled, expired | Stop polling and reconcile source transactions and balances before creating another operation. |
action-required, partial | Pause automation and investigate the reported transactions and provider state. |
Same-chain status maps a successful receipt to completed, a reverted receipt to failed, and an unknown receipt state to pending. It does not establish a chosen number of finality confirmations. Cross-chain status maps Butter's response; it is not an independent destination-balance audit.
Investigate partial execution
ButterPartialExecutionError can include approval hashes, a source hash, and failedType. An approval hash is not the source swap hash and should be inspected through the source RPC, rather than passed as the swap identifier.
- Read the saved transaction list in submission order and inspect every receipt.
- Check the current ERC-20 allowance if an approval or reset was attempted.
- If a source transaction exists, resume
getSwidgeStatus()with that source hash. - If a broadcast is uncertain, reconcile wallet submission records and account nonces before requesting another quote.
A sender can broadcast and then fail without returning a usable hash. Therefore, an empty transaction list or an error outside the partial-execution class is not proof that nothing was sent. Approval timeouts and local polling limits do not undo transactions.
The module has no automatic refund, allowance-revocation, cancellation, or rollback method. Any follow-up transaction requires a separate decision based on the actual chain state. Do not place a fresh swidge() call in a polling or error handler.
Next Steps
API Reference
Review status options, execution results, and partial-error properties.
Configuration
Configure RPC readers, request timeouts, and approval confirmation.
Execute a Swidge
Review the confirmation and operation-recording flow.