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.