Transaction System

Overview

Wafra implements a hybrid transaction signing system that combines WebAuthn passkey convenience with server-side validation for secure, user-friendly transaction execution on Gnosis Safe multisig wallets.

For wallet creation and multisig setup, see Multisig Wallet Creation.

Transaction Architecture

Hybrid Signing Model

Signing Process

Client-Side Responsibilities:

  • Transaction preparation and parameter collection
  • WebAuthn challenge handling and signature generation
  • DER signature parsing and validation
  • Initial transaction validation

Server-Side Responsibilities:

  • Final transaction validation and authorization
  • Gas management and optimization
  • Server signature generation (controller key)
  • Blockchain submission and monitoring

Transaction Builder Pattern

Current Transaction Types:

interface TransactionBuilder {
  // Fund operations
  deposit(amount: bigint, token: Address): Transaction;
  withdraw(amount: bigint, recipient: Address): Transaction;
 
  // Strategy operations
  allocateToStrategy(strategy: Address, amount: bigint): Transaction;
  harvestStrategy(strategy: Address): Transaction;
 
  // Token operations
  transfer(token: Address, to: Address, amount: bigint): Transaction;
  approve(token: Address, spender: Address, amount: bigint): Transaction;
 
  // Administrative
  addOwner(newOwner: Address): Transaction;
  removeOwner(owner: Address): Transaction;
  changeThreshold(newThreshold: number): Transaction;
}

Transaction Lifecycle

  • Step 1: Client builds transaction with parameters

  • Step 2: User signs with WebAuthn passkey

  • Step 3: Server validates transaction and user authorization

  • Step 4: Controller key provides second signature

  • Step 5: Transaction executed on Gnosis Safe

  • Step 6: Real-time status tracking and confirmation

  • Step 7: Final status update and balance refresh

WebAuthn Integration

Passkey Signature Process

Current Implementation:

interface PasskeySignature {
  // WebAuthn response data
  authenticatorData: Uint8Array;
  clientDataJSON: string;
  signature: Uint8Array;
 
  // Parsed signature components
  r: bigint; // ECDSA signature component
  s: bigint; // ECDSA signature component
 
  // Recovery data
  recoveryId: number;
  messageHash: string;
}

Signature Validation:

// Client-side signature processing
export function processWebAuthnSignature(
  response: AuthenticatorAssertionResponse,
  challenge: string
): PasskeySignature {
  // Parse DER-encoded signature
  const signature = parseDERSignature(response.signature);
 
  // Calculate message hash
  const messageHash = calculateMessageHash(
    response.authenticatorData,
    response.clientDataJSON,
    challenge
  );
 
  // Determine recovery ID
  const recoveryId = calculateRecoveryId(signature, messageHash);
 
  return {
    authenticatorData: response.authenticatorData,
    clientDataJSON: response.clientDataJSON,
    signature: response.signature,
    r: signature.r,
    s: signature.s,
    recoveryId,
    messageHash,
  };
}

Address Derivation

Passkey to Ethereum Address:

export function deriveAddressFromPasskey(
  publicKey: Uint8Array,
  authenticatorData: Uint8Array,
  clientDataJSON: string
): Address {
  // Extract coordinates from public key
  const { x, y } = parsePublicKey(publicKey);
 
  // Calculate Ethereum address from public key
  const address = computeEthereumAddress(x, y);
 
  return address;
}

Gas Management System

Gas Management Framework

  • Step 1: Current gas prices and network congestion

  • Step 2: Estimate gas usage for specific transaction

  • Step 3: Select appropriate fee strategy (slow/medium/fast)

  • Step 4: Predefined values if estimation fails

  • Step 5: Convert to ETH and USD for user display

Gas Optimization Strategies

Current Optimizations:

  • Batch Transactions: Combine multiple operations when possible
  • Gas Price Monitoring: Real-time fee optimization
  • Fallback System: Reliable execution even when estimation fails
  • Cost Tracking: Comprehensive gas usage analytics

Gas Price Management:

Gas prices are dynamically determined based on current network conditions, with automatic optimization for Base L2 network characteristics. The system monitors real-time gas prices and applies appropriate buffers to ensure transaction confirmation while minimizing costs.

Security Features

Multi-Signature Protection

Security Benefits:

  • No Single Point of Failure: Requires multiple signatures
  • Hardware-Backed Security: Passkey stored in secure hardware
  • Server Validation: Transaction validation before co-signing
  • Audit Trail: Complete transaction history on-chain

Transaction Validation

Current Validation Checks:

interface TransactionValidation {
  // Basic validation
  validRecipient: boolean;
  validAmount: boolean;
  sufficientBalance: boolean;
 
  // Security checks
  authorizedUser: boolean;
  validPasskeySignature: boolean;
  sessionValid: boolean;
 
  // Business logic
  complianceCheck: boolean;
  rateLimitCheck: boolean;
  riskAssessment: RiskLevel;
}

Risk Assessment:

  • Transaction Amount: Higher amounts require additional verification
  • Recipient Analysis: Check against known addresses and blacklists
  • User Behavior: Detect unusual transaction patterns
  • Device Context: Verify transaction from trusted device

Recovery Mechanisms

Backup Key System:

  • Secure Generation: Backup key generated in HSM
  • Encrypted Storage: Backup key encrypted and stored securely
  • Recovery Process: Multi-step verification for backup key usage
  • Audit Logging: Complete audit trail for recovery operations

Real-Time Monitoring

Transaction Tracking

Current Monitoring:

interface TransactionStatus {
  id: string;
  hash?: string;
  status:
    | "preparing"
    | "signed"
    | "submitted"
    | "pending"
    | "confirmed"
    | "failed";
  blockNumber?: number;
  confirmations: number;
  gasUsed?: bigint;
  gasPrice?: bigint;
  fees: {
    eth: bigint;
    usd: number;
  };
  timestamps: {
    created: Date;
    signed?: Date;
    submitted?: Date;
    confirmed?: Date;
  };
}

Real-Time Notifications

Socket.IO Events:

  • Transaction Submitted: Immediate notification when transaction enters mempool
  • Block Confirmations: Updates as confirmations accumulate
  • Final Confirmation: Transaction finalized notification
  • Failure Alerts: Immediate notification of transaction failures

Notification System with Manual Control:

// Client-side event handlers (listen-only mode)
socket.on("transaction-status-update", (data: TransactionStatusUpdate) => {
  // Log status update but don't auto-update UI
  console.log("📡 Transaction status update:", data.status);
 
  // Show notification to user that update is available
  if (data.status === "confirmed") {
    console.log("✅ Transaction confirmed - manual refresh to see changes");
  }
 
  // Do NOT automatically invalidate queries - user controls when to refresh
  // trpc.transactions.getTransaction.invalidate({ id: data.transactionId });
  // trpc.balances.getBalances.invalidate();
});
 
// Manual refresh function for user-triggered updates
const refreshTransactionData = useCallback(() => {
  queryClient.invalidateQueries({ queryKey: ["transactions"] });
  queryClient.invalidateQueries({ queryKey: ["balances"] });
}, [queryClient]);

Performance Optimizations

Transaction Batching

Batch Operations:

interface BatchTransaction {
  operations: TransactionOperation[];
  totalGasEstimate: bigint;
  estimatedSavings: {
    gas: bigint;
    usd: number;
  };
}
 
// Example: Deposit + Strategy Allocation
const batchDeposit = {
  operations: [
    { type: "deposit", amount: 1000n * 10n ** 6n },
    { type: "allocate", strategy: "aave", amount: 500n * 10n ** 6n },
    { type: "allocate", strategy: "morpho", amount: 500n * 10n ** 6n },
  ],
};

Optimistic UI Updates

Immediate User Feedback:

  • Instant Updates: UI updates immediately upon transaction submission
  • Pending States: Clear indication of pending transactions
  • Rollback Capability: Revert optimistic updates if transaction fails
  • Progress Indicators: Real-time progress during transaction lifecycle

Error Handling & Recovery

Transaction Failure Handling

Failure Categories:

enum TransactionFailureReason {
  INSUFFICIENT_GAS = "insufficient_gas",
  REVERTED = "reverted",
  NETWORK_ERROR = "network_error",
  SIGNATURE_INVALID = "signature_invalid",
  TIMEOUT = "timeout",
  RATE_LIMITED = "rate_limited",
}
 
interface TransactionFailure {
  reason: TransactionFailureReason;
  message: string;
  recoverable: boolean;
  suggestedAction: string;
  retryAfter?: number;
}

Automatic Recovery

Recovery Mechanisms:

  • Retry Logic: Automatic retry for transient failures
  • Gas Price Adjustment: Increase gas price for stuck transactions
  • Alternative Routes: Fallback to different execution paths
  • User Notification: Clear communication of issues and solutions

Development & Testing

Local Development Setup

Development Tools:

  • Local Blockchain: Hardhat/Anvil for local testing
  • Mock Passkeys: Simulated WebAuthn for development
  • Gas Management: Reliable gas handling in development
  • Transaction Monitoring: Real-time transaction tracking

Testing Framework

Comprehensive Testing:

  • Unit Tests: Individual component testing
  • Integration Tests: End-to-end transaction flows
  • Security Tests: Signature validation and authorization
  • Performance Tests: Gas optimization and throughput

This wallet architecture provides enterprise-grade security while maintaining the simplicity users expect from modern financial applications.