# Adapter Architecture Specification ## Overview This document specifies the architecture for the hybrid adapter system that supports both DeFi protocols and Fiat/DTL (banking rails) connectors. It defines adapter interfaces, whitelist/blacklist mechanisms, protocol versioning, upgrade paths, and integration guides. --- ## 1. Adapter System Architecture ### High-Level Design ``` ┌─────────────────────────────────────────────────────────────┐ │ Combo Builder UI │ │ (Drag & Drop Adapter Selection) │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Adapter Registry Contract │ │ (Whitelist/Blacklist, Version Management) │ └──────────────┬──────────────────────────────┬───────────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ DeFi Adapters │ │ Fiat/DTL Adapters│ │ │ │ │ │ • Uniswap V3 │ │ • ISO-20022 Pay │ │ • Aave │ │ • SWIFT MT │ │ • Compound │ │ • SEPA │ │ • Bridge │ │ • FedNow │ └──────────────────┘ └──────────────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ DeFi Protocols │ │ Banking Rails │ │ (On-Chain) │ │ (Off-Chain) │ └──────────────────┘ └──────────────────┘ ``` --- ## 2. Adapter Interface Contract ### Base Interface: `IAdapter` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; interface IAdapter { /** * @notice Execute a step using this adapter * @param stepData Encoded step-specific parameters * @return success Whether execution succeeded * @return returnData Return data from execution */ function executeStep(bytes calldata stepData) external returns (bool success, bytes memory returnData); /** * @notice Prepare phase for 2PC (optional, if supported) * @param stepData Encoded step parameters * @return prepared Whether preparation succeeded */ function prepareStep(bytes calldata stepData) external returns (bool prepared); /** * @notice Get adapter metadata * @return name Adapter name * @return version Adapter version * @return adapterType Type (DEFI or FIAT_DTL) */ function getMetadata() external view returns (string memory name, string memory version, AdapterType adapterType); /** * @notice Check if adapter supports a specific step type * @param stepType Step type to check * @return supported Whether step type is supported */ function supportsStepType(StepType stepType) external view returns (bool supported); } enum AdapterType { DEFI, FIAT_DTL } enum StepType { BORROW, SWAP, REPAY, PAY, DEPOSIT, WITHDRAW, BRIDGE } ``` ### DeFi Adapter Example: `UniswapV3Adapter.sol` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "./IAdapter.sol"; import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; contract UniswapV3Adapter is IAdapter { ISwapRouter public constant swapRouter = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); function executeStep(bytes calldata stepData) external override returns (bool success, bytes memory returnData) { SwapParams memory params = abi.decode(stepData, (SwapParams)); ISwapRouter.ExactInputSingleParams memory swapParams = ISwapRouter.ExactInputSingleParams({ tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: params.recipient, deadline: block.timestamp + 300, amountIn: params.amountIn, amountOutMinimum: params.amountOutMinimum, sqrtPriceLimitX96: 0 }); uint256 amountOut = swapRouter.exactInputSingle(swapParams); return (true, abi.encode(amountOut)); } function prepareStep(bytes calldata) external pure override returns (bool) { // Uniswap doesn't support prepare phase return false; } function getMetadata() external pure override returns (string memory, string memory, AdapterType) { return ("Uniswap V3", "3.0.1", AdapterType.DEFI); } function supportsStepType(StepType stepType) external pure override returns (bool) { return stepType == StepType.SWAP; } struct SwapParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 amountIn; uint256 amountOutMinimum; } } ``` ### Fiat/DTL Adapter Example: `ISO20022PayAdapter.sol` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "./IAdapter.sol"; contract ISO20022PayAdapter is IAdapter { address public orchestrator; mapping(bytes32 => PaymentRequest) public pendingPayments; struct PaymentRequest { bytes32 planId; string beneficiaryIBAN; uint256 amount; string currency; bool executed; } function executeStep(bytes calldata stepData) external override returns (bool success, bytes memory returnData) { require(msg.sender == orchestrator, "Only orchestrator"); PayParams memory params = abi.decode(stepData, (PayParams)); // Store payment request for off-chain processing bytes32 requestId = keccak256(abi.encodePacked(params.planId, params.beneficiaryIBAN, params.amount)); pendingPayments[requestId] = PaymentRequest({ planId: params.planId, beneficiaryIBAN: params.beneficiaryIBAN, amount: params.amount, currency: params.currency, executed: false }); // Emit event for off-chain orchestrator to process emit PaymentRequested(requestId, params.planId, params.beneficiaryIBAN, params.amount, params.currency); return (true, abi.encode(requestId)); } function prepareStep(bytes calldata stepData) external override returns (bool) { // Fiat payments can support prepare phase (provisional ISO message) PayParams memory params = abi.decode(stepData, (PayParams)); bytes32 requestId = keccak256(abi.encodePacked(params.planId, params.beneficiaryIBAN, params.amount)); // Mark as prepared (provisional) pendingPayments[requestId].executed = false; // Not yet executed emit PaymentPrepared(requestId); return true; } function getMetadata() external pure override returns (string memory, string memory, AdapterType) { return ("ISO-20022 Pay", "1.2.0", AdapterType.FIAT_DTL); } function supportsStepType(StepType stepType) external pure override returns (bool) { return stepType == StepType.PAY; } function confirmPayment(bytes32 requestId, string memory isoMessageId) external { require(msg.sender == orchestrator, "Only orchestrator"); PaymentRequest storage payment = pendingPayments[requestId]; require(!payment.executed, "Already executed"); payment.executed = true; emit PaymentConfirmed(requestId, isoMessageId); } event PaymentRequested(bytes32 indexed requestId, bytes32 indexed planId, string beneficiaryIBAN, uint256 amount, string currency); event PaymentPrepared(bytes32 indexed requestId); event PaymentConfirmed(bytes32 indexed requestId, string isoMessageId); struct PayParams { bytes32 planId; string beneficiaryIBAN; uint256 amount; string currency; } } ``` --- ## 3. Whitelist/Blacklist Mechanisms ### On-Chain Registry (Smart Contract) ```solidity // Managed by AdapterRegistry contract (see Smart_Contract_Interfaces.md) // - registerAdapter() - Register new adapter // - whitelistAdapter() - Add to whitelist // - blacklistAdapter() - Remove from whitelist // - isWhitelisted() - Check whitelist status ``` ### Off-Chain API Filtering ```typescript // Backend API filters adapters based on: // 1. On-chain whitelist status // 2. User role/permissions // 3. Compliance requirements // 4. Geographic restrictions GET /api/adapters?type=DEFI&whitelistedOnly=true&userId=user123 ``` ### UI Filtering ```typescript // Frontend filters adapters based on: // 1. User selection (All, DeFi, Fiat/DTL, Whitelisted Only) // 2. Chain compatibility // 3. Compliance requirements const filteredAdapters = adapters.filter(adapter => { if (filter === 'DEFI') return adapter.type === 'DEFI'; if (filter === 'FIAT_DTL') return adapter.type === 'FIAT_DTL'; if (filter === 'WHITELISTED') return adapter.whitelisted; return true; // ALL }); ``` --- ## 4. Protocol Versioning ### Version String Format ``` Major.Minor.Patch Example: "3.0.1", "1.2.0" ``` ### Version Management #### On-Chain (Adapter Contract) ```solidity function getMetadata() external view returns (string memory, string memory, AdapterType) { return ("Uniswap V3", "3.0.1", AdapterType.DEFI); } ``` #### Off-Chain (API/Registry) ```json { "id": "uniswap-v3", "name": "Uniswap V3", "version": "3.0.1", "type": "DEFI", "whitelisted": true, "deprecated": false, "replacedBy": null, "chainIds": [1, 137, 42161], "lastUpdated": "2025-01-15T00:00:00Z" } ``` ### Version Upgrade Path 1. **Register New Version**: Deploy new adapter contract with incremented version 2. **Register in AdapterRegistry**: Call `registerAdapter()` with new address 3. **Whitelist New Version**: Call `whitelistAdapter()` for new address 4. **Deprecate Old Version**: Optionally blacklist old version 5. **Update UI**: Frontend fetches latest version from registry ### Breaking Changes - **Major Version**: Incompatible API changes (new interface required) - **Minor Version**: New features, backward compatible - **Patch Version**: Bug fixes, backward compatible --- ## 5. Upgrade Paths ### Option 1: New Contract Deployment (Recommended) - Deploy new adapter contract - Register in AdapterRegistry - Whitelist new contract - Update frontend to use new address - Old adapter remains for existing plans ### Option 2: Proxy Pattern (For Complex Adapters) ```solidity // Use Transparent Proxy or UUPS // Allows upgrade without changing address // Requires careful upgrade governance ``` ### Option 3: Adapter Factory Pattern ```solidity contract AdapterFactory { function createAdapter(string memory version) external returns (address) { // Deploy new adapter instance // Register automatically // Return address } } ``` --- ## 6. Integration Guide for Adding New Adapters ### Step 1: Implement IAdapter Interface ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "./IAdapter.sol"; contract MyNewAdapter is IAdapter { function executeStep(bytes calldata stepData) external override returns (bool, bytes memory) { // Implementation } function prepareStep(bytes calldata stepData) external override returns (bool) { // Implementation (optional) } function getMetadata() external pure override returns (string memory, string memory, AdapterType) { return ("My New Adapter", "1.0.0", AdapterType.DEFI); } function supportsStepType(StepType stepType) external pure override returns (bool) { return stepType == StepType.SWAP; // Example } } ``` ### Step 2: Deploy Contract ```bash # Deploy to target network npx hardhat run scripts/deploy.js --network mainnet ``` ### Step 3: Register in AdapterRegistry ```solidity // Call from admin account adapterRegistry.registerAdapter( myNewAdapterAddress, AdapterType.DEFI, "1.0.0", abi.encode(ipfsHash) // Metadata ); ``` ### Step 4: Register Codehash in NotaryRegistry ```solidity // Get codehash bytes32 codeHash; assembly { codeHash := extcodehash(myNewAdapterAddress) } // Register notaryRegistry.registerCodeHash(myNewAdapterAddress, codeHash); ``` ### Step 5: Whitelist Adapter ```solidity // After security review adapterRegistry.whitelistAdapter(myNewAdapterAddress); ``` ### Step 6: Update Backend API ```typescript // Add adapter to database/configuration const adapter = { id: 'my-new-adapter', address: myNewAdapterAddress, type: 'DEFI', version: '1.0.0', whitelisted: true }; await db.adapters.insert(adapter); ``` ### Step 7: Update Frontend ```typescript // Adapter should appear automatically via API // If custom UI needed, add to adapter palette configuration ``` ### Step 8: Testing - Unit tests for adapter contract - Integration tests with ComboHandler - E2E tests in UI - Security audit (if handling significant funds) --- ## 7. DeFi Adapter Integration Examples ### Aave Lending Adapter ```solidity contract AaveAdapter is IAdapter { IPool public constant aavePool = IPool(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); function executeStep(bytes calldata stepData) external override returns (bool, bytes memory) { LendingParams memory params = abi.decode(stepData, (LendingParams)); if (params.action == LendingAction.BORROW) { aavePool.borrow(params.asset, params.amount, 2, 0, msg.sender); // Variable rate } else if (params.action == LendingAction.REPAY) { aavePool.repay(params.asset, params.amount, 2, msg.sender); } return (true, ""); } enum LendingAction { BORROW, REPAY, DEPOSIT, WITHDRAW } struct LendingParams { LendingAction action; address asset; uint256 amount; } } ``` ### Bridge Adapter (Cross-Chain) ```solidity contract BridgeAdapter is IAdapter { function executeStep(bytes calldata stepData) external override returns (bool, bytes memory) { BridgeParams memory params = abi.decode(stepData, (BridgeParams)); // Lock tokens on source chain // Emit event for bridge service emit BridgeRequest(params.token, params.amount, params.targetChain, params.recipient); return (true, ""); } event BridgeRequest(address indexed token, uint256 amount, uint256 targetChain, address recipient); struct BridgeParams { address token; uint256 amount; uint256 targetChain; address recipient; } } ``` --- ## 8. Fiat/DTL Adapter Integration Examples ### SWIFT MT Adapter ```solidity contract SWIFTAdapter is IAdapter { function executeStep(bytes calldata stepData) external override returns (bool, bytes memory) { SWIFTParams memory params = abi.decode(stepData, (SWIFTParams)); // Store SWIFT message request bytes32 messageId = keccak256(abi.encodePacked(params.planId, params.beneficiary, params.amount)); emit SWIFTMessageRequested(messageId, params.planId, params.beneficiary, params.amount); return (true, abi.encode(messageId)); } event SWIFTMessageRequested(bytes32 indexed messageId, bytes32 indexed planId, string beneficiary, uint256 amount); struct SWIFTParams { bytes32 planId; string beneficiary; uint256 amount; string currency; string messageType; // MT103, MT202, etc. } } ``` ### SEPA Adapter ```solidity contract SEPAAdapter is IAdapter { function executeStep(bytes calldata stepData) external override returns (bool, bytes memory) { SEPAParams memory params = abi.decode(stepData, (SEPAParams)); bytes32 paymentId = keccak256(abi.encodePacked(params.planId, params.creditorIBAN, params.amount)); emit SEPACreditTransferRequested(paymentId, params.planId, params.creditorIBAN, params.amount); return (true, abi.encode(paymentId)); } event SEPACreditTransferRequested(bytes32 indexed paymentId, bytes32 indexed planId, string creditorIBAN, uint256 amount); struct SEPAParams { bytes32 planId; string creditorIBAN; string creditorName; uint256 amount; string currency; string remittanceInfo; } } ``` --- ## 9. Security Considerations ### Adapter Validation 1. **Codehash Verification**: Verify adapter codehash matches registered hash before execution 2. **Whitelist Check**: Only execute whitelisted adapters 3. **Reentrancy Protection**: Use ReentrancyGuard in handler contract 4. **Input Validation**: Validate all step parameters before execution ### Access Control 1. **Orchestrator-Only Execution**: Only orchestrator can call adapter execute functions 2. **Admin Functions**: Multi-sig required for whitelist/blacklist operations 3. **Timelock**: Implement timelock for critical operations ### Audit Requirements 1. **Security Audit**: All adapters must pass security audit before whitelisting 2. **Code Review**: Peer review required for adapter code 3. **Testing**: Comprehensive test coverage required --- ## 10. Testing Requirements ### Unit Tests ```solidity // Test adapter interface implementation function testExecuteStep() public { // Test successful execution // Test failure cases // Test return data } function testPrepareStep() public { // Test prepare phase (if supported) } ``` ### Integration Tests ```solidity // Test adapter with ComboHandler function testAdapterInCombo() public { // Test adapter works in multi-step combo // Test step dependencies // Test error handling } ``` ### E2E Tests ```typescript // Test adapter in full UI flow describe('Uniswap V3 Adapter', () => { it('should execute swap in combo', async () => { // Build combo with Uniswap step // Execute combo // Verify results }); }); ``` --- ## 11. Best Practices ### Adapter Design 1. **Keep It Simple**: Adapters should do one thing well 2. **Error Handling**: Return clear error messages 3. **Gas Optimization**: Minimize gas usage 4. **Event Emission**: Emit events for off-chain tracking ### Version Management 1. **Semantic Versioning**: Follow semver (Major.Minor.Patch) 2. **Backward Compatibility**: Maintain backward compatibility when possible 3. **Deprecation Policy**: Clearly communicate deprecation timeline ### Documentation 1. **README**: Document adapter purpose, parameters, usage 2. **API Docs**: Document all functions and parameters 3. **Examples**: Provide usage examples --- **Document Version**: 1.0 **Last Updated**: 2025-01-15 **Author**: Architecture Team