Yearn Lens
Yearn Lens is a series of smart contracts that aggregate and format Yearn family protocol data into standardized interfaces.
Architecture
Block Diagram
Key Concepts
- Lens
- The lens contract acts as the primary data aggregator
- Registry adapters can be added or removed from the lens contract
- The lens contract supports many of the same methods as the registry adapters, but returns aggregated views across asset types
- Registry Adapters
- The purpose of registry adapters is to read data from various registries and return data in a standardized format
- Currently registry adapters exist for:
- v1 Vaults
- v2 Vaults
- Earn
- Iron Bank
- veCrv
- All registry adapters must implement a standardized set of methods (details below)
- Registry adapters have the ability to return metadata specific to an asset type (for example for vaults:
pricePerShare
,controller
, etc.)
- Oracle
- The oracle contract is responsible for fetching price information from various sources
- The oracle is intended for non-critical off-chain calculations
- TVL calculations
- Token and asset balance normalizations
- Not intended to be used with strategies or other contracts
- Prices are returned in USDC
- Supports adding, removing and upgrading price calculations
- Currently supported calculations:
- Sushiswap market price (based on
getAmountsOut
) - Uniswap market price (based on
getAmountsOut
) - Sushiswap/Uniswap LP token prices (based on
getReserves
) - Iron Bank market price (based on
exchangeRateStored
) - Curve LP token price (based on
virtualPrice
and base underlying token price)
- Sushiswap market price (based on
- Currently supported calculations:
- The oracle contract itself is very lightweight
- A cascading fallback mechanism is utilized to fetch the most appropriate price for a given asset
- This means the majority of logic lives on calculation contracts
- This also means the oracle contract gets direct access to all underlying calculation contract helper methods
- A cascading fallback mechanism is utilized to fetch the most appropriate price for a given asset
Key Features
- Obtain all Yearn family asset data in a standardized interface
- Obtain all user asset and token balances/allowances
- Ability to return USDC normalized balances as well as base asset balances
- Obtain various TVL calculations for Yearn
- Total TVL
- TVL per asset type
- TVL per asset
- Integration with all Yearn family protocols
- v1/v2 vaults
- Iron Bank
- Earn
- VeCrv (base contract and pJar)
Contracts
Registry Adapters
Schema
Standardized Interfaces
All registry adapters are required to implement the following interfaces.
AdapterMetadata
struct AdapterMetadata {
string typeId;
string categoryId;
string subcategoryId;
}
Asset
struct Asset {
address id;
string typeId;
string name;
string version;
uint256 balance;
uint256 balanceUsdc; // Asset TVL == balance * token.priceUsdc
Token token;
AssetMetadata metadata;
}
AssetStatic (static) (not connected)
struct AssetStatic {
address id;
string typeId;
string name;
string version;
Token token;
}
Token (static) (not connected)
struct Token {
address id;
string name;
string symbol;
uint8 decimals;
}
assetsStatic():
[{
id: "0x123...",
typeId: "v2Vault",
name: "YFI Vault",
version: "0.3.2",
tokenId: "0x234..."
token: {
id: "0x234...",
name: "yearn.fi",
symbol: "YFI",
decimals: 18
}
}]
AssetDynamic (dynamic) (not connected)
struct AssetDynamic {
address assetId;
address tokenId;
AssetMetadata metadata;
TokenAmount underlyingTokenBalance; // Amount of underlying token in the asset
}
TokenAmount (dynamic)
struct TokenAmount {
uint256 amount;
uint256 amountUsdc;
}
assetsDynamic():
[{
assetId: "0x123",
tokenId: "0x456",
metadata: {
pricePerShare: "101000022394340034000"
},
underlyingTokenBalance: {
amount: "53233222334444334444", // vault.totalAssets()
amountUsdc: "3422233333445"
}
}]
Position (dynamic) (connected)
struct Position {
address assetId;
string typeId;
address tokenId;
uint256 balance; // asset.balanceOf(account) - shares owned by user
TokenPostion underlyingTokenBalance; // Amount of underlying token in the asset that a user owns
TokenPosition accountTokenBalance; // Amount of underlying token a user owns
Allowance[] tokenAllowances;
Allowance[] assetAllowances;
}
positionsOf(account):
[{
assetId: "0x123",
typeId: "deposit",
tokenId: "0x545",
balance: "105344343435553", // vault.balanceOf(account)
underlyingTokenBalance: {
amount: "53233222334444334444", // vault.balanceOf(account) * pricePerShare
amountUsdc: "3422233333445"
},
accountTokenBalance: {
amount: "4424424000238", // token.balanceOf(account)
amountUsdc: "23323"
},
tokenAllowances: [{
owner: "0x4800", // Account
spender: "0x123", // Asset address
amount: "115335543535353535235325325325235235325235425235"
}],
assetAllowances: [{
owner: "0x123", // Asset address
spender: "0x456", // Trusted migrator
amount: "115335543535353535235325325325235235325235425235"
}]
}]
Position
struct Position {
address assetId;
uint256 balance;
uint256 balanceUsdc;
TokenPosition tokenPosition;
Allowance[] allowances;
}
Token
struct Token {
address id;
string name;
string symbol;
uint8 decimals;
uint256 priceUsdc;
}
TokenPosition
struct TokenPosition {
address tokenId;
uint256 balance;
uint256 balanceUsdc;
Allowance[] allowances;
}
struct Allowance {
address owner;
address spender;
uint256 allowance;
}
Adapter-specific Interfaces
Each registry adapter can export asset metadata that is specific to the asset type.
Vault
struct AssetMetadata {
string symbol;
uint256 pricePerShare;
bool migrationAvailable;
address latestVaultAddress;
uint256 depositLimit;
bool emergencyShutdown;
}
Earn
TBD
Iron Bank
TBD
veCrv
TBD
Methods
All registry adapters are required to implement the following methods.
registryAddress
Get the registry address associated with the adapter. The adapter pulls most information from the registry. registryAddress
is defined in the adapter consturctor.
address public registryAddress;
assetsLength
Get the number of assets the registry adapter is capable of returning.
function assetsLength() public view returns (uint256);
RETURN
Number of registry adapter assets
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
uint256 assetsLength = registryAdapter.assetsLength();
Example Response
32
positionSpenderAddresses
address[] public positionSpenderAddresses;
setPositionSpenderAddresses
function setPositionSpenderAddresses(address addresses)
assetsAddresses
Get a list of asset addresses from the asset adapter's associated registry.
function assetsAddresses() public view returns (address[] memory);
RETURN
Array of asset addresses
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
address[] assetAddresses = registryAdapter.assetsAddresses();
Example Response
['0xE14d13d8B3b85aF791b2AADD661cDBd5E6097Db1',
'0xdCD90C7f6324cfa40d7169ef80b12031770B4325',
'0x986b4AFF588a109c09B50A03f42E4110E29D353F',
'0xcB550A6D4C8e3517A939BC79d0c7093eb7cF56B5']
assetTvl
Get TVL for a specific asset (in USDC).
function assetTvl(address assetAddress) public view returns (uint256);
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
address assetAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 assetTvl = registryAdapter.assetTvl(assetAddress);
Example Response
1637032292
assetsTvl
Get aggregated TVL for all assets in the adapter's registry.
function assetsTvl() external view returns (uint256);
RETURN
Aggregated TVL scoped to the adapter's assets in USDC
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
uint256 assetTvl = registryAdapter.assetsTvl();
Example Response
443923433354832
asset
Get a specific asset including metadata specific to the asset type. The response extends the standardized Asset
interface.
function asset(address assetAddress) public view returns (Asset memory);
assetAddress
The address of the asset to fetch
RETURN
An asset struct containing information about the asset
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
address assetAddress = 0xe2F6b9773BF3A015E2aA70741Bde1498bdB9425b;
registryAdapter.asset(assetAddress);
Example Response
{
name: 'WTFTM Vault',
id: 0xe2F6b9773BF3A015E2aA70741Bde1498bdB9425b,
version: '0.3.2',
totalAssets: 129999966466476313000000,
totalAssetsUsdc: 14355444934422, // TVL
metadata: {
controller: 0x19D3364A399d251E894aC732651be8B0E4e85001,
pricePerShare: 142004300000000000
...
}
}
assets
Get all assets for a registry adapter
function assets() external view returns (Asset[] memory);
RETURN
An array of asset structs containing information about the assets
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
registryAdapter.assets();
Example Response
[
{
name: 'WFTM Vault',
id: 0xe2F6b9773BF3A015E2aA70741Bde1498bdB9425b, // vault address
version: '0.3.2',
totalAssets: 129999966466476313000000,
totalAssetsUsdc: 14355444934422, // TVL
metadata: {
controller: 0x19D3364A399d251E894aC732651be8B0E4e85001,
pricePerShare: 142004300000000000
...
}
},
{
name: 'fUSD Vault',
id: 0x5f18C75AbDAe578b483E5F43f12a39cF75b973a9,
version: '0.3.2',
totalAssets: 4593326846642963130309078,
totalAssetsUsdc: 45444930943854422, // TVL
metadata: {
controller: 0x19D3364A399d251E894aC732651be8B0E4e85001,
pricePerShare: 101003300000000000
...
}
}
]
positionOf
Get the position of an account for a specific asset address.
function positionOf(address accountAddress, address assetAddress)
public
view
returns (Position memory);
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
address accountAddress = 0x481140F916a4e64559694DB4d56D692CadC0326c;
address assetAddress = 0xe2F6b9773BF3A015E2aA70741Bde1498bdB9425b;
registryAdapter.positionForAsset(accountAddress, assetAddress);
Example Response
{
assetId: 0xe2F6b9773BF3A015E2aA70741Bde1498bdB9425b, // vault address
depositedBalance: 10422000000000000000,
depositedBalanceUsdc: 10422000,
tokenBalance: 2110000000000000000,
tokenBalanceUsdc: 2110000,
tokenAllowance, 1157343987545498574545495749587452... // max uint256
}
positionsOf
Get all positions for an account for every adapter asset.
function positionsOf(address accountAddress)
external
view
returns (Position[] memory);
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
address accountAddress = 0x481140F916a4e64559694DB4d56D692CadC0326c;
registryAdapter.positionsOf(accountAddress);
Example Response
[
{
assetId: 0xe2F6b9773BF3A015E2aA70741Bde1498bdB9425b, // vault address
depositedBalance: 10422000000000000000,
depositedBalanceUsdc: 10422000,
tokenBalance: 2110000000000000000,
tokenBalanceUsdc: 2110000,
tokenAllowance, 1157343987545498574545495749587452... // max uint256
},
{
assetId: 0x19D3364A399d251E894aC732651be8B0E4e85001, // vault address
depositedBalance: 10422000000000000000,
depositedBalanceUsdc: 10422000,
tokenBalance: 0,
tokenBalanceUsdc: 0,
tokenAllowance, 0
}
]
tokens
Get unique tokens for the adapter.
function tokens()
external
view
returns (Token[] memory);
Solidity
RegistryAdapter registryAdapter = RegistryAdapter(0xABCD...);
registryAdapter.tokens();
Example Response
[
{
id: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // token address
name: 'USD Coin',
symbol: 'USDC',
decimals: 18,
priceUsdc: 100000
},
...
]
Oracle
Methods
setCalculations
Set oracle calculation contract addresses. Each calculation contract must implement getPriceUsdc(). The order of calculation contracts matters as it determines the order preference in the cascading fallback mechanism.
function setCalculations(address[] memory calculationAddresses)
public
onlyManagers;
calculationAddresses
An array of addresses for underlying calculation contracts
calculations
View the calculation contract addresses currently associated with the oracle.
function calculations() external view returns (address[] memory);
RETURN
An array of calculation contract addresses associated with the oracle