chaingate
Version:
Multi-chain cryptocurrency SDK for TypeScript — unified API for Bitcoin, Ethereum, Litecoin, Dogecoin, Bitcoin Cash, Polygon, Arbitrum, and any EVM-compatible chain. Create wallets, query balances, send transactions, and manage tokens and NFTs across UTXO
123 lines (122 loc) • 4.47 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BroadcastedEvmRpcTransaction = void 0;
/** Default polling interval for confirmation checks (in milliseconds). */
const DEFAULT_POLL_INTERVAL_MS = 4000;
/**
* Represents a transaction that has been signed and broadcast to an EVM
* network via a direct JSON-RPC connection.
*
* Use {@link onConfirmed} to register a callback that fires when the
* transaction is confirmed on-chain. If the transaction is already confirmed
* at the time of registration, the callback fires immediately.
*
* `onConfirmed` returns a `cancel` function to stop polling.
*
* @example
* ```ts
* const broadcasted = await tx.signAndBroadcast();
* console.log('TX sent:', broadcasted.transactionId);
*
* const cancel = broadcasted.onConfirmed((details) => {
* console.log('Confirmed in block', details.blockHeight);
* console.log('Status:', details.status);
* });
*
* // Stop waiting for confirmation at any time:
* cancel();
* ```
*/
class BroadcastedEvmRpcTransaction {
/** @internal */
constructor(transactionId, explorer) {
this.confirmationDetails = null;
this.polling = false;
this.cancelled = false;
this.callbacks = [];
this.transactionId = transactionId;
this.explorer = explorer;
}
/**
* Registers a callback to be invoked when the transaction is confirmed.
*
* - If the transaction is already confirmed, the callback fires immediately
* (asynchronously, in the next microtask).
* - Multiple callbacks can be registered; they all fire once on confirmation.
* - Polling starts automatically on the first call and stops after confirmation
* or when the returned `cancel` function is called.
*
* @param callback - Function invoked with confirmation details.
* @returns A function that, when called, removes this callback and stops
* polling if no other callbacks remain.
*/
onConfirmed(callback) {
// Already confirmed — fire immediately, return a no-op cancel.
if (this.confirmationDetails) {
Promise.resolve().then(() => callback(this.confirmationDetails));
return () => { };
}
this.callbacks.push(callback);
if (!this.polling && !this.cancelled) {
this.polling = true;
this.cancelled = false;
this.poll();
}
// Return a cancel function scoped to this specific callback.
let removed = false;
return () => {
if (removed)
return;
removed = true;
const idx = this.callbacks.indexOf(callback);
if (idx !== -1)
this.callbacks.splice(idx, 1);
// If no callbacks remain, stop polling.
if (this.callbacks.length === 0) {
this.cancelled = true;
this.polling = false;
}
};
}
/** Polls the RPC node for transaction confirmation until confirmed or cancelled. */
async poll() {
while (!this.cancelled) {
try {
const receipt = await this.explorer.getTransactionReceipt(this.transactionId);
if (receipt) {
this.confirmationDetails = {
transactionId: this.transactionId,
blockHeight: receipt.blockNumber,
status: receipt.status === 1 ? 'success' : 'reverted',
};
this.fireCallbacks();
return;
}
}
catch {
// Transaction may not be indexed yet — keep polling.
}
if (this.cancelled)
return;
await sleep(DEFAULT_POLL_INTERVAL_MS);
}
}
/** Invokes all registered callbacks and clears the list. */
fireCallbacks() {
const details = this.confirmationDetails;
for (const cb of this.callbacks) {
try {
cb(details);
}
catch {
// Swallow errors from user callbacks to avoid breaking other listeners.
}
}
this.callbacks = [];
this.polling = false;
}
}
exports.BroadcastedEvmRpcTransaction = BroadcastedEvmRpcTransaction;
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}