Architecture
Vaults use a minimal proxy pattern (ERC-1167) for gas-efficient deployment:- 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
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
- Only validated intents can withdraw funds
- Refunds require proper deadline checks
- Token recovery follows protocol rules
Funding
Standard Funding
Called duringPortal.publishAndFund()
or Portal.fund()
:
- Check native token balance against
reward.nativeAmount
- For each ERC20 token:
- Attempt permit-based transfer first (if permit contract provided)
- Fall back to standard
transferFrom
with allowance
- Return true only if all tokens fully funded
- 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:approve()
call.
Standard Transfer (Fallback):
transferFrom
based on existing allowance.
Funding Strategy:
- Check current vault balance
- Try permit transfer if permit contract provided
- Fall back to standard transfer for any remaining amount
- Return unfunded amount
Withdrawal
Solvers claim rewards after intent fulfillment and proof verification:- Transfer all reward ERC20 tokens to claimant (up to reward amounts)
- Transfer native tokens to claimant (up to reward amount)
- Use actual balance (may be less than reward if partially funded)
- 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:- Transfer all ERC20 token balances to
reward.creator
- Transfer all native token balance to
reward.creator
- No amount restrictions (returns full vault contents)
- 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:- User accidentally sent tokens directly to vault address
- Tokens sent to wrong vault
- Extra tokens beyond reward amounts
- 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:- 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: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 contractNativeTransferFailed(address, uint256)
: Failed to send native tokens to recipientZeroRecoverTokenBalance(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)
- Per token: ~50,000 gas
- Native transfer: ~21,000 gas
- Similar to withdrawal costs
- No amount validation overhead
Optimization Strategies
- Batch Operations: Use
Portal.batchWithdraw()
to amortize vault deployment costs - Pre-Funding: Send tokens to vault address before publishing to skip funding transaction
- Single Token Rewards: Minimize token array length in rewards
- 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)
Native Token Handling
Each chain’s native token is handled uniformly:- Ethereum: ETH
- Polygon: MATIC
- Arbitrum: ETH
- TRON: TRX
reward.nativeAmount
.
Best Practices
For Intent Creators
- Verify Funding: Call
portal.isIntentFunded()
before expecting fulfillment - Adequate Approvals: Ensure sufficient allowance for all reward tokens
- Gas Budgeting: Include native amount for destination chain execution
- Deadline Buffer: Set
reward.deadline
with buffer for proof verification time
For Solvers
- Check Vault Balance: Verify vault is fully funded before fulfilling
- Gas Estimation: Account for withdrawal transaction costs in profitability calculations
- Claimant Address: Ensure claimant address is correct and can receive tokens
- Token Compatibility: Verify all reward tokens are standard ERC20
For Integrators
- Address Prediction: Compute vault addresses off-chain to display to users
- Event Monitoring: Watch
IntentFunded
events to track funding progress - Error Handling: Handle partial funding scenarios gracefully
- Permit Integration: Support gasless approvals for better UX