Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions src/adaptors/monolith-market/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const sdk = require('@defillama/sdk');
const utils = require('../utils');
const lensAbi = require('./lensAbi');
const lenderAbi = require('./lenderAbi');
const vaultAbi = require('./vaultAbi');

const PROJECT = 'monolith-market';

const FACTORIES = {
ethereum: { chainId: 1, blocksPerYear: 2609750, factory: '0x6D961c9DCF1AD73566822BA4B087892e3839B849', lens: '0x0f3a7cd1828698D2B6daEf081d5c319c0734fA1c', fromBlock: 24949282 },
}

const simpleCalls = (arr, params) => {
return arr.map(a => ({ target: a, params, permitFailure: true }))
}

async function getChainPools(chain) {
const latestBlock = await sdk.api.util.getLatestBlock(chain);

const toBlock = latestBlock.number;

const { factory, chainId, lens, blocksPerYear, fromBlock } = FACTORIES[chain];

const logs = await sdk.getEventLogs({
target: factory,
fromBlock,
toBlock,
chain,
eventAbi: 'event Deployed(address indexed lender, address indexed coin, address indexed vault)',
});

const lenders = logs.map(l => l.args.lender);
const coins = logs.map(l => l.args.coin);
const vaults = logs.map(l => l.args.vault);

const [
rates,
vaultSymbols,
coinSymbols,
vaultAssets,
totalPaidDebts,
totalFreeDebts,
collateralFactors,
collaterals,
collateralsPriceData,
] = (await Promise.all([
sdk.api.abi.multiCall({
chain, abi: lensAbi.find(a => a.name === 'getRates'), calls: lenders.map(lender => {
return {
target: lens,
params: [lender],
permitFailure: true,
}
})
}),
sdk.api.abi.multiCall({ chain, abi: 'erc20:symbol', calls: simpleCalls(vaults) }),
sdk.api.abi.multiCall({ chain, abi: 'erc20:symbol', calls: simpleCalls(coins) }),
sdk.api.abi.multiCall({ chain, abi: vaultAbi.find(a => a.name === 'totalAssets'), calls: simpleCalls(vaults) }),
sdk.api.abi.multiCall({ chain, abi: lenderAbi.find(a => a.name === 'totalPaidDebt'), calls: simpleCalls(lenders) }),
sdk.api.abi.multiCall({ chain, abi: lenderAbi.find(a => a.name === 'totalFreeDebt'), calls: simpleCalls(lenders) }),
sdk.api.abi.multiCall({ chain, abi: lenderAbi.find(a => a.name === 'collateralFactor'), calls: simpleCalls(lenders) }),
sdk.api.abi.multiCall({ chain, abi: lenderAbi.find(a => a.name === 'collateral'), calls: simpleCalls(lenders) }),
sdk.api.abi.multiCall({ chain, abi: lenderAbi.find(a => a.name === 'getCollateralPrice'), calls: simpleCalls(lenders) }),
])).map(r => r.output.map(o => o.output));

const [
collateralSymbols,
collateralDecimals,
collateralDeposits,
pricesData,
] = (await Promise.all([
sdk.api.abi.multiCall({ chain, abi: 'erc20:symbol', calls: simpleCalls(collaterals) }),
sdk.api.abi.multiCall({ chain, abi: 'erc20:decimals', calls: simpleCalls(collaterals) }),
sdk.api.abi.multiCall({
chain, abi: 'erc20:balanceOf', calls: collaterals.map((col,i) => {
return {
target: col,
params: [lenders[i]],
permitFailure: true,
}
})
}),
utils.getPrices(coins.concat(collaterals), chain),
])).map(r => r.output ? r.output.map(o => o.output) : r);

const { pricesByAddress } = pricesData;

// CDP markets, where coins are minted against collaterals
const cdpMarkets = lenders.map((m, marketIndex) => {
const collateral = collaterals[marketIndex];
const collateralSymbol = collateralSymbols[marketIndex];
const collateralDecimal = collateralDecimals[marketIndex];
const mintedCoin = coinSymbols[marketIndex];

const coin = coins[marketIndex];
const coinPriceUsd = pricesByAddress[coin.toLowerCase()] || 0;

const { price: oraclePrice } = collateralsPriceData[marketIndex];
const oraclePriceUsd = (Number(oraclePrice) / (10 ** (36 - collateralDecimal))) || 0;
// use defillama if available otherwise fallback to oracle price
const collateralPriceUsd = pricesByAddress[collateral.toLowerCase()] || oraclePriceUsd;
const totalSupplyUsd = collateralPriceUsd * Number(collateralDeposits[marketIndex]) / (10 ** collateralDecimal)
const totalBorrowUsd = coinPriceUsd * (Number(totalPaidDebts[marketIndex]) / 1e18 + Number(totalFreeDebts[marketIndex]) / 1e18);
const borrowApr = Math.min(Number(rates[marketIndex][0]) / 1e16, 999_999_999);
const borrowApy = borrowApr < 999_999_999 ? Math.min(999_999_999, utils.aprToApy(borrowApr, blocksPerYear)) : 999_999_999;

return {
pool: `monolith-market-lending-${m}`,
chain: 'Ethereum',
project: PROJECT,
symbol: collateralSymbol,
mintedCoin,
borrowToken: coin,
apyBase: 0,
// cdp => tvlUsd = totalSupplyUsd
tvlUsd: totalSupplyUsd,
underlyingTokens: [collateral],
url: 'https://app.monolith.market/1/coin/' + marketIndex,
totalSupplyUsd,
totalBorrowUsd,
apyBaseBorrow: borrowApy,
borrowable: true,
ltv: Number(collateralFactors[marketIndex]) / 1e4,
};
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// savings vaults (ERC4626), a vault's asset is the coin minted by the cdp markets
const savingsVaults = lenders.map((m, marketIndex) => {
const underlying = coins[marketIndex];
const vaultSymbol = vaultSymbols[marketIndex];
const coinPriceUsd = pricesByAddress[underlying.toLowerCase()] || 0;
const totalSupplyUsd = coinPriceUsd * Number(vaultAssets[marketIndex]) / 1e18
const stakingApr = Number(rates[marketIndex][1]) / 1e16;
const apy = utils.aprToApy(stakingApr, blocksPerYear);

return {
pool: `monolith-market-savings-${vaults[marketIndex]}`,
chain: 'Ethereum',
project: PROJECT,
symbol: vaultSymbol,
tvlUsd: totalSupplyUsd,
apyBase: apy,
underlyingTokens: [underlying],
url: 'https://app.monolith.market/1/coin/' + marketIndex,
totalSupplyUsd,
borrowable: false,
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

return cdpMarkets.concat(savingsVaults).filter((p) => utils.keepFinite(p));
}

async function apy() {
const factoriesChains = Object.keys(FACTORIES);

const chainPools = await Promise.all(
factoriesChains.map(factoryChain => getChainPools(factoryChain))
)

return chainPools.flat();
}

module.exports = {
timetravel: false,
apy,
url: 'https://app.monolith.market',
};
Loading
Loading