Base L2 Yield Strategy with Chainlink & ERC-8004
Build an autonomous yield-farming agent on Base (Ethereum L2) using the Tenzro Rust SDK and the Ethereum MCP tools. The agent queries Chainlink price feeds for real-time market data, monitors gas for optimal entry timing, encodes ERC-4626 vault deposits, registers itself on-chain via ERC-8004, and settles yield profits back to the Tenzro Ledger.
What You'll Build
- A TDIP identity and MPC wallet via the Rust SDK
- An autonomous yield agent registered with Base DeFi capabilities
- Chainlink AggregatorV3 price feed queries (ETH/USD, USDC/USD) on Base
- Gas price monitoring with EIP-1559 fee history analysis
- ERC-20 token balance checks (USDC on Base)
- ABI-encoded ERC-4626 vault deposit with
eth_callsimulation - On-chain agent registration via ERC-8004 (Autonomous Agent Registry)
- EAS attestation for provable yield records
- Settlement of profits on the Tenzro Ledger
Ethereum MCP Tools Used
This tutorial uses 9 of the 16 tools available on the Ethereum MCP server (port 3004). Base is supported as an Ethereum L2 by passing chain_id=8453:
eth_get_price— Chainlink AggregatorV3 price feedseth_get_gas_price— current gas priceeth_get_fee_history— EIP-1559 fee history for trend analysiseth_get_token_balance— ERC-20 token balanceeth_get_balance— native ETH balanceeth_encode_function— ABI encode function callseth_call_contract— simulate contract calls without gaseth_register_agent_8004— ERC-8004 on-chain agent registrationeth_get_attestation— EAS attestation queries
Prerequisites
Add tenzro-sdk to your Cargo.toml:
[dependencies]
tenzro-sdk = "0.1"
tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.3"
uuid = { version = "1", features = ["v4"] }Step 1: Connect to the Network
use tenzro_sdk::{TenzroClient, SettlementRequest, config::SdkConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = SdkConfig::testnet();
let client = TenzroClient::connect(config).await?;Step 2: Create Identity & Wallet
Register a human identity via TDIP and create an MPC wallet. The wallet is auto-provisioned with a 2-of-3 threshold.
// Register a human identity via TDIP
let identity = client.identity().register_human("Base DeFi Strategist").await?;
println!("DID: {}", identity.did);
// Create an MPC wallet
let wallet = client.wallet().create_wallet().await?;
println!("Wallet: {}", wallet.address);Step 3: Register the Yield Agent
Register an agent with capabilities that tag it as a Base DeFi yield strategy:
// Register the yield agent with Base DeFi capabilities
let agent = client.agent().register(
"base-yield-agent",
"Base Yield Strategist",
&["defi", "yield", "base"],
).await?;
println!("Agent ID: {}", agent.agent_id);Step 4: Query Chainlink Price Feeds
The agent uses the eth_get_price MCP tool to query Chainlink AggregatorV3 price feeds on Base. This is the same infrastructure used by Aave, Compound, and other DeFi protocols for oracle data:
// Query Chainlink ETH/USD price feed on Base (chain_id 8453)
let eth_price = client.agent().send_message(
&agent.agent_id,
"Use eth_get_price tool: pair=ETH/USD, chain_id=8453",
).await?;
println!("ETH/USD: {}", eth_price.payload);
// Verify USDC peg
let usdc_price = client.agent().send_message(
&agent.agent_id,
"Use eth_get_price tool: pair=USDC/USD, chain_id=8453",
).await?;
println!("USDC/USD: {}", usdc_price.payload);ETH/USD: $2,847.32
USDC/USD: $1.0001Step 5: Monitor Gas for Optimal Entry
Gas on Base is typically very low (<0.02 gwei), but monitoring the trend helps the agent time its vault deposits during quiet periods:
// Check current gas prices on Base
let gas = client.agent().send_message(
&agent.agent_id,
"Use eth_get_gas_price tool: chain_id=8453",
).await?;
println!("Gas price: {}", gas.payload);
// Get fee history for trend analysis
let fee_history = client.agent().send_message(
&agent.agent_id,
"Use eth_get_fee_history tool: block_count=5, chain_id=8453",
).await?;
println!("Fee trend: {}", fee_history.payload);Gas price: 0.012 gwei
Fee trend: [0.010, 0.011, 0.012, 0.011, 0.012] gwei (stable, good entry window)Step 6: Check Token Balances
Before depositing, the agent checks both USDC and ETH balances on Base. The USDC contract address on Base is 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913:
// Check USDC balance on Base
let usdc_balance = client.agent().send_message(
&agent.agent_id,
&format!(
"Use eth_get_token_balance tool: address={}, \
token_address=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, \
chain_id=8453",
wallet.address,
),
).await?;
println!("USDC balance: {}", usdc_balance.payload);
// Check native ETH balance
let eth_bal = client.agent().send_message(
&agent.agent_id,
&format!("Use eth_get_balance tool: address={}, chain_id=8453", wallet.address),
).await?;
println!("ETH balance: {}", eth_bal.payload);Step 7: Encode & Simulate Vault Deposit
The agent ABI-encodes an ERC-4626 deposit(uint256,address) call, then simulates it with eth_call to verify it would succeed before spending gas:
// Encode an ERC-4626 vault deposit(uint256 assets, address receiver)
let deposit_calldata = client.agent().send_message(
&agent.agent_id,
&format!(
"Use eth_encode_function tool: \
function_signature=deposit(uint256,address), \
args=[\"1000000\", \"{}\"]",
wallet.address,
),
).await?;
println!("Calldata: {}", deposit_calldata.payload);Calldata: 0x6e553f65000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000...// Simulate the deposit via eth_call (no gas spent)
let sim_result = client.agent().send_message(
&agent.agent_id,
&format!(
"Use eth_call_contract tool: \
to=0xVaultContractAddress, \
data={}, from={}, chain_id=8453",
deposit_calldata.payload, wallet.address,
),
).await?;
println!("Simulation: {}", sim_result.payload);Simulation: success — 1000000 shares minted to 0x7f3a...9c1ewTNZO ERC-20 Pointer
On EVM chains like Base, TNZO is accessible as wTNZO — an ERC-20 pointer contract that shares the same underlying balance as native TNZO on the Tenzro Ledger. This follows the Sei V2 pointer model: no wrapping step is needed, and EVM contracts (including ERC-4626 vaults) interact with TNZO through the wTNZO pointer at a known address. Balances update atomically across VMs.
Step 8: Register Agent On-Chain via ERC-8004
ERC-8004 is the Autonomous Agent Registry standard. Registering makes the agent discoverable on-chain and verifiable by other agents and protocols:
// Register agent on-chain via ERC-8004 (Autonomous Agent Registry)
let erc8004 = client.agent().send_message(
&agent.agent_id,
&format!(
"Use eth_register_agent_8004 tool: \
agent_address={}, metadata_uri=ipfs://QmYieldStrategyBase",
wallet.address,
),
).await?;
println!("ERC-8004 registration: {}", erc8004.payload);ERC-8004 registration: tx 0xab12...ef34 confirmed — agent registered at registry 0x00...8004Step 9: Settle Profits on Tenzro Ledger
Settlement finalizes the yield profits on the Tenzro Ledger. The 0.5% network fee is deducted automatically:
// Settle yield profits on Tenzro Ledger
let settlement = client.settlement().settle(SettlementRequest {
request_id: format!("base-yield-{}", uuid::Uuid::new_v4()),
provider: wallet.address.clone(),
customer: "0x0000000000000000000000000000000000000000".to_string(),
amount: 1000,
asset: "TNZO".to_string(),
}).await?;
println!("Settlement: {}", settlement.receipt_id);
println!("Status: {}", settlement.status);Note: The asset: "TNZO" field refers to the native ledger asset. Cross-VM settlement is handled transparently via the pointer model — whether the yield was earned through wTNZO on EVM, SPL-wrapped TNZO on SVM, or CIP-56 holdings on Canton, settlement resolves to the same native balance.
Settlement: receipt-a1b2c3d4-e5f6-7890-abcd-ef1234567890
Status: confirmedStep 10: Create an EAS Attestation
The Ethereum Attestation Service (EAS) on Base provides a tamper-proof record of the yield event. This attestation can be verified by other agents or auditors:
// Query EAS attestation for yield record
let attestation = client.agent().send_message(
&agent.agent_id,
"Use eth_get_attestation tool: \
uid=0x000...001, chain_id=8453",
).await?;
println!("Attestation: {}", attestation.payload);Full Example
The complete example is available at sdk/tenzro-sdk/examples/defi_base_yield.rs. Run it with:
cargo run --example defi_base_yieldView full source
use tenzro_sdk::{TenzroClient, SettlementRequest, config::SdkConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let config = SdkConfig::testnet();
let client = TenzroClient::connect(config).await?;
// 1. Identity & Wallet
let identity = client.identity().register_human("Base DeFi Strategist").await?;
let wallet = client.wallet().create_wallet().await?;
// 2. Register agent
let agent = client.agent().register(
"base-yield-agent", "Base Yield Strategist",
&["defi", "yield", "base"],
).await?;
// 3. Check Chainlink prices
let eth_price = client.agent().send_message(
&agent.agent_id, "Use eth_get_price tool: pair=ETH/USD, chain_id=8453",
).await?;
// 4. Monitor gas
let gas = client.agent().send_message(
&agent.agent_id, "Use eth_get_gas_price tool: chain_id=8453",
).await?;
// 5. Check balances
let usdc_balance = client.agent().send_message(
&agent.agent_id,
&format!("Use eth_get_token_balance tool: address={}, \
token_address=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, \
chain_id=8453", wallet.address),
).await?;
// 6. Encode & simulate vault deposit
let calldata = client.agent().send_message(
&agent.agent_id,
&format!("Use eth_encode_function tool: \
function_signature=deposit(uint256,address), \
args=[\"1000000\", \"{}\"]", wallet.address),
).await?;
// 7. Register via ERC-8004
let erc8004 = client.agent().send_message(
&agent.agent_id,
&format!("Use eth_register_agent_8004 tool: \
agent_address={}, metadata_uri=ipfs://QmYieldStrategyBase",
wallet.address),
).await?;
// 8. Settle on Tenzro Ledger
let settlement = client.settlement().settle(SettlementRequest {
request_id: format!("base-yield-{}", uuid::Uuid::new_v4()),
provider: wallet.address.clone(),
customer: "0x0000000000000000000000000000000000000000".to_string(),
amount: 1000,
asset: "TNZO".to_string(),
}).await?;
println!("Done. Settlement: {}", settlement.receipt_id);
Ok(())
}What You Learned
- Chainlink price feeds — querying AggregatorV3 contracts on Base via
eth_get_price - Gas monitoring — using
eth_get_fee_historyto time DeFi entries on L2s - ABI encoding — constructing ERC-4626 vault deposit calldata with
eth_encode_function - Simulation — dry-running contract calls with
eth_call_contractbefore spending gas - ERC-8004 — on-chain agent registration for discoverability and verifiability
- EAS — creating tamper-proof attestations for provable yield records
- Settlement — finalizing profits on the Tenzro Ledger with the 0.5% network fee
- Cross-VM tokens — how wTNZO ERC-20 pointer provides TNZO access on EVM chains via the Sei V2 model
Next Steps
- See the Solana DEX Aggregation tutorial for Jupiter swap routing on Solana
- See the Canton DvP Settlement tutorial for institutional RWA tokenization
- Read the Yield Router tutorial for cross-chain yield comparison with bridge fee optimization