Skip to content

Commit fb03cbe

Browse files
committed
feat: Add token transformation tracking system for DeFi protocol interactions
- Add TokenTransformationEnforcer to track multiple tokens per delegation - Add AdapterManager to coordinate protocol adapters and update enforcer state - Add AaveAdapter and MorphoAdapter for lending protocol interactions - Add ILendingAdapter interface for protocol adapters - Add comprehensive documentation explaining the system This enables delegations to track token transformations through DeFi protocols, allowing AI agents to use delegated tokens in lending protocols while maintaining granular control over evolving token positions.
1 parent cad7320 commit fb03cbe

File tree

6 files changed

+1308
-0
lines changed

6 files changed

+1308
-0
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
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

Comments
 (0)