Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.usemina.co/llms.txt

Use this file to discover all available pages before exploring further.

The Execute Service handles bridge transaction execution via the LI.FI API, including token approvals, transaction signing, status polling, and step-by-step progress tracking.

Exports

import {
  execute,
  validateQuote,
  QuoteExpiredError,
  InvalidQuoteError,
  isQuoteExpiredError,
  isInvalidQuoteError,
} from '@mina-bridge/sdk/services/execute';

Functions

execute(config)

Execute a bridge transaction from a quote.
async function execute(config: ExecuteConfig): Promise<ExecutionResult>

ExecuteConfig

ParameterTypeRequiredDefaultDescription
quoteQuoteYes-Quote to execute
signerTransactionSignerYes-Wallet signer (viem WalletClient compatible)
onStepChangeOnStepChangeNo-Callback for step status updates
onStatusChangeOnStatusChangeNo-Callback for overall status updates
onApprovalRequest() => voidNo-Callback before approval transaction
onTransactionRequest() => voidNo-Callback before main transaction
infiniteApprovalbooleanNofalseAllow infinite token approval
emitterSDKEventEmitterNo-Event emitter for SDK events
Returns: ExecutionResult
PropertyTypeDescription
executionIdstringUnique execution ID for tracking
status'pending' | 'executing' | 'completed' | 'failed'Overall status
stepsStepStatus[]Array of step statuses
txHashstring | undefinedFinal transaction hash
fromAmountstring | undefinedInput amount
toAmountstring | undefinedExpected output amount
receivedAmountstring | undefinedActual received amount
depositTxHashstring | nullDeposit transaction hash (if auto-deposit)
errorError | undefinedError details (if failed)
Throws:
  • QuoteExpiredError if quote has expired (5 minute max age)
  • InvalidQuoteError if quote is malformed
  • TransactionFailedError if transaction fails on-chain
  • UserRejectedError if user rejects transaction
  • NetworkError if network request fails

Execution Flow

The execute function processes a quote through these stages:
1. VALIDATION
   - Validate quote structure
   - Check quote expiration (5 minute max)

2. APPROVAL (if needed)
   - Check current token allowance
   - Request approval transaction
   - Wait for approval confirmation

3. EXECUTION
   - Submit bridge/swap transaction
   - Update step status to 'executing'

4. BRIDGING
   - Poll LI.FI status API (5 second interval)
   - Update progress based on substatus
   - Wait up to 10 minutes for completion

5. COMPLETION
   - Mark all steps as completed
   - Return final execution result

Example: Basic Execution

import { execute } from '@mina-bridge/sdk';

const result = await execute({
  quote,
  signer: walletClient,
  infiniteApproval: true,
});

if (result.status === 'completed') {
  console.log('Bridge complete!');
  console.log('TxHash:', result.txHash);
  console.log('Received:', result.receivedAmount);
} else if (result.status === 'failed') {
  console.error('Bridge failed:', result.error?.message);
}

Example: With Progress Callbacks

const result = await execute({
  quote,
  signer: walletClient,
  onStepChange: (step) => {
    console.log(`Step ${step.stepId}: ${step.status}`);

    if (step.txHash) {
      console.log(`  Transaction: ${step.txHash}`);
    }

    if (step.error) {
      console.error(`  Error: ${step.error.message}`);
    }
  },
  onStatusChange: (status) => {
    console.log(`\n=== Status Update ===`);
    console.log(`Status: ${status.status}`);
    console.log(`Substatus: ${status.substatus}`);
    console.log(`Progress: ${status.progress}%`);
    console.log(`Step: ${status.currentStep}/${status.totalSteps}`);

    if (status.txHash) {
      console.log(`TxHash: ${status.txHash}`);
    }

    if (status.receivingTxHash) {
      console.log(`Receiving TxHash: ${status.receivingTxHash}`);
    }
  },
  onApprovalRequest: () => {
    console.log('Approval required - please confirm in wallet');
  },
  onTransactionRequest: () => {
    console.log('Transaction ready - please confirm in wallet');
  },
});

Example: With Event Emitter

import { SDKEventEmitter, SDK_EVENTS } from '@mina-bridge/sdk';

const emitter = new SDKEventEmitter();

// Subscribe to events
emitter.on(SDK_EVENTS.EXECUTION_STARTED, ({ executionId, quoteId }) => {
  console.log(`Execution ${executionId} started for quote ${quoteId}`);
});

emitter.on(SDK_EVENTS.STEP_CHANGED, (step) => {
  console.log(`Step ${step.stepId}: ${step.status}`);
});

emitter.on(SDK_EVENTS.TRANSACTION_SENT, ({ txHash, chainId, stepType }) => {
  console.log(`Transaction sent: ${txHash} on chain ${chainId} (${stepType})`);
});

emitter.on(SDK_EVENTS.EXECUTION_COMPLETED, ({ executionId, txHash }) => {
  console.log(`Execution ${executionId} completed: ${txHash}`);
});

emitter.on(SDK_EVENTS.EXECUTION_FAILED, ({ executionId, error, step }) => {
  console.error(`Execution ${executionId} failed at step ${step}:`, error);
});

// Execute with emitter
const result = await execute({
  quote,
  signer,
  emitter,
});

validateQuote(quote)

Validate a quote before execution.
function validateQuote(quote: Quote): void
Throws:
  • QuoteExpiredError if quote has expired
  • InvalidQuoteError if quote is malformed
Validation Checks:
  1. Quote is not null/undefined
  2. Quote has a valid ID
  3. Quote has at least one execution step
  4. Quote has fromAmount and toAmount
  5. Quote has not expired (expiresAt timestamp)
  6. Quote age does not exceed 5 minutes
Example:
import { validateQuote } from '@mina-bridge/sdk';

try {
  validateQuote(quote);
  console.log('Quote is valid');
} catch (error) {
  if (isQuoteExpiredError(error)) {
    console.error('Quote expired at:', new Date(error.expiredAt));
    // Fetch a new quote
  } else if (isInvalidQuoteError(error)) {
    console.error('Invalid quote:', error.reason);
  }
}

Error Types

QuoteExpiredError

Thrown when a quote has exceeded its validity period.
class QuoteExpiredError extends MinaError {
  readonly code: 'QUOTE_EXPIRED';
  readonly recoverable: true;
  readonly recoveryAction: 'fetch_new_quote';
  readonly quoteId: string;
  readonly expiredAt: number;
}
PropertyTypeDescription
quoteIdstringID of the expired quote
expiredAtnumberTimestamp when quote expired
Example:
try {
  const result = await execute({ quote, signer });
} catch (error) {
  if (isQuoteExpiredError(error)) {
    console.error('Quote expired:', error.quoteId);
    console.log('Expired at:', new Date(error.expiredAt));

    // Fetch new quote and retry
    const newQuote = await mina.getQuote(originalParams);
    const result = await execute({ quote: newQuote, signer });
  }
}

InvalidQuoteError

Thrown when a quote is malformed or missing required data.
class InvalidQuoteError extends MinaError {
  readonly code: 'INVALID_QUOTE';
  readonly recoverable: false;
  readonly reason: string;
}
PropertyTypeDescription
reasonstringWhy the quote is invalid
Reasons:
  • null_quote - Quote is null or undefined
  • missing_id - Quote has no ID
  • no_steps - Quote has no execution steps
  • missing_amounts - Quote is missing amount information
Example:
try {
  const result = await execute({ quote, signer });
} catch (error) {
  if (isInvalidQuoteError(error)) {
    console.error('Quote is invalid:', error.reason);
    // Fetch a new quote
  }
}

Types

TransactionSigner

Compatible with viem WalletClient and ethers Signer.
interface TransactionSigner {
  /** Sign and send a transaction, returns tx hash */
  sendTransaction: (request: TransactionRequest) => Promise<string>;
  /** Get the signer's address */
  getAddress: () => Promise<string>;
  /** Get the current chain ID */
  getChainId: () => Promise<number>;
}

TransactionRequest

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

ExecutionStatus

type ExecutionStatus =
  | 'idle'
  | 'approving'
  | 'approved'
  | 'executing'
  | 'bridging'
  | 'completed'
  | 'failed';

StepStatus

interface StepStatus {
  stepId: string;
  stepType?: StepType;
  status: 'pending' | 'executing' | 'completed' | 'failed';
  txHash?: string;
  error?: string;
  updatedAt: number;
}

StepStatusPayload

Payload passed to onStepChange callback.
interface StepStatusPayload {
  stepId: string;
  step: StepType;
  status: 'pending' | 'active' | 'completed' | 'failed';
  txHash: string | null;
  error: Error | null;
  timestamp: number;
}

TransactionStatusPayload

Payload passed to onStatusChange callback.
interface TransactionStatusPayload {
  status: 'pending' | 'in_progress' | 'completed' | 'failed';
  substatus: string;
  currentStep: number;
  totalSteps: number;
  fromAmount: string;
  toAmount: string | null;
  txHash: string;
  receivingTxHash: string | null;
  progress: number;
  estimatedTime: number;
}

Constants

Timing Constants

ConstantValueDescription
MAX_QUOTE_AGE_MS3000005 minutes - maximum quote validity
STATUS_POLL_INTERVAL_MS50005 seconds - polling interval
MAX_EXECUTION_WAIT_MS60000010 minutes - maximum wait time
APPROVAL_CONFIRMATION_WAIT_MS30003 seconds - approval wait

Common Patterns

Retry on Expiration

async function executeWithRetry(
  params: QuoteParams,
  signer: TransactionSigner,
  maxRetries = 3
) {
  let retries = 0;

  while (retries < maxRetries) {
    try {
      const quote = await mina.getQuote(params);
      return await execute({ quote, signer });
    } catch (error) {
      if (isQuoteExpiredError(error) && retries < maxRetries - 1) {
        console.log('Quote expired, fetching new quote...');
        retries++;
        continue;
      }
      throw error;
    }
  }
}

Progress UI State Machine

type UIState = 'idle' | 'approving' | 'sending' | 'bridging' | 'complete' | 'error';

function executeWithUI(quote: Quote, signer: TransactionSigner) {
  let uiState: UIState = 'idle';

  return execute({
    quote,
    signer,
    onApprovalRequest: () => {
      uiState = 'approving';
      updateUI(uiState);
    },
    onTransactionRequest: () => {
      uiState = 'sending';
      updateUI(uiState);
    },
    onStatusChange: (status) => {
      if (status.status === 'in_progress') {
        uiState = 'bridging';
      } else if (status.status === 'completed') {
        uiState = 'complete';
      } else if (status.status === 'failed') {
        uiState = 'error';
      }
      updateUI(uiState, status);
    },
  });
}

Cancel Support

// The execute function doesn't support cancellation directly,
// but you can track the execution and handle UI accordingly

let currentExecutionId: string | null = null;
let cancelled = false;

async function startExecution(quote: Quote, signer: TransactionSigner) {
  cancelled = false;

  const result = await execute({
    quote,
    signer,
    onStatusChange: (status) => {
      if (cancelled) {
        // Can't cancel on-chain, but can stop UI updates
        return;
      }
      // Update UI
    },
  });

  currentExecutionId = result.executionId;
  return result;
}

function cancelUI() {
  cancelled = true;
  // Note: The transaction will still complete on-chain
  console.warn('UI cancelled, but transaction may still complete');
}