Files
CurrenciCombo/docs/Adapter_Architecture_Spec.md

662 lines
19 KiB
Markdown
Raw Permalink Normal View History

# 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