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:

morpho-v1 repository with Lenses

  • For Morpho-Aave-V3, there is no lens but instead, there are snippets provided. The github repository is here:

morpho-snippets repository

๐Ÿ‘‡ 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:

  1. Imports

  2. 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);
        }
    }
}

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;
    }
}

ERC-4626 Vaults

Morpho's vaults are a new way to interact with Morpho according to the ERC-4626 standard.

// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.13;

import {ISupplyVault} from "@morpho-org/morpho-tokenized-vaults/src/compound/interfaces/ISupplyVault.sol";

contract MorphoCompoundVaultSupplier {
    address public constant MC_DAI = 0xd99D793B8FDaE42C1867293C172b9CBBD3ea49FF;

    /// @notice Returns the total balance of DAI this contract has supplied and accrued through the vault.
    /// @return The total balance of DAI this contract has supplied and accrued through the vault.
    function getDAIBalance() public view returns (uint256) {
        return
            ISupplyVault(MC_DAI).convertToAssets(
                ISupplyVault(MC_DAI).balanceOf(address(this))
            );
    }
}

Last updated

Logo

This documentation is provided by MorphoLabs, main contributor to Morpho DAO. For any question, reach out.