XChain Platform SDK — Transaction Lifecycle
The SDK provides a Transaction Lifecycle Manager that handles the complete flow of submitting an XChain action to the blockchain — from creation through to indexer confirmation — in a single call.
The Problem
Without the lifecycle manager, submitting an action requires multiple manual steps:
- Create the action string (
sdk.createAction()) - Encode it into a PSBT (
encoder.createTx()) - Sign the PSBT (
wallet.signPsbt()) - Broadcast the transaction (
encoder.broadcastTx()) - If P2SH/P2WSH encoding: execute phase 2 (
encoder.spendP2sh()→ sign → broadcast) - Wait for the indexer to process the transaction (poll
explorer.getTransaction())
The lifecycle manager wraps all of this into sdk.submitAction().
submitAction
const result = await sdk.submitAction(
{ action: 'SEND', params: { tick: 'MYTOKEN', amount: '100', destination: 'bc1q...' } },
{ pubkey: '02abc123...' },
{ wif: 'your-wif-key' }
);
console.log(result.txid); // final transaction hash
console.log(result.actionString); // 'SEND|0|MYTOKEN|100|bc1q...'
console.log(result.encoding); // 'OP_RETURN'
console.log(result.indexed); // action data from the indexer
Parameters
| Parameter | Type | Description |
|---|---|---|
actionData |
{ action, params } |
Action name and parameters (same shape as createAction, without encoder) |
encoderOpts |
object |
Encoder options: pubkey (required), change, utxos, encoding, fee, feePerKb, etc. |
opts |
object |
Lifecycle options (see below) |
Lifecycle Options (opts)
| Option | Type | Default | Description |
|---|---|---|---|
wif |
string |
(required) | WIF private key for signing |
waitForIndexer |
boolean |
true |
Wait for the indexer to process the transaction |
timeout |
number |
120000 |
Milliseconds to wait for indexer confirmation |
pollInterval |
number |
2000 |
Milliseconds between explorer poll attempts |
requireValid |
boolean |
true |
Reject if the indexed action has status 'invalid' |
onProgress |
function |
— | Callback for lifecycle step notifications |
Return Value
{
txid: 'abc123...', // final transaction hash (phase 2 for P2SH)
actionString: 'SEND|0|...', // serialized ACTION string
action: 'SEND', // ACTION name
version: 0, // protocol version used
encoding: 'OP_RETURN', // encoding type used
signed: { txHex, txid, psbtHex }, // signed transaction details
spentInputs: [{ txid, vout }], // UTXOs consumed
indexed: { ... } // action data from indexer (null if waitForIndexer=false)
}
Progress Callback
Track lifecycle steps in real-time:
await sdk.submitAction(actionData, encoderOpts, {
wif: myWIF,
onProgress: (step, data) => {
console.log(`Step: ${step}`, data);
}
});
Steps emitted: creating, encoding, signing, broadcasting, p2sh_spending (if P2SH), waiting, confirmed.
P2SH Two-Phase Handling
When the encoder selects P2SH or P2WSH encoding (for large action strings), submitAction automatically handles the two-transaction pattern:
- Phase 1: broadcast the funding transaction
- Phase 2: call
spendP2sh(), sign, and broadcast the spend transaction - The returned
txidis the phase 2 transaction (which the indexer looks for)
No special handling is needed by the caller.
Fee Estimation
Estimate fees before committing to a transaction:
const estimate = await sdk.estimateFees(
{ action: 'SEND', params: { tick: 'MYTOKEN', amount: '100', destination: 'bc1q...' } },
{ pubkey: '02abc123...' }
);
console.log(estimate.fee); // fee in satoshis
console.log(estimate.encoding); // encoding type that would be used
console.log(estimate.inputTotal); // total input value
console.log(estimate.outputTotal); // total output value
The returned psbt can be signed directly to avoid a second encode call:
const estimate = await sdk.estimateFees(actionData, encoderOpts);
if (estimate.fee < maxAcceptableFee) {
const signed = sdk.signPsbt(estimate.psbt, wif);
await sdk.broadcastTx(signed.txHex);
}
waitForAction
Wait for a specific transaction to be indexed, independently of submitAction:
// Wait for a transaction by hash
const action = await sdk.waitForAction('abc123...txhash', {
timeout: 60000,
pollInterval: 1000
});
console.log(action.action_index);
// Wait for a specific action_index
const action = await sdk.waitForActionIndex(12345);
Uses WebSocket events when the WebSocket client is connected, with polling fallback. Both strategies run simultaneously — whichever resolves first wins.
Related Documentation
- Wallet Sessions — bound wallet sessions that use
submitActioninternally - Workflows — multi-step recipes built on the lifecycle manager
- Encoder — encoding types, options, P2SH two-phase details
- Errors —
SDKActionErrorfor lifecycle failures
Copyright © 2025–2026 Dankest, LLC
Based on XChain Platform by Dankest, LLC – https://dankest.llc
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.
You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.