Skip to main content
The Vault is an escrow contract that holds intent rewards until they can be claimed by solvers or refunded to creators. Each intent has its own dedicated vault, deployed deterministically using CREATE2 for predictable addressing.

Architecture

Vaults use a minimal proxy pattern (ERC-1167) for gas-efficient deployment:
Key Properties:
  • One vault per intent, deployed on-demand
  • Deterministic addresses computed from intent hash
  • Only the Portal can manage vault operations
  • No persistent state between intents (stateless implementation)

Deployment

CREATE2 Determinism

Vault addresses are computed using CREATE2 with the intent hash as salt:

On-Demand Deployment

Vaults are deployed lazily when first needed:
  • Intent Publishing: Address is computed but not deployed
  • First Funding: Vault deployed if not already exists
  • Withdrawal/Refund: Vault deployed if not already exists
This saves gas when intents are published but never funded.

Chain Compatibility

The CREATE2 prefix adapts to different chains:
  • Standard EVM Chains: 0xff prefix
  • TRON Mainnet (728126428): 0x41 prefix
  • TRON Testnet (2494104990): 0x41 prefix

Access Control

All vault operations are restricted to the Portal contract that deployed the implementation. This ensures:
  • Only validated intents can withdraw funds
  • Refunds require proper deadline checks
  • Token recovery follows protocol rules

Funding

Standard Funding

Called during Portal.publishAndFund() or Portal.fund():
Funding Process:
  1. Check native token balance against reward.nativeAmount
  2. For each ERC20 token:
    • Attempt permit-based transfer first (if permit contract provided)
    • Fall back to standard transferFrom with allowance
  3. Return true only if all tokens fully funded
Partial Funding: The function transfers as much as available based on:
  • Funder’s token balance
  • Funder’s allowance/permit to vault
  • Existing vault balance

Permit-Based Funding

Vaults support gasless approvals via permit contracts (e.g., Permit2): Permit Transfer:
Queries permit contract for allowance and transfers tokens directly without requiring prior approve() call. Standard Transfer (Fallback):
Uses standard ERC20 transferFrom based on existing allowance. Funding Strategy:
  1. Check current vault balance
  2. Try permit transfer if permit contract provided
  3. Fall back to standard transfer for any remaining amount
  4. Return unfunded amount

Withdrawal

Solvers claim rewards after intent fulfillment and proof verification:
Withdrawal Process:
  1. Transfer all reward ERC20 tokens to claimant (up to reward amounts)
  2. Transfer native tokens to claimant (up to reward amount)
  3. Use actual balance (may be less than reward if partially funded)
Safety Features:
  • Uses SafeERC20.safeTransfer for token transfers
  • Minimum of reward amount and actual balance prevents over-withdrawal
  • Native transfers use low-level call with success check
  • Reverts on failed native transfer with NativeTransferFailed error

Refund

Creators reclaim rewards after intent expiry:
Refund Process:
  1. Transfer all ERC20 token balances to reward.creator
  2. Transfer all native token balance to reward.creator
  3. No amount restrictions (returns full vault contents)
Key Differences from Withdrawal:
  • Returns actual balance, not limited to reward amounts
  • Always transfers to reward.creator
  • Portal validates deadline before calling

Token Recovery

Recover tokens mistakenly sent to vault:
Use Cases:
  • User accidentally sent tokens directly to vault address
  • Tokens sent to wrong vault
  • Extra tokens beyond reward amounts
Restrictions (enforced by Portal):
  • Cannot recover zero address (native tokens)
  • Cannot recover any token in reward.tokens array
  • Intent must have zero native rewards OR already be claimed/refunded

Fund Flow

Publishing Flow

Withdrawal Flow

Refund Flow

Security Model

Immutable Portal

The portal address is set at construction and cannot be changed:
This creates a strong binding between vault and portal, preventing:
  • Unauthorized withdrawals
  • Fake refunds before expiry
  • Bypassing protocol validation

Balance-Based Logic

All transfers use actual balance rather than storing state:
  • No accounting variables to manipulate
  • Always transfers real tokens held by vault
  • Cannot withdraw more than vault contains

SafeERC20 Integration

Uses OpenZeppelin’s SafeERC20 for all token transfers:
  • Handles non-standard ERC20 returns
  • Prevents silent transfer failures
  • Reverts on insufficient balance

Minimal Proxy Pattern

Using ERC-1167 minimal proxies provides:
  • Gas Efficiency: ~2,500 gas to deploy vs ~200,000 for full contract
  • Security: Implementation cannot be changed after proxy deployment
  • Predictability: Deterministic addresses enable pre-funding

Integration Examples

Computing Vault Address

Pre-Funding Vault

Users can send tokens directly to the computed vault address before publishing:
Note: This skips the Funded status check, so verify funding manually with portal.isIntentFunded().

Partial Funding

Using Permit for Gasless Funding

Error Handling

  • NotPortalCaller(address): Caller is not the authorized Portal contract
  • NativeTransferFailed(address, uint256): Failed to send native tokens to recipient
  • ZeroRecoverTokenBalance(address): Attempted to recover token with zero balance

Gas Considerations

Deployment Costs

  • Full Vault Contract: ~200,000 gas
  • Minimal Proxy: ~2,500 gas (98.75% savings)
  • Implementation: One-time cost, shared across all vaults

Operation Costs

Funding:
  • Native transfer: ~21,000 gas
  • ERC20 transfer: ~50,000 gas per token
  • Permit transfer: ~70,000 gas per token (includes permit verification)
Withdrawal:
  • Per token: ~50,000 gas
  • Native transfer: ~21,000 gas
Refund:
  • Similar to withdrawal costs
  • No amount validation overhead

Optimization Strategies

  1. Batch Operations: Use Portal.batchWithdraw() to amortize vault deployment costs
  2. Pre-Funding: Send tokens to vault address before publishing to skip funding transaction
  3. Single Token Rewards: Minimize token array length in rewards
  4. Standard Transfers: Use regular approvals instead of permits when possible (lower gas)

Cross-Chain Considerations

Address Consistency

Vault addresses are deterministic across chains with same:
  • Portal deployment address
  • Intent parameters (destination, route, reward)
  • CREATE2 prefix (chain-specific)
Example:

Native Token Handling

Each chain’s native token is handled uniformly:
  • Ethereum: ETH
  • Polygon: MATIC
  • Arbitrum: ETH
  • TRON: TRX
Vaults treat all as “native amount” in reward.nativeAmount.

Best Practices

For Intent Creators

  1. Verify Funding: Call portal.isIntentFunded() before expecting fulfillment
  2. Adequate Approvals: Ensure sufficient allowance for all reward tokens
  3. Gas Budgeting: Include native amount for destination chain execution
  4. Deadline Buffer: Set reward.deadline with buffer for proof verification time

For Solvers

  1. Check Vault Balance: Verify vault is fully funded before fulfilling
  2. Gas Estimation: Account for withdrawal transaction costs in profitability calculations
  3. Claimant Address: Ensure claimant address is correct and can receive tokens
  4. Token Compatibility: Verify all reward tokens are standard ERC20

For Integrators

  1. Address Prediction: Compute vault addresses offchain to display to users
  2. Event Monitoring: Watch IntentFunded events to track funding progress
  3. Error Handling: Handle partial funding scenarios gracefully
  4. Permit Integration: Support gasless approvals for better UX