Adapter Architecture
Adapters are protocol-specific contracts that handle settlement logic via delegatecall from the Router. They inherit the Router’s storage context and permissions during execution. Key Principles fromAdapterBase.sol:8-61:
- Always executed via delegatecall from the Router
- Access Router’s storage and balance during execution
- Must return function selector for validation
- Must implement ERC165 interface detection
- Only interact with trusted, audited protocols
Implementation Requirements
Every adapter must meet these critical requirements:1. Return Function Selector
All fill and claim functions must return their own selector:2. Implement ERC165
Support interface detection for all settlement functions:3. Use onlyViaRouter Modifier
Protect all external functions from direct calls:4. Security Constraints
FromAdapterBase.sol:18-21:
- Never make direct calls to untrusted contracts
- Use only trusted, well-audited protocols
- Remember you’re executing in Router’s context
- Storage writes affect Router’s storage
Building Your First Adapter
Let’s build a simple adapter for a custom DEX protocol:1
Inherit from AdapterBase
Start with the base contract:
2
Set up constructor
Initialize with Router and Arbiter addresses:The constructor sets:
_ROUTER: Immutable router address for security checksARBITER: Contract that validates settlements (fromAdapterBase.sol:69)
3
Define data structures
Create structs for your fill operations:
4
Implement fill function
Add your settlement logic:
5
Extract solver context
Implement context extraction:The
_loadRelayerContext() helper is provided by AdapterBase.sol:137-144.6
Add interface detection
Implement ERC165 support:
Advanced Patterns
Using AdapterBasePrefund
For adapters that need to prefund recipients, inherit fromAdapterBasePrefund:
AdapterBasePrefund.sol:42-50, the helper handles both native ETH and ERC20 tokens:
Multi-Token Support
For adapters handling multiple tokens (fromAdapterBasePrefund.sol:22-31):
Custom Solver Context
Define complex context structures:Skip Relayer Context
For adapters that don’t need solver context (fromIntentExecutorAdapter.sol:227-229):
Claim Operations
Implement claim functions for resource unlocking:Real-World Example: SameChainAdapter
Let’s examine the production SameChainAdapter fromSameChainAdapter.sol:
Structure
Context Extraction
Fill Implementation
Interface Support
Testing Your Adapter
Unit Tests
Integration Tests
Deployment and Registration
See the Deployment Guide for details on deploying and registering your adapter with the Router.Best Practices
Security
- Validate inputs: Always check addresses, amounts, and context data
- Use SafeTransferLib: Never use raw
transfer()ortransferFrom() - Trusted protocols only: Only integrate with audited, production-ready protocols
- Reentrancy protection: Be aware of the Router’s reentrancy guard
Gas Optimization
- Minimize storage: Remember you’re in Router’s context - avoid unnecessary SSTORE operations
- Use assembly carefully: For calldata extraction, but maintain memory safety
- Cache storage reads: If reading Router storage, cache values
- Efficient encoding: Use
encodePackedfor solver context
Maintainability
- Clear documentation: Document context format and requirements
- Helper functions: Provide encoding helpers for solvers
- Versioning: Use semantic versioning properly
- Events: Emit events for off-chain tracking
Testing
- Unit tests: Test each function in isolation
- Integration tests: Test with actual Router
- Fuzz testing: Test with random inputs
- Gas benchmarks: Measure and optimize gas usage
Next Steps
Solver Context
Learn about solver context formats and patterns
Deployment
Deploy and register your adapter