Earn Tokens (eTokens)
Overview
Earn Tokens (eTokens) are Curvance's lending market tokens that enable users to earn interest on deposited assets. eTokens function similarly to interest-bearing tokens in other lending protocols but with a focus on enhanced security and flexibility. Users who deposit underlying assets receive eTokens representing their share of the lending pool, which increase in value as borrowers pay interest.
Core Architecture
eTokens operate within a network of contracts:
EToken: The core implementation of the Earn Token functionality.
ETokenWithGauge: Extension that integrates with Curvance's Gauge system for additional rewards.
MarketManager: Manages risk parameters, collateral requirements, and market health.
CentralRegistry: Provides protocol-wide configuration and permissions.
InterestRateModel: Determines interest rates based on market utilization.
State and Data Model
eTokens maintain several key state variables:
// User balances
mapping(address => uint256) public balanceOf;
// Debt tracking
mapping(address => DebtData) internal _debtOf;
// Market state
MarketData public marketData;
uint256 public totalBorrows;
uint256 public totalReserves;
uint256 public totalSupply;
// Structures
// Data for a market.
struct MarketData {
uint40 lastTimestampUpdated;
uint216 exchangeRate;
uint256 compoundRate;
}
// Data for a user's debt.
struct DebtData {
uint256 principal;
uint256 accountExchangeRate;
}
The exchangeRate
is a critical parameter that determines the conversion between eTokens (shares) and underlying assets. It increases over time as interest accrues, allowing eToken holders to claim more underlying assets for the same number of tokens.
Data Flow
Deposit Flow
User calls
mint()
with underlying assets.Contract accrues any pending interest.
Contract calculates shares based on current exchange rate.
Underlying tokens are transferred from the user to the contract.
eTokens are minted to the user.
MarketManager is notified of the deposit (if needed).
Withdrawal Flow
User calls
redeem()
with eTokens.Contract accrues any pending interest.
Contract calculates assets based on current exchange rate.
Underlying tokens are transferred from the contract to the user.
eTokens are burned.
MarketManager is notified of the withdrawal.
Borrowing Flow
User must first deposit collateral into a pToken.
User calls
borrow()
on an eToken.Contract accrues any pending interest.
MarketManager checks if user has sufficient collateral.
Debt is recorded for the user.
Underlying tokens are transferred to the user.
Repayment Flow
User calls
repay()
orrepayFor()
.Contract accrues any pending interest.
Current debt is calculated.
Underlying tokens are transferred from the user to the contract.
User's debt is reduced or eliminated.
MarketManager is updated regarding the user's position.
Interest Accrual Mechanism
Interest accrual is a key feature of eTokens, handled through the accrueInterest()
function:
Time elapsed since last update is calculated.
InterestRateModel determines appropriate interest rate based on market utilization.
Interest is applied to total borrows.
A portion of interest (determined by
interestFactor
) is allocated to protocol reserves.Exchange rate is updated, increasing the value of all eTokens.
This process occurs automatically when users interact with any of the main functions, ensuring up-to-date state before transactions execute.
Exchange Rate Calculation
The exchange rate is calculated by:
exchangeRate = (marketUnderlyingHeld + totalBorrows) * WAD / (totalSupply + totalReserves)
This rate determines how many underlying tokens each eToken is worth and increases over time as interest accrues.
Integration with Market Manager
eTokens work closely with the MarketManager, which:
Defines which assets can be used as collateral (pTokens).
Sets borrowing parameters like collateral factor and liquidation threshold.
Authorizes borrowing based on user's collateral.
Manages the liquidation process for underwater positions.
Enforces market-wide caps and security measures.
Safety Features
eTokens incorporate multiple security features:
Reentrancy protection on all critical functions.
Safe variants of functions for external protocol integration.
Accrual of interest before any state-changing operations.
Minimum hold periods to prevent flash loan attacks.
Multiple layer validation for borrowing and collateralization.
Protocol Revenue
The eToken contracts generate protocol revenue through:
Interest Factor: A portion of all interest paid by borrowers goes to protocol reserves.
These reserves can be withdrawn by the DAO using
processWithdrawReserves()
.For eTokens with gauges, additional rewards may be distributed to depositors based on their contribution.
User Interaction Functions
Share/Assets
convertToShares()
Contract: eToken
Description: Converts an amount of underlying assets to equivalent eToken shares using the current exchange rate.
Function signature:
function convertToShares(uint256 amount) public view returns (uint256)
uint256
amount
The number of underlying tokens to theoretically convert to eTokens
Return data:
uint256
The number of eTokens a user would receive for the given amount of underlying
convertToAssets()
Contract: eToken
Description: Converts an amount of eToken shares to equivalent underlying assets using the current exchange rate.
Function signature:
function convertToAssets(uint256 tokens) public view returns (uint256)
uint256
tokens
The number of eToken shares to theoretically convert to underlying assets
Return data:
uint256
The number of underlying assets the user would receive for redeeming shares
Mint Functions (Depositing Assets)
mint()
Contract: eToken
Description: Users deposit underlying assets into the market and receive eTokens in return.
Function signature:
function mint(uint256 amount) external returns (uint256)
uint256
amount
The amount of the underlying asset to deposit
Return Data:
uint256
The amount of eTokens minted
Events:
// From EToken
// Emitted from EToken and underlying asset.
event Transfer(address indexed from, address indexed to, uint256 value);
// Only emitted if interest needs to be approved.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
mintfor()
Contract: eToken
Description: Deposits underlying assets into the market, and recipient receives eTokens.
Function signature:
function mintFor(uint256 amount, address recipient) external returns (uint256)
uint256
amount
The amount of the underlying asset to deposit
address
recipient
The account that should receive the eTokens
Return data:
uint256
The amount of eTokens minted
Events:
// From EToken
// Emitted from EToken and underlying asset.
event Transfer(address indexed from, address indexed to, uint256 value);
// Only emitted if interest needs to be approved.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
Redeem Functions (Withdrawing Assets)
redeem()
Contract: eToken
Description: Redeems eTokens in exchange for the underlying asset
Function signature:
function redeem(uint256 tokens, address recipient) external returns (uint256)
uint256
tokens
The number of eTokens to redeem for underlying tokens
address
recipient
The account who will receive the underlying assets
Return data:
uint256
The amount of underlying asset redeemed
Events:
// From EToken
// Emitted from EToken and underlying asset.
event Transfer(address indexed from, address indexed to, uint256 value);
// Only emitted if interest needs to be approved.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// From MarketManager
// Only emitted if positions need to be closed
event PositionAdjusted(address mToken, address account, bool open);
redeemFor()
Contract: eToken
Description: Used by a delegated user to redeem eTokens in exchange for the underlying asset, on behalf of account.
Function signature:
function redeemFor(
uint256 tokens,
address recipient,
address account) external returns (uint256)
Return data:
uint256
The amount of underlying asset redeemed
Events:
// From EToken
// Emitted from EToken and underlying asset.
event Transfer(address indexed from, address indexed to, uint256 value);
// Only emitted if interest needs to be approved.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// From MarketManager
// Only emitted if positions need to be closed
event PositionAdjusted(address mToken, address account, bool open);
Borrow Functions
borrow()
Contract: eToken
Description: Borrows underlying tokens from lenders, based on collateral posted inside this market.
Function signature:
function borrow(uint256 amount) external
uint256
amount
The amount of the underlying asset to borrow
Event:
// From EToken
event Borrow(address account, uint256 amount);
// Emitted from EToken and underlying ERC20 asset.
event Transfer(address indexed from, address indexed to, uint256 value);
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// From MarketManager
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// Potentially emitted multiple times, if this is the first time borrowing this eToken,
// and for any positions that need to be closed after borrowing.
event PositionAdjusted(address mToken, address account, bool open);
borrowFor()
Contract: eToken
Description: Used by a delegated user to borrow underlying tokens from lenders, based on collateral posted inside this market by account
Function signature:
function borrowFor(
address account,
address recipient,
uint256 amount
) external nonReentrant
address
account
The account who will have their assets borrowed against
address
recipient
The account who will receive the borrowed assets
uint256
amount
The amount of the underlying asset to borrow
Event:
// From EToken
event Borrow(address account, uint256 amount);
// Emitted from EToken and underlying ERC20 asset.
event Transfer(address indexed from, address indexed to, uint256 value);
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// From MarketManager
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// Potentially emitted multiple times, if this is the first time borrowing this eToken,
// and for any positions that need to be closed after borrowing.
event PositionAdjusted(address mToken, address account, bool open);
Repay Functions
repay()
Contract: eToken
Description: Repays underlying tokens to lenders, freeing up their collateral posted inside this market and updates interest before executing the repayment.
Function signature:
function repay(uint256 amount) external nonReentrant
uint256
amount
The amount of the underlying asset to repay, or 0 for the full outstanding amount
Event:
// From EToken
event Repay(address payer, address account, uint256 amount);
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// Emitted from underlying ERC20 asset transfer.
event Transfer(address indexed from, address indexed to, uint256 value);
// From MarketManager
// Potentially emitted if a position is fully closed.
event PositionAdjusted(address mToken, address account, bool open);
repayFor()
Contract: eToken
Description: Repays underlying tokens to lenders, on behalf of account
, freeing up their collateral posted inside this market.
Function signature:
function repayFor(address account, uint256 amount) external nonReentrant
address
account
The account address to repay on behalf of.
uint256
amount
The amount to repay, or 0 for the full outstanding amount.
Event:
// From EToken
event Repay(address payer, address account, uint256 amount);
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
// Emitted from underlying ERC20 asset transfer.
event Transfer(address indexed from, address indexed to, uint256 value);
// From MarketManager
// Potentially emitted if a position is fully closed.
event PositionAdjusted(address mToken, address account, bool open);
Interest Accrual Mechanism
Interest is accrued on borrowed assets and distributed to eToken holders through the increasing exchange rate. The accrual process is managed by the accrueInterest
function.
accrueInterest()
Contract: eToken
Function signature:
function accrueInterest() public
This function:
Calculates time elapsed since the last interest accrual.
Fetches the current interest rate from the interest rate model.
Computes the interest amount based on current total borrows.
Updates total borrows with the new interest.
Allocates a portion of interest to protocol reserves based on the interestFactor.
Updates the exchange rate to reflect the new value of each eToken.
Interest accrues automaically when users interact with the protocol (mint, redeem, borrow, repay) as these functions call accrueInterest
internally.
Events:
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
Exchange Rate Calculation
exchangeRateWithUpdate()
Contract: eToken
Description: Returns the current exchange rate after updating interest, used to determine how many underlying tokens each eToken is worth:
Function signature:
function exchangeRateWithUpdate() external returns (uint256)
Return data:
uint256
Calculated exchange rate, in WAD
.
Events:
// Only emitted if interest needs to be accrued.
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
Safe Functions for External Integration
Curvance provides safe versions of key functions with additional reentrancy protection. These should be used when integrating with external protocols to minimize security risks.
exchangeRateWithUpdateSafe()
Contract: eToken
Description: Updates pending interest and returns the up-to-date exchange rate from the underlying to the eToken, with inherent reentrancy protection.
Function signature:
function exchangeRateWithUpdateSafe() public nonReentrant returns (uint256)
Return data:
uint256
Calculated exchange rate, in WAD
.
Event:
// Only emitted if enough time has passed since the last interest accrual
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
balanceOfUnderlyingSafe()
Contract: eToken
Description: Updates pending interest and returns the up-to-date balance of account
, in underlying assets, with inherent reentrancy protection.
Function signature:
function balanceOfUnderlyingSafe(
address account
) external returns (uint256) {
Return data:
uint256
The amount of underlying owned by account
.
Event:
// Only emitted if enough time has passed since the last interest accrual
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
debtBalanceWithUpdateSafe()
Contract: eToken
Description: Updates pending interest and returns the current debt balance for account
with inherent reentrancy protection.
Function signature:
function debtBalanceWithUpdateSafe(
address account
) external nonReentrant returns (uint256)
Return data:
uint256
The current balance index of account
, with pending interest applied.
Event:
// Only emitted if enough time has passed since the last interest accrual
event InterestAccrued(
uint256 debtAccumulated,
uint256 exchangeRate,
uint256 totalBorrows
);
Debt Tracking
debtBalanceCached()
Contract: eToken
Description: Returns the current debt balance for a given account without accruing interest. This is a gas-efficient view function that uses the cached exchange rate.
Function signature:
function debtBalanceCached(address account) public view returns (uint256)
address
account
The address whose debt balance should be calculated
Return data:
uint256
The current balance of debt for the account
debtBalanceAtTimestamp()
Contract: eToken
Description: Returns the estimated future debt balance for an account at a specific timestamp, assuming interest rates remain constant.
Function signature:
function debtBalanceAtTimestamp(
address account,
uint256 timestamp) public view returns (uint256)
address
account
The address whose debt balance should be calculated
uint256
timestamp
The unix timestamp to calculate the account's debt balance with
Return data:
uint256
The estimated debt balance at the specified timestamp
Market Utilization and Rates
getBorrowRatePerYear()
Contract: eToken
Description: Returns the current yearly borrow interest rate for the market, calculated from the interest rate model.
Function signature:
function getBorrowRatePerYear() public view returns (uint256)
Return data:
uint256
The borrow interest rate per year, in WAD format (1e18)
getSupplyRatePerYear()
Contract: eToken
Description: Returns the current yearly supply interest rate for lenders, derived from the borrow rate and adjusted by the interest factor.
Function signature:
function getSupplyRatePerYear() public view returns (uint256)
Return data:
uint256
The supply interest rate per year, in WAD format (1e18)
Last updated