DeFi Protocol Guide¶
This guide covers creating MCP servers for complex DeFi protocols like Uniswap, Aave, and Compound. These protocols have:
- Multiple contracts
- Complex parameters (structs, arrays)
- Payable functions
- Time-sensitive operations
Prerequisites¶
- abi-to-mcp installed
- Good understanding of Core Concepts
- RPC endpoint with archive access (recommended)
Example: Uniswap V3 Router¶
Uniswap's SwapRouter is one of the most used DeFi contracts:
| Property | Value |
|---|---|
| Contract | SwapRouter |
| Address | 0xE592427A0AEce92De3Edee1F18E0157C05861564 |
| Network | Ethereum Mainnet |
Generate the Server¶
abi-to-mcp generate 0xE592427A0AEce92De3Edee1F18E0157C05861564 \
--network mainnet \
--output ./uniswap-router \
--name "Uniswap V3 Router"
Key Functions¶
| Function | Purpose | Parameters |
|---|---|---|
exactInputSingle |
Swap exact input for output | ExactInputSingleParams struct |
exactOutputSingle |
Swap for exact output | ExactOutputSingleParams struct |
exactInput |
Multi-hop exact input | Encoded path |
exactOutput |
Multi-hop exact output | Encoded path |
Struct Parameters¶
Uniswap uses complex struct parameters:
# ExactInputSingleParams
{
"tokenIn": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", # WETH
"tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", # USDC
"fee": "3000", # 0.3% fee tier
"recipient": "0x...", # Your address
"deadline": "1699999999", # Unix timestamp
"amountIn": "1000000000000000000", # 1 WETH
"amountOutMinimum": "1800000000", # Min 1800 USDC
"sqrtPriceLimitX96": "0" # No price limit
}
Fee Tiers¶
Uniswap V3 has multiple fee tiers:
| Fee | Value | Best For |
|---|---|---|
| 0.01% | 100 |
Stable pairs (USDC/USDT) |
| 0.05% | 500 |
Stable pairs |
| 0.3% | 3000 |
Most pairs |
| 1% | 10000 |
Exotic pairs |
Example: Aave V3 Pool¶
Aave's lending pool enables deposits, borrows, and repayments:
| Property | Value |
|---|---|
| Contract | Pool |
| Address | 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2 |
| Network | Ethereum Mainnet |
Generate the Server¶
abi-to-mcp generate 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2 \
--network mainnet \
--output ./aave-pool \
--name "Aave V3 Pool"
Key Functions¶
| Function | Type | Purpose |
|---|---|---|
supply |
Write | Deposit assets |
withdraw |
Write | Withdraw assets |
borrow |
Write | Borrow assets |
repay |
Write | Repay debt |
getUserAccountData |
Read | Get user health factor |
getReserveData |
Read | Get asset info |
Reading User Data¶
# Get user's account summary
data = await get_user_account_data(user="0x...")
# Returns:
{
"totalCollateralBase": "10000000000", # In base currency (USD, 8 decimals)
"totalDebtBase": "5000000000",
"availableBorrowsBase": "2500000000",
"currentLiquidationThreshold": "8250", # 82.5%
"ltv": "8000", # 80%
"healthFactor": "1650000000000000000" # 1.65
}
Health Factor
If healthFactor falls below 1000000000000000000 (1.0), the position can be liquidated!
Working with Multiple Contracts¶
DeFi protocols often require multiple contracts:
Uniswap Complete Setup¶
# Router for swaps
abi-to-mcp generate 0xE592427A0AEce92De3Edee1F18E0157C05861564 \
-o ./uniswap/router -n mainnet
# Quoter for quotes (read-only)
abi-to-mcp generate 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6 \
-o ./uniswap/quoter -n mainnet --read-only
# Factory for pool lookups
abi-to-mcp generate 0x1F98431c8aD98523631AE4a59f267346ea31F984 \
-o ./uniswap/factory -n mainnet --read-only
Combined Server¶
You can combine multiple contracts into one server by editing the generated code:
# server.py
from uniswap.router.server import router_tools
from uniswap.quoter.server import quoter_tools
from uniswap.factory.server import factory_tools
# Register all tools
for tool in router_tools + quoter_tools + factory_tools:
mcp.register_tool(tool)
Token Approvals¶
Most DeFi operations require token approvals first:
sequenceDiagram
participant User
participant Token
participant Protocol
User->>Token: approve(protocol, amount)
Token-->>User: ✓ Approved
User->>Protocol: deposit(amount)
Protocol->>Token: transferFrom(user, protocol, amount)
Token-->>Protocol: ✓ Transferred
Protocol-->>User: ✓ Deposited
Check Allowance¶
# Check current allowance
allowance = await token_allowance(
owner="0x...",
spender="0x...protocol..."
)
if int(allowance) < amount_needed:
# Need to approve first
await token_approve(
spender="0x...protocol...",
amount="115792089237316195423570985008687907853269984665640564039457584007913129639935", # Max uint256
simulate=True
)
Max Approval
Many users approve the maximum uint256 value to avoid repeated approvals. This is convenient but less secure.
Handling Deadlines¶
Time-sensitive DeFi operations include deadlines:
import time
# Set deadline 20 minutes from now
deadline = str(int(time.time()) + 20 * 60)
result = await exact_input_single(
params={
"tokenIn": "0x...",
"tokenOut": "0x...",
"fee": "3000",
"recipient": "0x...",
"deadline": deadline, # Important!
"amountIn": "1000000000000000000",
"amountOutMinimum": "0",
"sqrtPriceLimitX96": "0"
},
simulate=True
)
Expired Deadlines
If the deadline passes before the transaction is mined, it will revert. Set reasonable deadlines based on network congestion.
Slippage Protection¶
Always set minimum output amounts:
# Get quote first
quote = await quote_exact_input_single(
tokenIn="0x...",
tokenOut="0x...",
fee="3000",
amountIn="1000000000000000000",
sqrtPriceLimitX96="0"
)
# Calculate minimum with 0.5% slippage
amount_out = int(quote)
slippage = 0.005
amount_out_minimum = str(int(amount_out * (1 - slippage)))
# Execute swap with protection
result = await exact_input_single(
params={
# ...
"amountOutMinimum": amount_out_minimum
}
)
Flash Loans¶
Some protocols support flash loans (borrow + repay in one transaction). These require custom contract interactions beyond basic MCP tools.
Advanced Feature
Flash loans typically require writing custom smart contracts to execute the borrowed funds logic.
Gas Optimization¶
DeFi transactions can be gas-intensive:
| Operation | Typical Gas |
|---|---|
| Token approval | 45,000 |
| Simple swap | 150,000 |
| Multi-hop swap | 250,000+ |
| Aave supply | 200,000 |
| Aave borrow | 300,000 |
Tips: - Batch operations when possible - Time transactions for low gas periods - Use gas estimation before executing
# Always simulate first to get gas estimate
result = await exact_input_single(params={...}, simulate=True)
print(f"Estimated gas: {result['gas_estimate']}")
print(f"Estimated cost: {result['estimated_cost_eth']} ETH")
Common Errors¶
"INSUFFICIENT_OUTPUT_AMOUNT"¶
Slippage too high or pool price moved. Solutions:
- Increase amountOutMinimum tolerance
- Use smaller trade sizes
- Try different fee tiers
"EXPIRED"¶
Transaction wasn't mined before deadline: - Increase deadline - Increase gas price - Check network congestion
"STF" (Safe Transfer Failed)¶
Token transfer failed. Check: - Sufficient balance - Sufficient allowance - Token isn't paused
"UNPREDICTABLE_GAS_LIMIT"¶
Can't estimate gas (transaction would fail): - Simulation will help identify the issue - Check all parameters are valid - Verify contract state
Next Steps¶
- Custom Networks - DeFi on L2s
- Safety Features - Protect your funds
- Claude Desktop - Use with Claude
Quick Start Example
A ready-to-use DeFi router example is available in examples/defi_protocol/.