Bako Safe SDK
Transactions
Status & Monitoring

Transaction Status & Monitoring

Track and monitor transaction status throughout its lifecycle.

TransactionStatus Enum

enum TransactionStatus {
  AWAIT_REQUIREMENTS = 'await_requirements',
  PENDING_SENDER = 'pending_sender',
  PROCESS_ON_CHAIN = 'process_on_chain',
  SUCCESS = 'success',
  DECLINED = 'declined',
  FAILED = 'failed',
  CANCELED = 'canceled'
}

Status Descriptions

StatusDescriptionNext Actions
AWAIT_REQUIREMENTSWaiting for signaturesSigners need to approve
PENDING_SENDERReady to executeCall vault.send()
PROCESS_ON_CHAINSubmitted to networkWait for confirmation
SUCCESSCompleted successfullyDone
DECLINEDRejected by signersCreate new transaction
FAILEDExecution failedCheck error, retry
CANCELEDManually canceledCreate new transaction

Checking Status

Single Check

const txData = await vault.transactionFromHash(hashTxId);
 
console.log('Status:', txData.status);
console.log('Signatures:', txData.witnesses.length, '/', txData.requiredSigners);

ITransactionResume Structure

interface ITransactionResume {
  hash: string;
  BakoSafeID: string;
  totalSigners: number;
  requiredSigners: number;
  predicate: {
    id: string;
    address: string;
  };
  outputs: ITransferAsset[];
  status: TransactionStatus;
  witnesses?: IWitnesses[];
  gasUsed?: string;
  sendTime?: Date;
  error?: string;
}

Polling for Updates

async function pollStatus(
  vault: Vault,
  hashTxId: string,
  onUpdate: (status: TransactionStatus, data: any) => void
): Promise<TransactionStatus> {
  const POLL_INTERVAL = 5000; // 5 seconds
  const TIMEOUT = 300000; // 5 minutes
 
  const startTime = Date.now();
 
  while (Date.now() - startTime < TIMEOUT) {
    const txData = await vault.transactionFromHash(hashTxId);
    onUpdate(txData.status, txData);
 
    // Terminal states
    if (['success', 'failed', 'declined', 'canceled'].includes(txData.status)) {
      return txData.status as TransactionStatus;
    }
 
    await new Promise(r => setTimeout(r, POLL_INTERVAL));
  }
 
  throw new Error('Polling timeout');
}
 
// Usage
const finalStatus = await pollStatus(vault, hashTxId, (status, data) => {
  console.log(`Status: ${status}`);
  if (status === 'await_requirements') {
    const signed = data.witnesses.filter(w => w.status === 'done').length;
    console.log(`Signatures: ${signed}/${data.requiredSigners}`);
  }
});

Status Handlers

import { TransactionStatus } from 'bakosafe';
 
function handleStatus(txData: ITransactionResume) {
  switch (txData.status) {
    case TransactionStatus.AWAIT_REQUIREMENTS:
      handleAwaitingSignatures(txData);
      break;
 
    case TransactionStatus.PENDING_SENDER:
      handleReadyToSend(txData);
      break;
 
    case TransactionStatus.PROCESS_ON_CHAIN:
      handleProcessing(txData);
      break;
 
    case TransactionStatus.SUCCESS:
      handleSuccess(txData);
      break;
 
    case TransactionStatus.FAILED:
      handleFailure(txData);
      break;
 
    case TransactionStatus.DECLINED:
      handleDeclined(txData);
      break;
 
    case TransactionStatus.CANCELED:
      handleCanceled(txData);
      break;
  }
}
 
function handleAwaitingSignatures(txData) {
  const signed = txData.witnesses.filter(w => w.status === 'done').length;
  const pending = txData.requiredSigners - signed;
  console.log(`Waiting for ${pending} more signature(s)`);
 
  // List pending signers
  const signedAddresses = txData.witnesses
    .filter(w => w.status === 'done')
    .map(w => w.account);
 
  console.log('Signed by:', signedAddresses);
}
 
function handleReadyToSend(txData) {
  console.log('All signatures collected!');
  console.log('Transaction ready to execute');
}
 
function handleProcessing(txData) {
  console.log('Transaction submitted to network');
  console.log('Waiting for confirmation...');
}
 
function handleSuccess(txData) {
  console.log('Transaction completed successfully!');
  console.log('Gas used:', txData.gasUsed);
  console.log('Time:', txData.sendTime);
}
 
function handleFailure(txData) {
  console.error('Transaction failed:', txData.error);
}
 
function handleDeclined(txData) {
  console.log('Transaction was declined by signers');
}
 
function handleCanceled(txData) {
  console.log('Transaction was canceled');
}

Listing Transactions

Query multiple transactions:

interface IListTransactions {
  to?: string;              // Filter by recipient
  hash?: string;            // Filter by hash
  status?: TransactionStatus[];  // Filter by status
  perPage?: number;         // Pagination
  page?: number;
  orderBy?: string;
  sort?: SortOption;
}
 
enum SortOption {
  ASC = 'ASC',
  DESC = 'DESC'
}
 
// Example: Get pending transactions
const pendingTxs = await provider.listTransactions({
  status: [TransactionStatus.AWAIT_REQUIREMENTS],
  sort: SortOption.DESC,
  perPage: 10
});

Building a Status Dashboard

async function getVaultDashboard(vault: Vault) {
  const [
    pendingTxs,
    recentSuccess,
    recentFailed
  ] = await Promise.all([
    provider.listTransactions({
      status: [
        TransactionStatus.AWAIT_REQUIREMENTS,
        TransactionStatus.PENDING_SENDER
      ]
    }),
    provider.listTransactions({
      status: [TransactionStatus.SUCCESS],
      perPage: 5,
      sort: SortOption.DESC
    }),
    provider.listTransactions({
      status: [TransactionStatus.FAILED],
      perPage: 5,
      sort: SortOption.DESC
    })
  ]);
 
  return {
    pendingCount: pendingTxs.length,
    pendingTransactions: pendingTxs,
    recentSuccessful: recentSuccess,
    recentFailed: recentFailed
  };
}

Next Steps