The whole pipeline, in one call
Encode, sign, broadcast, and wait for the indexer to confirm. One call, not four services.
const { XChainSDK } = require(xchain-sdk);
// A network string is the whole config. Regtest resolves to localhost.
const sdk = new XChainSDK({ network: bitcoin-regtest });
const tx = await sdk.submitAction(
{ action: SEND, params: { tick: MYTOKEN, amount: 100, destination: bcrt1q... } },
{ pubkey: 02abc123... },
{ wif: cREGTEST_PLACEHOLDER_KEY },
);
console.log(tx.txid); // broadcast
console.log(tx.indexed); // and confirmed by the indexer
Bind a key once, then just act
A session carries the address, key, and UTXO state, so repeated actions from one address stop repeating themselves.
const session = sdk.session(cREGTEST_PLACEHOLDER_KEY);
await session.send({ tick: MYTOKEN, amount: 50, destination: bcrt1qaddr1... });
await session.send({ tick: MYTOKEN, amount: 50, destination: bcrt1qaddr2... });
// Back-to-back sends do not double-spend: the session chains UTXOs
// in memory rather than re-querying a stale set.
const balances = await session.getBalances();
Many actions, one transaction
The fluent batch builder packs several actions into a single on-chain transaction, so you pay one fee instead of several.
// build() is async: it resolves tickers and picks formats before encoding.
const batch = await sdk.batch()
.send({ tick: MYTOKEN, amount: 10, destination: bcrt1qaddr1... })
.mint({ tick: MYTOKEN, amount: 500 })
.build();
Wait for confirmation without polling
Event-driven, so you are not writing a retry loop around an explorer endpoint.
const indexed = await sdk.waitForAction(txid);
console.log(indexed.status);
Subscribe to the chain
Blocks, actions, and per-address activity over one WebSocket. No polling loop to write or tune.
sdk.onBlock((block) => console.log(new block, block.height));
sdk.onAction((action) => console.log(action.action, action.tick));
sdk.onAddress(bcrt1q..., (evt) => console.log(activity, evt));
Coordinate across two chains
One helper over two SDK instances. No bridge, no wrapped coin: the swap settles natively on both chains.
const { CrossChainHelper } = require(xchain-sdk);
const cross = new CrossChainHelper({
BTC: new XChainSDK({ network: bitcoin-regtest }),
LTC: new XChainSDK({ network: litecoin-regtest }),
});
await cross.createSwap({
giveCoin: BTC, giveTick: MYTOKEN, giveAmount: 100,
getCoin: LTC, getTick: THEIRTOKEN, getAmount: 250,
wif: cREGTEST_PLACEHOLDER_KEY,
});
Give an agent a bounded wallet
A policy block an AI agent cannot argue its way past: out-of-policy actions are refused before anything is signed.
const agent = sdk.agentSession(cREGTEST_PLACEHOLDER_KEY, {
allowedActions: [SEND, EXECUTE],
maxPerAction: { SEND: { MYTOKEN: 100 } },
maxPerWindow: { hours: 24, perTick: { MYTOKEN: 500 }, maxActions: 50 },
});
await agent.send({ tick: MYTOKEN, amount: 5, destination: bcrt1q... });
// Honest about what this is: a client-side guardrail, not a security
// boundary. Whoever holds the key can bypass it with raw SDK calls.
// Hard enforcement is the MuSig2 co-signer, which withholds its
// partial signature on an out-of-policy transaction.
Recipes for the multi-step things
Issue a token and distribute it in one call, instead of orchestrating ISSUE then a fan of SENDs yourself.
await sdk.issueAndDistribute(cREGTEST_PLACEHOLDER_KEY,
{ tick: NEWTOKEN, maxSupply: 1000000, decimals: 8 },
[
{ destination: bcrt1qaddr1..., amount: 500000 },
{ destination: bcrt1qaddr2..., amount: 300000 },
]);