Tenzro Testnet is live —request testnet TNZO
← Back to Tutorials

Base L2 Yield Strategy with Chainlink & ERC-8004

DeFiAdvanced35 min

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_call simulation
  • 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 feeds
  • eth_get_gas_price — current gas price
  • eth_get_fee_history — EIP-1559 fee history for trend analysis
  • eth_get_token_balance — ERC-20 token balance
  • eth_get_balance — native ETH balance
  • eth_encode_function — ABI encode function calls
  • eth_call_contract — simulate contract calls without gas
  • eth_register_agent_8004 — ERC-8004 on-chain agent registration
  • eth_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.0001

Step 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...9c1e

wTNZO 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...8004

Step 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:     confirmed

Step 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_yield
View 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

Next Steps