YieldToken
contracts/src/agents/YieldToken.sol
Transferable ERC-20 that represents a permanent yield share in an agent. Holders receive USDC proportional to their balance whenever AgentToken.harvest() is called.
Why a separate token?
Tracking yield share as an internal mapping inside AgentToken creates a composability problem: if a contract burns AT to earn yield, and that contract is later upgraded (new address), the yield is permanently stuck at the old address.
YieldToken is a standard ERC-20 — it can be transferred to a new contract, traded on secondary markets, or used in other DeFi protocols. The yield share moves with whoever holds the token.
Minting
YieldToken is minted exclusively by AgentToken:
AgentToken.burnForYield(amount)→ mintsamountYT to caller.AgentToken.burnForYieldTo(amount, recipient)→ mintsamountYT torecipient.
The total YT supply equals the total AT ever burned for yield. YT can never be minted without burning AT first.
Yield distribution (MasterChef accumulator)
uint256 public accUsdcPerShare; // grows with each receiveYield() call
// When receiveYield(amount) is called:
accUsdcPerShare += amount * PRECISION / totalSupply();
// User's pending USDC:
pending = balance * (accUsdcPerShare - userAccUsdcPerShare[user]) / PRECISIONreceiveYield(uint256 amount)
Called by AgentToken after claiming USDC from Pool. USDC must already be in the YieldToken contract before this is called. Updates the accumulator proportionally to total YT supply.
// Only AgentToken can call this
function receiveYield(uint256 amount) external onlyAgentTokenclaim() → uint256
Settles the accumulator for msg.sender and transfers accumulated USDC.
uint256 earned = yieldToken.claim();pendingEarnings(address user) → uint256
View function — returns unsettled USDC including the current accumulator delta.
Settlement before transfers
_update(from, to, amount) is overridden to settle both from and to before any balance change. This ensures the accumulator is always correct regardless of when transfers happen:
Before any mint / burn / transfer:
_settle(from) → pendingUsdc[from] += balance[from] * delta / PRECISION
_settle(to) → pendingUsdc[to] += balance[to] * delta / PRECISION
userAccUsdcPerShare[user] = accUsdcPerShare (for both)Transferability and composability
Because YieldToken is a standard ERC-20:
- It can be listed and traded — "buy exposure to an agent's yield stream."
- A DAO can pool AT burns across many members, hold the YT collectively, and distribute USDC to DAO members by whatever rules they choose.
- An aggregator can hold YT across multiple agents and batch
claim()calls.