|
| 1 | +# Token Transformation System |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Token Transformation System enables delegations to track and control token transformations through DeFi protocol interactions. This system allows AI agents and other automated systems to use delegated tokens in lending protocols (like Aave and Morpho) while maintaining granular control over the evolving token positions. |
| 6 | + |
| 7 | +## Problem Statement |
| 8 | + |
| 9 | +### The Challenge |
| 10 | + |
| 11 | +Traditional delegation systems grant access to a fixed amount of a single token. However, when tokens are used in DeFi protocols, they often transform into different tokens: |
| 12 | + |
| 13 | +- **Lending Protocols**: Depositing USDC into Aave yields aUSDC (a rebasing token that increases over time) |
| 14 | +- **Yield Strategies**: Tokens may be transformed through multiple protocol interactions |
| 15 | +- **Multi-Token Positions**: A single delegation may evolve to control multiple token types |
| 16 | + |
| 17 | +**Example Scenario:** |
| 18 | +1. User delegates 1000 USDC to an AI agent |
| 19 | +2. Agent deposits 500 USDC → receives 500 aUSDC (Aave) |
| 20 | +3. Agent uses 200 USDC to buy DAI via a swap |
| 21 | +4. Final state: User should have control over 300 USDC + 500 aUSDC + 200 DAI |
| 22 | + |
| 23 | +The challenge is maintaining permission over **all tokens derived from the original delegation** until the delegation expires or is revoked. |
| 24 | + |
| 25 | +### Requirements |
| 26 | + |
| 27 | +1. **Track Transformations**: Monitor what tokens are generated from protocol interactions |
| 28 | +2. **Multi-Token Support**: Track multiple tokens per delegation simultaneously |
| 29 | +3. **Granular Control**: Maintain access control over each token type and amount |
| 30 | +4. **Protocol Agnostic**: Support multiple lending protocols (Aave, Morpho, etc.) |
| 31 | +5. **Public Visibility**: Allow anyone to query available token amounts per delegation |
| 32 | + |
| 33 | +## Solution Architecture |
| 34 | + |
| 35 | +The solution consists of three main components: |
| 36 | + |
| 37 | +### 1. TokenTransformationEnforcer |
| 38 | + |
| 39 | +A caveat enforcer that tracks multiple tokens per delegation hash. |
| 40 | + |
| 41 | +**Key Features:** |
| 42 | +- Maps `delegationHash => token => availableAmount` |
| 43 | +- Initializes from delegation terms on first use |
| 44 | +- Validates token usage in `beforeHook` |
| 45 | +- Updates state via `updateAssetState()` (only callable by AdapterManager) |
| 46 | +- Public view function: `getAvailableAmount(delegationHash, token)` |
| 47 | + |
| 48 | +**State Structure:** |
| 49 | +```solidity |
| 50 | +mapping(bytes32 delegationHash => mapping(address token => uint256 amount)) public availableAmounts; |
| 51 | +mapping(bytes32 delegationHash => bool initialized) public isInitialized; |
| 52 | +``` |
| 53 | + |
| 54 | +**Initialization:** |
| 55 | +- Terms encode: `20 bytes token address + 32 bytes initial amount` |
| 56 | +- On first use of initial token, amount is initialized from terms |
| 57 | +- Subsequent uses deduct from available amount |
| 58 | + |
| 59 | +### 2. AdapterManager |
| 60 | + |
| 61 | +Central coordinator that routes protocol interactions to specific adapters and updates enforcer state. |
| 62 | + |
| 63 | +**Key Responsibilities:** |
| 64 | +- Routes protocol calls to appropriate adapters via `protocolAdapters` mapping |
| 65 | +- Handles token approvals for protocol interactions |
| 66 | +- Measures token balances before/after protocol actions |
| 67 | +- Updates `TokenTransformationEnforcer` state after transformations |
| 68 | +- Transfers all tokens to root delegator (never holds tokens) |
| 69 | + |
| 70 | +**Flow:** |
| 71 | +1. Receives delegation request with protocol address and action |
| 72 | +2. Routes to appropriate adapter based on protocol address |
| 73 | +3. Adapter executes protocol interaction and measures transformations |
| 74 | +4. AdapterManager updates enforcer state: deducts `tokenFrom`, adds `tokenTo` |
| 75 | +5. Transfers output tokens to root delegator |
| 76 | + |
| 77 | +### 3. Protocol Adapters |
| 78 | + |
| 79 | +Protocol-specific adapters that handle interactions with lending protocols. |
| 80 | + |
| 81 | +**Current Adapters:** |
| 82 | +- **AaveAdapter**: Handles Aave V3 deposits/withdrawals |
| 83 | +- **MorphoAdapter**: Handles Morpho market interactions |
| 84 | + |
| 85 | +**Adapter Interface:** |
| 86 | +```solidity |
| 87 | +interface ILendingAdapter { |
| 88 | + struct TransformationInfo { |
| 89 | + address tokenFrom; |
| 90 | + uint256 amountFrom; |
| 91 | + address tokenTo; |
| 92 | + uint256 amountTo; |
| 93 | + } |
| 94 | + |
| 95 | + function executeProtocolAction( |
| 96 | + address _protocolAddress, |
| 97 | + string calldata _action, |
| 98 | + IERC20 _tokenFrom, |
| 99 | + uint256 _amountFrom, |
| 100 | + bytes calldata _actionData |
| 101 | + ) external returns (TransformationInfo memory); |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +**Adapter Responsibilities:** |
| 106 | +- Measure token balances before protocol interaction |
| 107 | +- Execute protocol function (deposit, withdraw, etc.) |
| 108 | +- Measure token balances after interaction |
| 109 | +- Return transformation information (tokenFrom, amountFrom, tokenTo, amountTo) |
| 110 | + |
| 111 | +## How It Works |
| 112 | + |
| 113 | +### Example Flow: Aave Deposit |
| 114 | + |
| 115 | +1. **Initial Delegation**: |
| 116 | + ``` |
| 117 | + User delegates 1000 USDC with TokenTransformationEnforcer |
| 118 | + Terms: [USDC address, 1000] |
| 119 | + ``` |
| 120 | + |
| 121 | +2. **Agent Initiates Deposit**: |
| 122 | + ``` |
| 123 | + Agent calls AdapterManager.executeProtocolActionByDelegation( |
| 124 | + protocol: Aave Pool, |
| 125 | + action: "deposit", |
| 126 | + tokenFrom: USDC, |
| 127 | + amountFrom: 500, |
| 128 | + delegations: [...] |
| 129 | + ) |
| 130 | + ``` |
| 131 | + |
| 132 | +3. **Delegation Redemption**: |
| 133 | + - DelegationManager validates delegations |
| 134 | + - TokenTransformationEnforcer.beforeHook() validates 500 USDC is available |
| 135 | + - Deducts 500 USDC from availableAmounts[delegationHash][USDC] |
| 136 | + - Transfers 500 USDC to AdapterManager |
| 137 | + |
| 138 | +4. **Protocol Interaction**: |
| 139 | + - AdapterManager approves Aave Pool |
| 140 | + - AaveAdapter measures aUSDC balance before |
| 141 | + - AaveAdapter calls Aave Pool.supply(USDC, 500, AdapterManager, 0) |
| 142 | + - AaveAdapter wraps aUSDC → wrapped aUSDC (non-rebasing) |
| 143 | + - AaveAdapter measures wrapped aUSDC balance after |
| 144 | + - Returns: tokenFrom=USDC, amountFrom=500, tokenTo=wrapped aUSDC, amountTo=500 |
| 145 | + |
| 146 | +5. **State Update**: |
| 147 | + - AdapterManager calls TokenTransformationEnforcer.updateAssetState( |
| 148 | + delegationHash, |
| 149 | + wrapped aUSDC, |
| 150 | + 500 |
| 151 | + ) |
| 152 | + - Enforcer state: availableAmounts[delegationHash][wrapped aUSDC] = 500 |
| 153 | + |
| 154 | +6. **Token Transfer**: |
| 155 | + - AdapterManager transfers wrapped aUSDC to root delegator |
| 156 | + - Final state: |
| 157 | + - availableAmounts[delegationHash][USDC] = 500 |
| 158 | + - availableAmounts[delegationHash][wrapped aUSDC] = 500 |
| 159 | + |
| 160 | +### Example Flow: Multiple Transformations |
| 161 | + |
| 162 | +**Initial**: 1000 USDC delegated |
| 163 | + |
| 164 | +**Step 1**: Deposit 500 USDC → Aave |
| 165 | +- Result: 500 USDC + 500 wrapped aUSDC tracked |
| 166 | + |
| 167 | +**Step 2**: Use 200 USDC → Swap → DAI |
| 168 | +- Result: 300 USDC + 500 wrapped aUSDC + 200 DAI tracked |
| 169 | + |
| 170 | +**Step 3**: Use 100 wrapped aUSDC → Withdraw → USDC |
| 171 | +- Result: 400 USDC + 400 wrapped aUSDC + 200 DAI tracked |
| 172 | + |
| 173 | +All tokens remain under delegation control until expiration or revocation. |
| 174 | + |
| 175 | +## Key Design Decisions |
| 176 | + |
| 177 | +### 1. Adapter Pattern |
| 178 | + |
| 179 | +**Why**: Different protocols have different interfaces and behaviors. Adapters encapsulate protocol-specific logic while maintaining a consistent interface. |
| 180 | + |
| 181 | +**Benefits**: |
| 182 | +- Easy to add new protocols (just implement ILendingAdapter) |
| 183 | +- Protocol-specific logic isolated from core system |
| 184 | +- Consistent transformation tracking across protocols |
| 185 | + |
| 186 | +### 2. AdapterManager as State Updater |
| 187 | + |
| 188 | +**Why**: Only AdapterManager can update enforcer state to prevent unauthorized state changes. |
| 189 | + |
| 190 | +**Security**: |
| 191 | +- Enforcer validates `msg.sender == adapterManager` in `updateAssetState()` |
| 192 | +- Ensures state updates only occur after verified protocol interactions |
| 193 | + |
| 194 | +### 3. Tokens Always Go to Root Delegator |
| 195 | + |
| 196 | +**Why**: Maintains clear ownership - tokens never stay in adapters or enforcer contracts. |
| 197 | + |
| 198 | +**Flow**: |
| 199 | +- Tokens flow: Root Delegator → AdapterManager → Protocol → AdapterManager → Root Delegator |
| 200 | +- Enforcer only tracks amounts, never holds tokens |
| 201 | + |
| 202 | +### 4. Balance Measurement in Adapters |
| 203 | + |
| 204 | +**Why**: Adapters know the expected output tokens and can measure accurately. |
| 205 | + |
| 206 | +**Implementation**: |
| 207 | +- Adapters measure balances before/after protocol interactions |
| 208 | +- Return actual transformation amounts |
| 209 | +- AdapterManager validates received amounts match reported amounts |
| 210 | + |
| 211 | +### 5. Wrapped Tokens for Rebasing Assets |
| 212 | + |
| 213 | +**Why**: Rebasing tokens (like aTokens) change balance over time, complicating tracking. |
| 214 | + |
| 215 | +**Solution**: |
| 216 | +- AaveAdapter wraps aTokens into non-rebasing wrapped tokens |
| 217 | +- Wrapped tokens have fixed supply, easier to track |
| 218 | +- TODO: Investigate using Aave's ATokenVault (ERC-4626) for direct wrapped token support |
| 219 | + |
| 220 | +## Public API |
| 221 | + |
| 222 | +### Query Available Amounts |
| 223 | + |
| 224 | +Anyone can query available token amounts for a delegation: |
| 225 | + |
| 226 | +```solidity |
| 227 | +uint256 available = tokenTransformationEnforcer.getAvailableAmount( |
| 228 | + delegationHash, |
| 229 | + tokenAddress |
| 230 | +); |
| 231 | +``` |
| 232 | + |
| 233 | +### Check Protocol Adapters |
| 234 | + |
| 235 | +```solidity |
| 236 | +address adapter = adapterManager.protocolAdapters(protocolAddress); |
| 237 | +``` |
| 238 | + |
| 239 | +## Security Considerations |
| 240 | + |
| 241 | +1. **State Updates**: Only AdapterManager can update enforcer state |
| 242 | +2. **Token Validation**: Enforcer validates all token transfers before execution |
| 243 | +3. **Balance Verification**: AdapterManager verifies received tokens match adapter reports |
| 244 | +4. **Ownership**: All tokens always belong to root delegator |
| 245 | +5. **Initialization Protection**: Initial amount only set once per delegationHash |
| 246 | + |
| 247 | +## Future Enhancements |
| 248 | + |
| 249 | +1. **ATokenVault Support**: Use Aave's native ATokenVault for direct wrapped token deposits/withdrawals |
| 250 | +2. **Additional Protocols**: Add adapters for more lending protocols |
| 251 | +3. **Borrowing Support**: Extend adapters to handle borrowing and repayment |
| 252 | +4. **Multi-Step Strategies**: Support complex multi-protocol strategies |
| 253 | +5. **Gas Optimization**: Optimize state updates and balance measurements |
| 254 | + |
| 255 | +## Files Structure |
| 256 | + |
| 257 | +``` |
| 258 | +src/ |
| 259 | +├── enforcers/ |
| 260 | +│ └── TokenTransformationEnforcer.sol # Tracks multi-token state per delegation |
| 261 | +├── helpers/ |
| 262 | +│ ├── adapters/ |
| 263 | +│ │ ├── AdapterManager.sol # Routes to adapters, updates state |
| 264 | +│ │ ├── AaveAdapter.sol # Aave V3 interactions |
| 265 | +│ │ └── MorphoAdapter.sol # Morpho interactions |
| 266 | +│ └── interfaces/ |
| 267 | +│ └── ILendingAdapter.sol # Adapter interface |
| 268 | +``` |
| 269 | + |
| 270 | +## Usage Example |
| 271 | + |
| 272 | +```solidity |
| 273 | +// 1. Create delegation with TokenTransformationEnforcer |
| 274 | +Delegation memory delegation = Delegation({ |
| 275 | + delegate: agentAddress, |
| 276 | + delegator: userAddress, |
| 277 | + authority: ROOT_AUTHORITY, |
| 278 | + caveats: [Caveat({ |
| 279 | + enforcer: tokenTransformationEnforcer, |
| 280 | + terms: abi.encodePacked(usdcAddress, 1000e6), // 1000 USDC |
| 281 | + args: hex"" |
| 282 | + })], |
| 283 | + salt: 0, |
| 284 | + signature: hex"" |
| 285 | +}); |
| 286 | +
|
| 287 | +// 2. Agent uses delegation to deposit to Aave |
| 288 | +adapterManager.executeProtocolActionByDelegation( |
| 289 | + aavePoolAddress, |
| 290 | + "deposit", |
| 291 | + usdcToken, |
| 292 | + 500e6, |
| 293 | + abi.encode(adapterManagerAddress), |
| 294 | + delegations |
| 295 | +); |
| 296 | +
|
| 297 | +// 3. Query available amounts |
| 298 | +uint256 usdcAvailable = tokenTransformationEnforcer.getAvailableAmount( |
| 299 | + delegationHash, |
| 300 | + usdcAddress |
| 301 | +); // Returns: 500e6 |
| 302 | +
|
| 303 | +uint256 wrappedAUsdcAvailable = tokenTransformationEnforcer.getAvailableAmount( |
| 304 | + delegationHash, |
| 305 | + wrappedAUsdcAddress |
| 306 | +); // Returns: 500e6 |
| 307 | +``` |
| 308 | + |
0 commit comments