Track Supply Positions
Learn how to track any position supplied through Morpho, on-chain via a Smart Contract & off-chain via ethers.js.
For Morpho-Aave-V2 and Morpho-Compound, there are lenses deployed.
Morpho's Lens exposes generic information about the protocol, such as the total supply in USD (18 decimals). Anyone can query it off-chain through Etherscan (morpho-compound, morpho-aave-V2) or with ethers.js, or on-chain using a Smart Contract.
In addition to querying generic data, anyone can, at anytime, query Morpho's Lens to get information about anyone's supply position. Here is the repository:
For Morpho-Aave-V3, there is no lens but instead, there are snippets provided. The github repository is here:
👇 Here are concrete examples 👇
Morpho-Aave Instances
Morpho-Aave-V3 & Morpho-Aave-V2 are Deployed.
The structure of the following solidity snippets is as follows:
Imports
Contracts
/// IMPORTS ///
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import {IPool, IPoolAddressesProvider} from "@aave-v3-core/interfaces/IPool.sol";
import {IAaveOracle} from "@aave-v3-core/interfaces/IAaveOracle.sol";
import {IMorpho} from "@morpho-aave-v3/interfaces/IMorpho.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {Types} from "@morpho-aave-v3/libraries/Types.sol";
import {MarketLib} from "@snippets/morpho-aave-v3/libraries/MarketLib.sol";
import {Utils} from "@snippets/morpho-aave-v3/Utils.sol";
import {Math} from "@morpho-utils/math/Math.sol";
import {WadRayMath} from "@morpho-utils/math/WadRayMath.sol";
import {DataTypes} from "@aave-v3-core/protocol/libraries/types/DataTypes.sol";
import {ReserveConfiguration} from "@aave-v3-core/protocol/libraries/configuration/ReserveConfiguration.sol";
/// FUNCTIONS ///
/// @title Snippets
/// @author Morpho Labs
/// @custom:contact security@morpho.xyz
/// @notice Code snippets for Morpho-Aave V3.
contract Snippets {
using Math for uint256;
using WadRayMath for uint256;
using MarketLib for Types.Market;
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
IMorpho public immutable morpho;
IPoolAddressesProvider public immutable addressesProvider;
IPool public immutable pool;
uint8 public immutable eModeCategoryId;
constructor(address morphoAddress) {
morpho = IMorpho(morphoAddress);
pool = IPool(morpho.pool());
addressesProvider = IPoolAddressesProvider(morpho.addressesProvider());
eModeCategoryId = uint8(morpho.eModeCategoryId());
}
/// @notice Computes and returns the total distribution of supply through Morpho, using virtually updated indexes.
/// @return p2pSupplyAmount The total supplied amount matched peer-to-peer, subtracting the supply delta and the idle supply on Morpho's contract (in base currency).
/// @return poolSupplyAmount The total supplied amount on the underlying pool, adding the supply delta (in base currency).
/// @return idleSupplyAmount The total idle supply amount on the Morpho's contract (in base currency).
/// @return totalSupplyAmount The total amount supplied through Morpho (in base currency).
function totalSupply()
public
view
returns (uint256 p2pSupplyAmount, uint256 poolSupplyAmount, uint256 idleSupplyAmount, uint256 totalSupplyAmount)
{
address[] memory marketAddresses = morpho.marketsCreated();
uint256 underlyingPrice;
uint256 nbMarkets = marketAddresses.length;
for (uint256 i; i < nbMarkets; ++i) {
address underlying = marketAddresses[i];
DataTypes.ReserveConfigurationMap memory reserve = pool.getConfiguration(underlying);
underlyingPrice = assetPrice(underlying, reserve.getEModeCategory());
uint256 assetUnit = 10 ** reserve.getDecimals();
(uint256 marketP2PSupplyAmount, uint256 marketPoolSupplyAmount, uint256 marketIdleSupplyAmount) =
marketSupply(underlying);
p2pSupplyAmount += (marketP2PSupplyAmount * underlyingPrice) / assetUnit;
poolSupplyAmount += (marketPoolSupplyAmount * underlyingPrice) / assetUnit;
idleSupplyAmount += (marketIdleSupplyAmount * underlyingPrice) / assetUnit;
}
totalSupplyAmount = p2pSupplyAmount + poolSupplyAmount + idleSupplyAmount;
}
/// @notice Returns the supply rate per year a given user is currently experiencing on a given market.
/// @param underlying The address of the underlying asset.
/// @param user The user to compute the supply rate per year for.
/// @return supplyRatePerYear The supply rate per year the user is currently experiencing (in ray).
function supplyAPR(address underlying, address user) public view returns (uint256 supplyRatePerYear) {
(uint256 balanceInP2P, uint256 balanceOnPool,) = supplyBalance(underlying, user);
(uint256 poolSupplyRate, uint256 poolBorrowRate) = poolAPR(underlying);
Types.Market memory market = morpho.market(underlying);
Types.Indexes256 memory indexes = morpho.updatedIndexes(underlying);
uint256 p2pSupplyRate = Utils.p2pSupplyAPR(
Utils.P2PRateComputeParams({
poolSupplyRatePerYear: poolSupplyRate,
poolBorrowRatePerYear: poolBorrowRate,
poolIndex: indexes.supply.poolIndex,
p2pIndex: indexes.supply.p2pIndex,
proportionIdle: market.proportionIdle(),
p2pDelta: market.deltas.supply.scaledDelta,
p2pTotal: market.deltas.supply.scaledP2PTotal,
p2pIndexCursor: market.p2pIndexCursor,
reserveFactor: market.reserveFactor
})
);
supplyRatePerYear = Utils.weightedRate(p2pSupplyRate, poolSupplyRate, balanceInP2P, balanceOnPool);
}
/// @notice Computes and returns the total distribution of supply for a given market, using virtually updated indexes.
/// @notice It takes into account the amount of token deposit in supply and in collateral in Morpho.
/// @param underlying The address of the underlying asset to check.
/// @return p2pSupply The total supplied amount (in underlying) matched peer-to-peer, subtracting the supply delta and the idle supply.
/// @return poolSupply The total supplied amount (in underlying) on the underlying pool, adding the supply delta.
/// @return idleSupply The total idle amount (in underlying) on the Morpho contract.
function marketSupply(address underlying)
public
view
returns (uint256 p2pSupply, uint256 poolSupply, uint256 idleSupply)
{
Types.Market memory market = morpho.market(underlying);
Types.Indexes256 memory indexes = morpho.updatedIndexes(underlying);
p2pSupply = market.trueP2PSupply(indexes);
poolSupply = ERC20(market.aToken).balanceOf(address(morpho));
idleSupply = market.idleSupply;
}
/// @notice Returns the balance in underlying of a given user in a given market.
/// @param underlying The address of the underlying asset.
/// @param user The user to determine balances of.
/// @return balanceInP2P The balance in peer-to-peer of the user (in underlying).
/// @return balanceOnPool The balance on pool of the user (in underlying).
/// @return totalBalance The total balance of the user (in underlying).
function supplyBalance(address underlying, address user)
public
view
returns (uint256 balanceInP2P, uint256 balanceOnPool, uint256 totalBalance)
{
Types.Indexes256 memory indexes = morpho.updatedIndexes(underlying);
balanceInP2P = morpho.scaledP2PSupplyBalance(underlying, user).rayMulDown(indexes.supply.p2pIndex);
balanceOnPool = morpho.scaledPoolSupplyBalance(underlying, user).rayMulDown(indexes.supply.poolIndex);
totalBalance = balanceInP2P + balanceOnPool;
}
/// @dev Computes and returns the underlying pool rates for a specific market.
/// @param underlying The underlying pool market address.
/// @return poolSupplyRatePerYear The market's pool supply rate per year (in ray).
/// @return poolBorrowRatePerYear The market's pool borrow rate per year (in ray).
function poolAPR(address underlying)
public
view
returns (uint256 poolSupplyRatePerYear, uint256 poolBorrowRatePerYear)
{
DataTypes.ReserveData memory reserve = pool.getReserveData(underlying);
poolSupplyRatePerYear = reserve.currentLiquidityRate;
poolBorrowRatePerYear = reserve.currentVariableBorrowRate;
}
/// @notice Returns the price of a given asset.
/// @param asset The address of the asset to get the price of.
/// @param reserveEModeCategoryId Aave's associated reserve e-mode category.
/// @return price The current price of the asset.
function assetPrice(address asset, uint256 reserveEModeCategoryId) public view returns (uint256 price) {
address priceSource;
if (eModeCategoryId != 0 && reserveEModeCategoryId == eModeCategoryId) {
priceSource = pool.getEModeCategoryData(eModeCategoryId).priceSource;
}
IAaveOracle oracle = IAaveOracle(addressesProvider.getPriceOracle());
if (priceSource != address(0)) {
price = oracle.getAssetPrice(priceSource);
}
if (priceSource == address(0) || price == 0) {
price = oracle.getAssetPrice(asset);
}
}
}
The structure of the following TS snippets is as follows:
Imports
Interface
Functions
Utils
/// IMPORTS ///
import { BigNumber, providers } from "ethers";
import { constants } from "ethers/lib/index";
import { PercentMath, WadRayMath } from "@morpho-labs/ethers-utils/lib/maths";
import { minBN, pow10 } from "@morpho-labs/ethers-utils/lib/utils";
/// INTERFACE ///
interface P2PRateComputeParams {
/** The pool supply rate per year (in ray). */
poolSupplyRatePerYear: BigNumber;
/** The pool borrow rate per year (in ray). */
poolBorrowRatePerYear: BigNumber;
/** The last stored pool index (in ray). */
poolIndex: BigNumber;
/** The last stored peer-to-peer index (in ray). */
p2pIndex: BigNumber;
/** The delta amount in pool unit. */
p2pDelta: BigNumber;
/** The total peer-to-peer amount in peer-to-peer unit. */
p2pAmount: BigNumber;
/** The index cursor of the given market (in bps). */
p2pIndexCursor: BigNumber;
/** The reserve factor of the given market (in bps). */
reserveFactor: BigNumber;
/** The proportion idle of the given market (in underlying). */
proportionIdle: BigNumber;
}
/// FUNCTIONS ///
/**
* This function retrieves the total supply over the Morpho Aave v3
* markets for both collateral and supply only.
*
* @param provider A provider instance
*/
const getTotalSupply = async (provider: providers.BaseProvider) => {
const { oracle, morphoAaveV3 } = getContracts(provider);
const markets = await morphoAaveV3.marketsCreated();
const marketsData = await Promise.all(
markets.map(async (underlying) => {
const [
{
aToken: aTokenAddress,
indexes: {
supply: { p2pIndex, poolIndex },
},
deltas: {
supply: { scaledDelta, scaledP2PTotal },
},
idleSupply,
},
underlyingPrice,
] = await Promise.all([
morphoAaveV3.market(underlying),
oracle.getAssetPrice(underlying), // TODO: handle if emode
]);
const aToken = AToken__factory.connect(aTokenAddress, provider);
const [decimals, poolSupplyAmount] = await Promise.all([
aToken.decimals(),
aToken.balanceOf(morphoAaveV3.address),
]);
const p2pSupplyAmount = zeroFloorSub(
WadRayMath.rayMul(scaledP2PTotal, p2pIndex),
WadRayMath.rayMul(scaledDelta, poolIndex)
);
return {
p2pSupplyAmount,
poolSupplyAmount,
idleSupply,
underlyingPrice,
decimals,
};
})
);
const amounts = marketsData.reduce(
(acc, { p2pSupplyAmount, poolSupplyAmount, idleSupply, underlyingPrice, decimals }) => {
const toUsd = (amount: BigNumber) => amount.mul(underlyingPrice).div(pow10(decimals));
return {
p2pSupplyAmount: acc.p2pSupplyAmount.add(toUsd(p2pSupplyAmount)),
poolSupplyAmount: acc.poolSupplyAmount.add(toUsd(poolSupplyAmount)),
idleSupply: acc.idleSupply.add(toUsd(idleSupply)),
};
},
{
p2pSupplyAmount: constants.Zero,
poolSupplyAmount: constants.Zero,
idleSupply: constants.Zero,
}
);
/**
* This function gets the total supply for one given market.
*
* @param underlying The address of the underlying token
* @param provider A provider instance
*/
const getTotalMarketSupply = async (
underlying: string,
provider: providers.BaseProvider
) => {
const { morphoAaveV3 } = getContracts(provider);
const {
aToken: aTokenAddress,
indexes: {
supply: { p2pIndex, poolIndex },
},
deltas: {
supply: { scaledDelta, scaledP2PTotal },
},
idleSupply,
} = await morphoAaveV3.market(underlying);
const aToken = AToken__factory.connect(aTokenAddress, provider);
const poolSupplyAmount = await aToken.balanceOf(morphoAaveV3.address);
const p2pSupplyAmount = zeroFloorSub(
WadRayMath.rayMul(scaledP2PTotal, p2pIndex),
WadRayMath.rayMul(scaledDelta, poolIndex)
);
return {
p2pSupplyAmount,
poolSupplyAmount,
idleSupply,
totalSupplyAmount: p2pSupplyAmount.add(poolSupplyAmount).add(idleSupply),
};
};
/**
* This function retrieves the supply balance of one given user in one given market.
*
* @param underlying The market to retrieve the supplied liquidity.
* @param user The user address.
* @param provider A provider instance
*
* @returns The matched peer-to-peer amount, the pool amount and the total supply amount.
*/
const getCurrentSupplyBalanceInOf = async (
underlying: string,
user: string,
provider: providers.BaseProvider
) => {
const { morphoAaveV3 } = getContracts(provider);
const [
{
supply: { p2pIndex, poolIndex },
},
scaledP2PSupplyBalance,
scaledPoolSupplyBalance,
] = await Promise.all([
morphoAaveV3.updatedIndexes(underlying),
morphoAaveV3.scaledP2PSupplyBalance(underlying, user),
morphoAaveV3.scaledPoolSupplyBalance(underlying, user),
]);
const balanceInP2P = WadRayMath.rayMul(scaledP2PSupplyBalance, p2pIndex);
const balanceOnPool = WadRayMath.rayMul(scaledPoolSupplyBalance, poolIndex);
return {
balanceInP2P,
balanceOnPool,
totalBalance: balanceInP2P.add(balanceOnPool),
};
};
/**
* This function retrieves the collateral balance of one given user in one given market.
*
* @param underlying The market to retrieve the collateral amount.
* @param user The user address.
* @param provider A provider instance
*
* @returns The total collateral of the user.
*/
const getCurrentCollateralBalanceInOf = async (
underlying: string,
user: string,
provider: providers.BaseProvider
) => {
const { morphoAaveV3 } = getContracts(provider);
return morphoAaveV3.collateralBalance(underlying, user)
};
/**
* This function retrieves the supply APY of a user on a given market.
*
* @param underlying The market to retrieve the supply APY.
* @param user The user address.
* @param provider A provider instance
*
* @returns The experienced rate and the total balance of the deposited liquidity on this market.
*/
const getCurrentUserSupplyRatePerYear = async (
underlying: string,
user: string,
provider: providers.BaseProvider
) => {
const [{ balanceInP2P, balanceOnPool }, balanceIdle, { p2pSupplyRate, poolSupplyRate }] =
await Promise.all([
getCurrentSupplyBalanceInOf(underlying, user, provider),
getCurrentCollateralBalanceInOf(underlying, user, provider),
getSupplyRatesPerYear(underlying, provider),
]);
const poolAmount = balanceIdle.add(balanceOnPool);
return getWeightedRate(p2pSupplyRate, poolSupplyRate, balanceInP2P, poolAmount);
};
/**
* This function compute the P2P supply rate and returns the result.
*
* @param params The parameters inheriting of the P2PRateComputeParams interface allowing the computation.
* @returns The p2p supply rate per year in _RAY_ units.
*/
const getP2PSupplyRate = ({
poolSupplyRatePerYear,
poolBorrowRatePerYear,
p2pIndexCursor,
p2pIndex,
poolIndex,
proportionIdle,
reserveFactor,
p2pDelta,
p2pAmount,
}: P2PRateComputeParams) => {
let p2pSupplyRate;
if (poolSupplyRatePerYear.gt(poolBorrowRatePerYear)) p2pSupplyRate = poolBorrowRatePerYear;
else {
const p2pRate = getWeightedAvg(poolSupplyRatePerYear, poolBorrowRatePerYear, p2pIndexCursor);
p2pSupplyRate = p2pRate.sub(
PercentMath.percentMul(p2pRate.sub(poolBorrowRatePerYear), reserveFactor)
);
}
if (p2pDelta.gt(0) && p2pAmount.gt(0)) {
const proportionDelta = minBN(
WadRayMath.rayDiv(
// TODO: use of indexDivUp
WadRayMath.rayMul(p2pDelta, poolIndex),
WadRayMath.rayMul(p2pAmount, p2pIndex)
),
WadRayMath.RAY.sub(proportionIdle) // To avoid proportionDelta + proportionIdle > 1 with rounding errors.
);
p2pSupplyRate = WadRayMath.rayMul(
p2pSupplyRate,
WadRayMath.RAY.sub(proportionDelta).sub(proportionIdle)
)
.add(WadRayMath.rayMul(poolSupplyRatePerYear, proportionDelta))
.add(proportionIdle);
}
return p2pSupplyRate;
};
/**
* This function compute the supply rate on a specific asset and returns the result.
*
* @param underlying The market to retrieve the supply APY.
* @param provider A provider instance
*
* @returns The P2P supply rate per year and the pool supply rate per year in _RAY_ units.
*/
const getSupplyRatesPerYear = async (
underlying: string,
provider: providers.BaseProvider
) => {
const { morphoAaveV3, pool } = getContracts(provider);
const [
{ currentLiquidityRate, currentVariableBorrowRate },
{
idleSupply,
deltas: {
supply: { scaledDelta, scaledP2PTotal },
},
indexes: {
supply: { p2pIndex, poolIndex },
},
reserveFactor,
p2pIndexCursor,
},
] = await Promise.all([pool.getReserveData(underlying), morphoAaveV3.market(underlying)]);
const totalP2PSupplied: BigNumber = WadRayMath.rayMul(
scaledP2PTotal,
p2pIndex // TODO: use updated index
);
const propIdleSupply = WadRayMath.rayDiv(idleSupply, totalP2PSupplied);
const p2pSupplyRate = await getP2PSupplyRate({
poolSupplyRatePerYear: currentLiquidityRate,
poolBorrowRatePerYear: currentVariableBorrowRate,
poolIndex,
p2pIndex,
proportionIdle: propIdleSupply,
p2pDelta: scaledDelta,
p2pAmount: scaledP2PTotal,
p2pIndexCursor: BigNumber.from(p2pIndexCursor),
reserveFactor: BigNumber.from(reserveFactor),
});
return {
p2pSupplyRate,
poolSupplyRate: currentLiquidityRate,
};
};
/// UTILS ///
/**
* This function is subtracting one number from another, but ensuring that the result is never negative, instead of returning a negative value it will return zero.
*
* @param a A BigNumber.
* @param b A BigNumber.
* @returns A non negative number or 0.
*/
const zeroFloorSub = (a: BigNumber, b: BigNumber) => maxBN(constants.Zero, a.sub(b));
/**
* This function is computing an average rate
* and returns the weighted rate and the total balance.
*
* @param p2pRate The peer-to-peer rate per year, in _RAY_ units
* @param poolRate The pool rate per year, in _RAY_ units
* @param balanceInP2P The underlying balance matched peer-to-peer
* @param balanceOnPool The underlying balance on the pool
*/
const getWeightedRate = async (
p2pRate: BigNumber,
poolRate: BigNumber,
balanceInP2P: BigNumber,
balanceOnPool: BigNumber
) => {
const totalBalance = balanceInP2P.add(balanceOnPool);
if (totalBalance.isZero())
return {
weightedRate: constants.Zero,
totalBalance,
};
return {
weightedRate: p2pRate.mul(balanceInP2P).add(poolRate.mul(balanceOnPool)).div(totalBalance),
totalBalance,
};
};
/**
* This function Executes a weighted average (x * (1 - p) + y * p), rounded up and returns the result.
* TODO: move it to ethers-utils
*
* @param x The first value, with a weight of 1 - percentage.
* @param y The second value, with a weight of percentage.
* @param percentage The weight of y, and complement of the weight of x.
* @returns The result of the weighted average.
*/
const getWeightedAvg = (x: BigNumber, y: BigNumber, percentage: BigNumber) => {
const MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR = constants.MaxUint256.sub(
PercentMath.HALF_PERCENT
);
let z: BigNumber = PercentMath.BASE_PERCENT.sub(percentage);
if (
percentage.gt(PercentMath.BASE_PERCENT) ||
(percentage.gt(0) && y.gt(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR.div(percentage))) ||
(PercentMath.BASE_PERCENT.gt(percentage) &&
x.gt(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR.sub(y.mul(percentage)).div(z)))
) {
throw new Error("Underflow or overflow detected");
}
z = x.mul(z).add(y.mul(percentage)).add(PercentMath.HALF_PERCENT).div(PercentMath.BASE_PERCENT);
return z;
};
Lens address here.
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.16;
import {ILens} from "./interfaces/ILens.sol";
import {IMorpho} from "./interfaces/IMorpho.sol";
import {IPriceOracleGetter} from "./interfaces/aave/IPriceOracleGetter.sol";
import {WadRayMath} from "@morpho-org/morpho-utils/src/math/WadRayMath.sol";
contract MorphoAaveV2Supplier {
using WadRayMath for uint256;
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant ADAI = 0x028171bCA77440897B824Ca71D1c56caC55b68A3;
address public constant AWBTC = 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656;
address public constant LENS = 0x507fA343d0A90786d86C7cd885f5C49263A91FF4;
address public constant MORPHO = 0x777777c9898D384F785Ee44Acfe945efDFf5f3E0;
ICompoundOracle public immutable ORACLE;
constructor() {
ORACLE = IPriceOracleGetter(
IMorpho(MORPHO).addressesProvider().getPriceOracle()
);
}
/// @notice Returns the distribution of WBTC supplied by this contract through Morpho-AaveV2.
/// @return suppliedOnPool The amount of WBTC supplied on AaveV2's pool (with 8 decimals, the number of decimals of WBTC).
/// @return suppliedP2P The amount of WBTC supplied peer-to-peer through Morpho-AaveV2 (with 8 decimals, the number of decimals of WBTC).
function getWBTCSupplyBalance()
public
view
returns (uint256 suppliedOnPool, uint256 suppliedP2P)
{
(suppliedOnPool, suppliedP2P, ) = ILens(LENS)
.getCurrentSupplyBalanceInOf(
AWBTC, // the WBTC market, represented by the aWBTC ERC20 token
address(this) // the address of the user you want to know the supply of
);
}
/// @notice Returns the distribution of WBTC supplied by this contract through Morpho-AaveV2.
/// @return suppliedOnPoolDAI The DAI amount of WBTC supplied on AaveV2's pool (with 18 decimals, the number of decimals of DAI).
/// @return suppliedP2PDAI The DAI amount of WBTC supplied peer-to-peer through Morpho-AaveV2 (with 18 decimals, the number of decimals of DAI).
function getWBTCSupplyBalanceDAI()
public
view
returns (uint256 suppliedOnPoolDAI, uint256 suppliedP2PDAI)
{
(uint256 suppliedOnPool, uint256 suppliedP2P) = getWBTCSupplyBalance();
uint256 oraclePrice = ORACLE.getAssetPrice(DAI); // with 18 decimals, whatever the market
suppliedOnPoolDAI = suppliedOnPool.wadMul(oraclePrice); // with 18 decimals, the number of decimals of DAI
suppliedP2PDAI = suppliedP2P.wadMul(oraclePrice); // with 18 decimals, the number of decimals of DAI
}
/// @notice Returns the average supply APR experienced on the DAI market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the AaveV2 pool.
/// @return The APR at which supply interests are accrued on average on the DAI market (with 27 decimals).
function getDAIAvgSupplyAPR() public view returns (uint256) {
return
ILens(LENS).getAverageSupplyRatePerYear(
ADAI // the DAI market, represented by the cDAI ERC20 token
);
}
/// @notice Returns the supply APR this contract experiences on the WBTC market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the AaveV2 pool.
/// @return The APR at which supply interests are accrued by this contract on the WBTC market (with 27 decimals).
function getWBTCSupplyAPR() public view returns (uint256) {
return
ILens(LENS).getCurrentUserSupplyRatePerYear(
AWBTC, // the WBTC market, represented by the aWBTC ERC20 token
address(this) // the address of the user you want to know the supply rate of
);
}
/// @notice Returns the supply APR this contract will experience (at minimum) if it supplies the given amount on the WBTC market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the AaveV2 pool.
/// @return nextSupplyAPR The APR at which supply interests would be accrued by this contract on the WBTC market (with 27 decimals).
function getWBTCNextSupplyAPR(uint256 _amount)
public
view
returns (uint256 nextSupplyAPR)
{
(nextSupplyAPR, , , ) = ILens(LENS).getNextUserSupplyRatePerYear(
AWBTC, // the WBTC market, represented by the aWBTC ERC20 token
address(this), // the address of the user you want to know the next supply rate of
_amount
);
}
/// @notice Returns the expected amount of supply interests accrued by this contract, on the WBTC market, after `_nbSeconds`.
/// @return The expected amount of WBTC supply interests accrued (with 8 decimals, the number of decimals of WBTC).
function getWBTCExpectedAccruedInterests(uint256 _nbSeconds)
public
view
returns (uint256)
{
(uint256 suppliedOnPool, uint256 suppliedP2P) = getWBTCSupplyBalance();
uint256 supplyRatePerYear = getWBTCSupplyAPR();
return
((suppliedOnPool + suppliedP2P).rayMul(supplyRatePerYear) *
_nbSeconds) / 365.25 days;
}
}
Lens address here.
import ethers from "ethers";
const signer = new ethers.Wallet(
process.env.PRIVATE_KEY,
new ethers.providers.JsonRpcBatchProvider(process.env.RPC_URL)
);
const signerAddress = await signer.getAddress();
const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const wbtcAddress = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599";
const aWethAddress = "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e";
const aDaiAddress = "0x028171bCA77440897B824Ca71D1c56caC55b68A3";
const aWbtcAddress = "0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656";
const wbtcDecimals = 8;
const daiDecimals = 18;
const dai = new ethers.Contract("0x6B175474E89094C44Da98b954EedeAC495271d0F", DAIAbi, signer);
const weth = new ethers.Contract("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", WETH9Abi, signer);
const lens = new ethers.Contract("0x507fA343d0A90786d86C7cd885f5C49263A91FF", LensAbi, signer);
const morpho = new ethers.Contract("0x777777c9898d384f785ee44acfe945efdff5f3e0", MorphoAbi, signer);
const oracle = new ethers.Contract("0xA50ba011c48153De246E5192C8f9258A2ba79Ca9", OracleAbi, signer);
async function getTotalSupplyETH() {
const [, , totalSupplyETH] = await lens.getTotalSupply();
return Number(ethers.utils.formatUnits(totalSupplyETH, 18)); // ETH amounts are always in 18 decimals
}
async function getTotalSupplyDAI() {
const totalSupplyETH = await getTotalSupplyETH();
const daiOraclePrice = await oracle.getAssetPrice(daiAddress); // in ETH (18 decimals), whatever the market
return totalSupplyETH / Number(ethers.utils.formatUnits(daiOraclePrice, 18)); // ETH amounts are always in 18 decimals
}
async function getTotalDAIMarketSupply() {
const [suppliedP2P, suppliedOnPool] = await lens.getTotalMarketSupply(
aDaiAddress // the DAI market, represented by the aDAI ERC20 token
);
return Number(ethers.utils.formatUnits(suppliedP2P.add(suppliedOnPool), daiDecimals));
}
async function getWBTCSupplyBalance() {
const [suppliedOnPool, suppliedP2P] = await lens.getCurrentSupplyBalanceInOf(
aWbtcAddress, // the WBTC market, represented by the aWBTC ERC20 token
signerAddress // the address of the user you want to get the supply of
);
return Number(ethers.utils.formatUnits(suppliedP2P.add(suppliedOnPool), wbtcDecimals));
}
async function getWBTCSupplyBalanceETH() {
const totalMarketSupply = await getWBTCSupplyBalance();
const wbtcOraclePrice = await oracle.getAssetPrice(wbtcAddress); // in ETH (18 decimals), whatever the market
return totalMarketSupply * Number(ethers.utils.formatUnits(wbtcOraclePrice, 18));
}
async function getWBTCSupplyBalanceDAI() {
const wbtcSupplyBalance = await getWBTCSupplyBalanceETH();
const daiOraclePrice = await oracle.getAssetPrice(daiAddress); // in ETH (18 decimals), whatever the market
return wbtcSupplyBalance / Number(ethers.utils.formatUnits(daiOraclePrice, 18));
}
// @note The supply rate experienced on a market is specific to each user,
// @note dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
async function getDAIAvgSupplyAPR() {
const [avgSupplyRatePerYear] = await lens.getAverageSupplyRatePerYear(
aDaiAddress // the DAI market, represented by the aDAI ERC20 token
);
return Number(ethers.utils.formatUnits(avgSupplyRatePerYear, 27)); // 27 decimals, whatever the market
}
// @note The supply rate experienced on a market is specific to each user,
// @note dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
async function getWBTCSupplyAPR() {
const supplyRatePerYear = await lens.getCurrentUserSupplyRatePerYear(
aWbtcAddress, // the DAI market, represented by the aDAI ERC20 token
signerAddress // the address of the user you want to get the supply rate of
);
return Number(ethers.utils.formatUnits(supplyRatePerYear, 27)); // 27 decimals, whatever the market
}
// @note The supply rate experienced on a market is specific to each user,
// @note dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
async function getWBTCNextSupplyAPR(amount) {
const [nextSupplyRatePerYear] = await lens.getNextUserSupplyRatePerYear(
aWbtcAddress, // the DAI market, represented by the aDAI ERC20 token
signerAddress, // the address of the user you want to get the next supply rate of
amount
);
return Number(ethers.utils.formatUnits(nextSupplyRatePerYear, 27)); // 27 decimals, whatever the market
}
getTotalSupplyETH().then((val) => console.log("Total supply ETH", val));
getTotalSupplyDAI().then((val) => console.log("Total supply DAI", val));
getTotalDAIMarketSupply().then((val) => console.log("DAI supply", val));
getWBTCSupplyBalance().then((val) => console.log("WBTC own supply", val));
getWBTCSupplyBalanceETH().then((val) => console.log("WBTC own supply ETH", val));
getWBTCSupplyBalanceDAI().then((val) => console.log("WBTC own supply DAI", val));
getDAIAvgSupplyAPR().then((val) => console.log("DAI avg supply APR", val));
getWBTCSupplyAPR().then((val) => console.log("WBTC supply APR", val));
getWBTCNextSupplyAPR(ethers.utils.parseUnits("100", wbtcDecimals)).then((val) =>
console.log("WBTC next supply rate", val)
);
Morpho-Compound
Lens address here.
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.16;
import {ILens} from "@morpho-org/morpho-core-v1/contracts/compound/interfaces/ILens.sol";
import {IMorpho, ICompoundOracle} from "@morpho-org/morpho-core-v1/contracts/compound/interfaces/IMorpho.sol";
import {CompoundMath} from "@morpho-org/morpho-utils/src/math/CompoundMath.sol";
contract MorphoCompoundSupplier {
using CompoundMath for uint256;
address public constant CDAI = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;
address public constant CWBTC = 0xC11b1268C1A384e55C48c2391d8d480264A3A7F4;
address public constant LENS = 0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67;
address public constant MORPHO = 0x8888882f8f843896699869179fB6E4f7e3B58888;
ICompoundOracle public immutable ORACLE;
constructor() {
ORACLE = ICompoundOracle(IMorpho(MORPHO).comptroller().oracle());
}
/// @notice Returns the distribution of WBTC supplied by this contract through Morpho-Compound.
/// @return suppliedOnPool The amount of WBTC supplied on Compound's pool (with 8 decimals, the number of decimals of WBTC).
/// @return suppliedP2P The amount of WBTC supplied peer-to-peer through Morpho-Compound (with 8 decimals, the number of decimals of WBTC).
function getWBTCSupplyBalance()
public
view
returns (uint256 suppliedOnPool, uint256 suppliedP2P)
{
(suppliedOnPool, suppliedP2P, ) = ILens(LENS)
.getCurrentSupplyBalanceInOf(
CWBTC2, // the WBTC market, represented by the cWBTC2 ERC20 token
address(this) // the address of the user you want to know the supply of
);
}
/// @notice Returns the distribution of WBTC supplied by this contract through Morpho-Compound.
/// @return suppliedOnPoolUSD The USD value of the amount of WBTC supplied on Compound's pool (with 18 decimals, whatever the market).
/// @return suppliedP2PUSD The USD value of the amount of WBTC supplied peer-to-peer through Morpho-Compound (with 18 decimals, whatever the market).
function getWBTCSupplyBalanceUSD()
public
view
returns (uint256 suppliedOnPoolUSD, uint256 suppliedP2PUSD)
{
(uint256 suppliedOnPool, uint256 suppliedP2P) = getWBTCSupplyBalance();
uint256 oraclePrice = ORACLE.getUnderlyingPrice(CWBTC2); // with (36 - nb decimals of WBTC = 28) decimals
suppliedOnPoolUSD = suppliedOnPool.mul(oraclePrice); // with 18 decimals, whatever the underlying token
suppliedP2PUSD = suppliedP2P.mul(oraclePrice); // with 18 decimals, whatever the underlying token
}
/// @notice Returns the average supply rate per block experienced on the DAI market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
/// @return The rate per block at which supply interests are accrued on average on the DAI market (with 18 decimals).
function getDAIAvgSupplyRatePerBlock() public view returns (uint256) {
return
ILens(LENS).getAverageSupplyRatePerBlock(
CDAI // the DAI market, represented by the cDAI ERC20 token
);
}
/// @notice Returns the average supply APR experienced on the DAI market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
/// @return The APR at which supply interests are accrued on average on the DAI market (with 18 decimals).
function getDAIAvgSupplyAPR() public view returns (uint256) {
return getDAIAvgSupplyRatePerBlock() * BLOCKS_PER_YEAR;
}
/// @notice Returns the supply rate per block this contract experiences on the WBTC market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
/// @return The rate per block at which supply interests are accrued by this contract on the WBTC market (with 18 decimals).
function getWBTCSupplyRatePerBlock() public view returns (uint256) {
return
ILens(LENS).getCurrentUserSupplyRatePerBlock(
CWBTC2, // the WBTC market, represented by the cWBTC2 ERC20 token
address(this) // the address of the user you want to know the supply rate of
);
}
/// @notice Returns the expected APR at which supply interests are accrued by this contract, on the WBTC market.
/// @return The APR at which WBTC supply interests are accrued (with 18 decimals).
function getWBTCSupplyAPR() public view returns (uint256) {
uint256 supplyRatePerBlock = getWBTCSupplyRatePerBlock();
return supplyRatePerBlock * BLOCKS_PER_YEAR;
}
/// @notice Returns the supply APR this contract will experience (at minimum) if it supplies the given amount on the WBTC market.
/// @dev The supply rate experienced on a market is specific to each user,
/// dependending on how their supply is matched peer-to-peer or supplied to the Compound pool.
/// @return The APR at which supply interests are accrued by this contract on the WBTC market (with 18 decimals).
function getWBTCNextSupplyAPR(uint256 _amount)
public
view
returns (uint256)
{
return
ILens(LENS).getNextUserSupplyRatePerBlock(
CWBTC2, // the WBTC market, represented by the cWBTC2 ERC20 token
address(this), // the address of the user you want to know the next supply rate of
_amount
) * BLOCKS_PER_YEAR;
}
/// @notice Returns the expected amount of supply interests accrued by this contract, on the WBTC market, after `_nbBlocks`.
/// @return The expected amount of WBTC supply interests accrued (with 8 decimals, the number of decimals of WBTC).
function getWBTCExpectedAccruedInterests(uint256 _nbBlocks)
public
view
returns (uint256)
{
(uint256 suppliedOnPool, uint256 suppliedP2P) = getWBTCSupplyBalance();
uint256 supplyRatePerBlock = getWBTCSupplyRatePerBlock();
return
(suppliedOnPool + suppliedP2P).mul(supplyRatePerBlock) * _nbBlocks;
}
}
Lens address here.
import ethers from "ethers";
const signer = new ethers.Wallet(
process.env.PRIVATE_KEY,
new ethers.providers.JsonRpcBatchProvider(process.env.RPC_URL)
);
const signerAddress = await signer.getAddress();
const cDaiAddress = "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643";
const cWbtc2Address = "0xccF4429DB6322D5C611ee964527D42E5d685DD6a";
const lens = new ethers.Contract(
"0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67",
[
"function getTotalSupply() external view returns (uint256, uint256, uint256)",
"function getTotalMarketSupply(address) external view returns (uint256, uint256)"