Skip to main content

Execution

The Mina SDK provides a comprehensive execution system for bridge transactions with step-by-step tracking and status callbacks.

Overview

After obtaining a quote, use the execute() method to perform the bridge transaction. The execution process handles:
  • Token approvals (if needed)
  • Swap transactions
  • Bridge transactions
  • Status monitoring
  • Real-time progress callbacks

Execute Method

execute(options)

Executes a bridge transaction with the provided quote and signer.
import { Mina } from '@siphoyawe/mina-sdk';

const mina = new Mina({ integrator: 'my-app' });

// First, get a quote
const quote = await mina.getQuote({
  fromChainId: 42161,
  toChainId: 999,
  fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
  toToken: '0xb88339cb7199b77e23db6e890353e22632ba630f',
  fromAmount: '100000000',
  fromAddress: '0x...',
});

// Then execute with callbacks
const result = await mina.execute({
  quote,
  signer: walletSigner, // Your wallet signer
  onStepChange: (step) => {
    console.log(`Step ${step.stepId}: ${step.status}`);
  },
  onStatusChange: (status) => {
    console.log(`Progress: ${status.progress}%`);
  },
});

if (result.status === 'completed') {
  console.log('Bridge complete! TxHash:', result.txHash);
}

Execute Options

ExecuteOptions

interface ExecuteOptions {
  /** Quote to execute */
  quote: Quote;
  /** Signer for transaction signing */
  signer: TransactionSigner;
  /** Callback for step status updates */
  onStepChange?: OnStepChange;
  /** Callback for overall status updates */
  onStatusChange?: OnStatusChange;
  /** Callback before approval transaction */
  onApprovalRequest?: () => void;
  /** Callback before main transaction */
  onTransactionRequest?: () => void;
  /** Allow infinite token approval */
  infiniteApproval?: boolean;
}

Transaction Signer

The SDK requires a signer that implements the TransactionSigner interface:
interface TransactionSigner {
  /** Sign and send a transaction */
  sendTransaction: (request: TransactionRequestData) => Promise<string>;
  /** Get the signer's address */
  getAddress: () => Promise<string>;
  /** Get the current chain ID */
  getChainId: () => Promise<number>;
}

interface TransactionRequestData {
  to: string;
  data: string;
  value: string;
  gasLimit?: string;
  gasPrice?: string;
  chainId: number;
}

Using with Viem/Wagmi

import { useWalletClient } from 'wagmi';
import { Mina, TransactionSigner } from '@siphoyawe/mina-sdk';

function createSigner(walletClient: WalletClient): TransactionSigner {
  return {
    async sendTransaction(request) {
      const hash = await walletClient.sendTransaction({
        to: request.to as `0x${string}`,
        data: request.data as `0x${string}`,
        value: BigInt(request.value),
        gas: request.gasLimit ? BigInt(request.gasLimit) : undefined,
        chain: walletClient.chain,
      });
      return hash;
    },
    async getAddress() {
      const [address] = await walletClient.getAddresses();
      return address;
    },
    async getChainId() {
      return walletClient.chain?.id ?? 1;
    },
  };
}

Step Types

Bridge execution consists of multiple step types:
Step TypeDescription
approveToken approval for spending
swapToken swap on source chain
bridgeCross-chain bridge transfer
depositDeposit to Hyperliquid L1

Status Callbacks

onStepChange

Called when individual step status changes:
interface StepStatusPayload {
  /** Step identifier */
  stepId: string;
  /** Type of step */
  step: StepType;
  /** Current status */
  status: 'pending' | 'active' | 'completed' | 'failed';
  /** Transaction hash (if submitted) */
  txHash: string | null;
  /** Error (if failed) */
  error: Error | null;
  /** Timestamp of last update */
  timestamp: number;
}

mina.execute({
  quote,
  signer,
  onStepChange: (stepStatus) => {
    switch (stepStatus.status) {
      case 'pending':
        console.log(`Step ${stepStatus.stepId} waiting...`);
        break;
      case 'active':
        console.log(`Step ${stepStatus.stepId} executing...`);
        break;
      case 'completed':
        console.log(`Step ${stepStatus.stepId} done! Tx: ${stepStatus.txHash}`);
        break;
      case 'failed':
        console.error(`Step ${stepStatus.stepId} failed:`, stepStatus.error);
        break;
    }
  },
});

onStatusChange

Called for overall transaction status updates:
interface TransactionStatusPayload {
  /** Overall status */
  status: 'pending' | 'in_progress' | 'completed' | 'failed';
  /** Substatus for detailed state */
  substatus: string;
  /** Current step index (1-based) */
  currentStep: number;
  /** Total number of steps */
  totalSteps: number;
  /** Input amount */
  fromAmount: string;
  /** Output amount (or expected) */
  toAmount: string | null;
  /** Bridge transaction hash */
  txHash: string;
  /** Receiving transaction hash on destination */
  receivingTxHash: string | null;
  /** Progress percentage (0-100) */
  progress: number;
  /** Estimated time remaining in seconds */
  estimatedTime: number;
}

mina.execute({
  quote,
  signer,
  onStatusChange: (status) => {
    console.log(`Status: ${status.status}`);
    console.log(`Step: ${status.currentStep}/${status.totalSteps}`);
    console.log(`Progress: ${status.progress}%`);

    if (status.receivingTxHash) {
      console.log(`Receiving on HyperEVM: ${status.receivingTxHash}`);
    }
  },
});

Execution Result

ExecutionResult

interface ExecutionResult {
  /** Unique execution ID for status tracking */
  executionId: string;
  /** Overall status */
  status: 'pending' | 'executing' | 'completed' | 'failed';
  /** All step statuses */
  steps: StepStatus[];
  /** Final bridge transaction hash */
  txHash?: string;
  /** Input amount that was bridged */
  fromAmount?: string;
  /** Output amount received (or expected) */
  toAmount?: string;
  /** Received amount after bridge completion */
  receivedAmount?: string;
  /** Deposit transaction hash (if auto-deposit was enabled) */
  depositTxHash?: string | null;
  /** Error (if failed) */
  error?: Error;
}

Example: Complete Execution Flow

import { Mina, TransactionSigner } from '@siphoyawe/mina-sdk';

const mina = new Mina({
  integrator: 'my-app',
  autoDeposit: true  // Enable auto-deposit to L1
});

async function executeBridge(
  signer: TransactionSigner,
  params: {
    fromChainId: number;
    fromToken: string;
    amount: string;
  }
) {
  // 1. Get quote
  const fromAddress = await signer.getAddress();

  const quote = await mina.getQuote({
    fromChainId: params.fromChainId,
    toChainId: 999,
    fromToken: params.fromToken,
    toToken: '0xb88339cb7199b77e23db6e890353e22632ba630f',
    fromAmount: params.amount,
    fromAddress,
  });

  console.log('Quote received:');
  console.log(`  Expected output: ${quote.toAmount}`);
  console.log(`  Fees: $${quote.fees.totalUsd.toFixed(2)}`);

  // 2. Execute bridge
  const result = await mina.execute({
    quote,
    signer,
    onApprovalRequest: () => {
      console.log('Please approve the token spend in your wallet...');
    },
    onTransactionRequest: () => {
      console.log('Please confirm the bridge transaction...');
    },
    onStepChange: (step) => {
      console.log(`[${step.step}] ${step.status}`);
      if (step.txHash) {
        console.log(`  Tx: ${step.txHash}`);
      }
    },
    onStatusChange: (status) => {
      const progressBar = '='.repeat(status.progress / 5) + '-'.repeat(20 - status.progress / 5);
      console.log(`[${progressBar}] ${status.progress}% - ${status.substatus}`);
    },
    infiniteApproval: false, // Use exact approval for security
  });

  // 3. Handle result
  if (result.status === 'completed') {
    console.log('\nBridge completed successfully!');
    console.log(`Transaction: ${result.txHash}`);
    console.log(`Received: ${result.receivedAmount}`);

    if (result.depositTxHash) {
      console.log(`Deposit to L1: ${result.depositTxHash}`);
    }
  } else if (result.status === 'failed') {
    console.error('\nBridge failed:', result.error?.message);
  }

  return result;
}

Error Handling

The SDK throws specific errors during execution:
import {
  QuoteExpiredError,
  TransactionFailedError,
  UserRejectedError,
  NetworkError,
} from '@siphoyawe/mina-sdk';

try {
  const result = await mina.execute({ quote, signer });
} catch (error) {
  if (error instanceof QuoteExpiredError) {
    console.error('Quote expired, please get a new quote');
    // Get new quote and retry
  } else if (error instanceof UserRejectedError) {
    console.error('User rejected the transaction');
    // User cancelled in wallet
  } else if (error instanceof TransactionFailedError) {
    console.error(`Transaction failed: ${error.message}`);
    console.error(`Tx Hash: ${error.txHash}`);
    console.error(`Reason: ${error.reason}`);
  } else if (error instanceof NetworkError) {
    console.error('Network error, please try again');
  }
}
Always validate that the quote has not expired before executing. Quotes are valid for 60 seconds.

Token Approvals

For ERC20 tokens, an approval transaction is required before bridging:
mina.execute({
  quote,
  signer,
  // Use infinite approval to avoid repeated approvals
  infiniteApproval: true,

  onApprovalRequest: () => {
    // Show approval UI to user
    showApprovalModal();
  },

  onStepChange: (step) => {
    if (step.step === 'approval' && step.status === 'completed') {
      // Approval confirmed, bridge will proceed
      hideApprovalModal();
    }
  },
});
Setting infiniteApproval: false (default) approves only the exact amount needed, which is more secure but requires approval for each transaction.

Next Steps

Auto-Deposit

Learn about automatic L1 deposits

Slippage

Configure slippage settings