diff --git a/coins/src/adapters/index.ts b/coins/src/adapters/index.ts index 98e09c197f..ee6b72f79e 100644 --- a/coins/src/adapters/index.ts +++ b/coins/src/adapters/index.ts @@ -7,6 +7,7 @@ import * as balancer from "./markets/balancer"; import * as others from "./other/index"; import * as others2 from "./other/others2"; import * as graphCoins from "./markets/graphCoins"; +import * as morpho from "./moneyMarkets/morpho"; export default { ...compound.adapters, @@ -18,6 +19,7 @@ export default { ...others.adapters, ...others2.adapters, ...graphCoins.adapters, + ...morpho.adapters, fraxtalGas: require("./other/fraxtalGas"), reservoirprotocol: require("./rwa/reservoir-protocol"), trize: require("./rwa/t-rize"), @@ -174,4 +176,5 @@ export default { goblin: require("./markets/goblin"), ember: require("./yield/ember"), suirewards: require("./markets/suirewards"), + axlp: require("./liquidStaking/axlp"), }; diff --git a/coins/src/adapters/liquidStaking/axlp.ts b/coins/src/adapters/liquidStaking/axlp.ts new file mode 100644 index 0000000000..6f854aeb54 --- /dev/null +++ b/coins/src/adapters/liquidStaking/axlp.ts @@ -0,0 +1,66 @@ +import { Write } from "../utils/dbInterfaces"; +import { addToDBWritesList } from "../utils/database"; +import { getApi } from "../utils/sdk"; + +const contracts: { [chain: string]: { [key: string]: { symbol: string, token: string, stakingContract: string, rate: number } } } = { + "arbitrum": { + "amlp": { + "symbol": "AMLP", + "token": "0x152f5E6142db867f905a68617dBb6408D7993a4b", + "stakingContract": "0x3a66b81be26f2d799C5A96A011e1E3FB2BA50999", + "rate": 1.0 + }, + "ahlp": { + "symbol": "AHLP", + "token": "0x5fd22dA8315992dbbD82d5AC1087803ff134C2c4", + "stakingContract": "0x1ba274EBbB07353657ed8C76A87ACf362E408D85", + "rate": 1.0 + } + } +} + +export async function axlp(timestamp: number = 0) { + const writes: Write[] = []; + + await Promise.all( + Object.keys(contracts).map(async (chain) => { + const { amlp, ahlp } = contracts[chain]; + const api = await getApi(chain, timestamp); + const amlpPrice = await api.call({ + target: amlp.stakingContract, + abi: "uint256:price", + }); + + addToDBWritesList( + writes, + chain, + amlp.token, + amlpPrice / 10 ** 18, + 18, + amlp.symbol, + timestamp, + "amlp", + 1 + ); + + const ahlpPrice = await api.call({ + target: ahlp.stakingContract, + abi: "uint256:price", + }); + + addToDBWritesList( + writes, + chain, + ahlp.token, + ahlpPrice / 10 ** 18, + 18, + ahlp.symbol, + timestamp, + "ahlp", + ahlp.rate + ); + }) + ); + + return writes; +} \ No newline at end of file diff --git a/coins/src/adapters/moneyMarkets/morpho.ts b/coins/src/adapters/moneyMarkets/morpho.ts new file mode 100644 index 0000000000..0e9554b996 --- /dev/null +++ b/coins/src/adapters/moneyMarkets/morpho.ts @@ -0,0 +1,285 @@ +import { runInPromisePool } from "@defillama/sdk/build/generalUtil"; +import { getApi } from "../utils/sdk"; +import { getCurrentUnixTimestamp } from "../../utils/date"; +import { Write } from "../utils/dbInterfaces"; +import { addToDBWritesList } from "../utils/database"; +import { getTokenInfoMap } from "../utils/erc20"; +import { request } from "@defillama/sdk/build/util/graph"; +import { getConfig } from "../../utils/cache"; + +type VaultDatas = { + [vault: string]: { + totalAssets: number; + positions: any[]; + markets: string[]; + }; +}; + +const listaConfig: { [chain: string]: { vault: string; vaultInfo: string } } = { + bsc: { + vault: "0x8F73b65B4caAf64FBA2aF91cC5D4a2A1318E5D8C", + vaultInfo: + "https://api.lista.org/api/moolah/vault/list?page=1&pageSize=1000", + }, + ethereum: { + vault: "0xf820fB4680712CD7263a0D3D024D5b5aEA82Fd70", + vaultInfo: + "https://api.lista.org/api/moolah/vault/list?page=1&pageSize=1000&chain=ethereum", + }, +}; + +async function fetchMorphoVaultAddresses(chainId: string) { + let assets: { [vault: string]: string } = {}; + let skip = 0; + let length = 1000; + + while (length == 1000) { + const query = ` + query { + vaults (first: ${length}, skip: ${skip}, orderBy: Address, where: { + chainId_in: [${chainId}] + }) { + items { + asset { + address + } + address + } + }}`; + + const res = await request("https://api.morpho.org/graphql", query, { cache: true, cacheKey: `morpho-vaults-${skip}` }); + res.vaults.items.forEach((item: any) => { + assets[item.address.toLowerCase()] = item.asset.address.toLowerCase(); + }); + length = res.vaults.items.length; + skip += length; + } + + return assets; +} + +async function morpho( + timestamp: number = 0, + vaultAssets: { [vault: string]: string } = {}, + api: any, + target: string +) { + const threeDaysAgo = + (timestamp == 0 ? getCurrentUnixTimestamp() : timestamp) - 3 * 24 * 60 * 60; + const threeDaysAgoApi = await getApi(api.chain, threeDaysAgo); + + if (!api.chainId) throw new Error("Chain ID not found"); + const allMarkets: string[] = []; + const currentVaultDatas: VaultDatas = {}; + const previousVaultDatas: VaultDatas = {}; + + await runInPromisePool({ + items: Object.keys(vaultAssets), + concurrency: 5, + processor: (vault: string) => fetchVaultPositions(vault, api, true), + }); + + await runInPromisePool({ + items: Object.keys(vaultAssets), + concurrency: 5, + processor: (vault: string) => fetchVaultPositions(vault, threeDaysAgoApi, false), + }); + + async function fetchVaultPositions(vault: string, api: any, isCurrent: boolean) { + const totalAssets = await api.call({ + target: vault, + abi: "uint256:totalAssets", + permitFailure: true, + }); + + const withdrawQueueLength = await api.call({ + target: vault, + abi: "uint256:withdrawQueueLength", + permitFailure: true, + }); + + const markets = await api.multiCall({ + target: vault, + abi: "function withdrawQueue(uint256 index) view returns (bytes32)", + calls: Array.from({ length: withdrawQueueLength }, (_, i) => ({ + params: i, + })), + permitFailure: true, + }); + + // vaults position in market + const positions = await api.multiCall({ + target, + abi: "function position(bytes32, address) view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)", + calls: markets.map((market: string) => ({ + params: [market, vault], + })), + permitFailure: true, + }); + + allMarkets.push(...markets); + isCurrent + ? (currentVaultDatas[vault] = { totalAssets, positions, markets }) + : (previousVaultDatas[vault] = { totalAssets, positions, markets }); + } + + const uniqueMarkets = [...new Set(allMarkets)]; + const [currentMarketData, previousMarketData] = await Promise.all([ + fetchMarketData(api), + fetchMarketData(threeDaysAgoApi), + ]); + + async function fetchMarketData(api: any) { + const marketDataArray = await api.multiCall({ + target, + abi: "function market(bytes32) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)", + calls: uniqueMarkets.map((market: string) => ({ params: market })), + permitFailure: true, + }); + const marketData: { + [market: string]: { + totalSupplyAssets: number; + totalSupplyShares: number; + totalBorrowAssets: number; + }; + } = {}; + marketDataArray.forEach((m: any, i: number) => { + marketData[uniqueMarkets[i]] = m; + }); + + return marketData; + } + + const currentTotalWithdrawables = aggregateWithdrawable( + currentVaultDatas, + currentMarketData + ); + const previousTotalWithdrawables = aggregateWithdrawable( + previousVaultDatas, + previousMarketData + ); + + function aggregateWithdrawable(vaultDatas: VaultDatas, marketData: any) { + let totalWithdrawables: { [vault: string]: number } = {}; + Object.keys(vaultDatas).map((vault: string) => { + totalWithdrawables[vault] = 0; + const { positions, markets } = vaultDatas[vault]; + + markets.map((market: string, i: number) => { + const { totalSupplyAssets, totalSupplyShares, totalBorrowAssets } = + marketData[market]; + if (positions[i].supplyShares == 0) return; + const supplyAssets = + (positions[i].supplyShares * totalSupplyAssets) / totalSupplyShares; + + const availableLiquidity = Math.max( + totalSupplyAssets - totalBorrowAssets, + 0 + ); + + const withdrawable = Math.min(supplyAssets, availableLiquidity); + + totalWithdrawables[vault] += Number(withdrawable); + }); + }); + + return totalWithdrawables; + } + + const problemVaultList: string[] = []; + Object.keys(currentVaultDatas).map((vault: string) => { + const { totalAssets } = currentVaultDatas[vault]; + if (totalAssets == 0) return; + + const currentWithdrawable = currentTotalWithdrawables[vault]; + const previousWithdrawable = previousTotalWithdrawables[vault]; + if (currentWithdrawable / totalAssets > 0.01) return; + + if (!previousVaultDatas[vault]) { + if (currentWithdrawable / totalAssets < 0.01) + console.log( + `Bad debt in vault ${vault} on ${api.chain}: ${((currentWithdrawable / totalAssets) * 100).toFixed(2)}% liquidity` + ); + + problemVaultList.push(vault); + return; + } + + const { totalAssets: previousTotalAssets } = previousVaultDatas[vault]; + if ( + previousWithdrawable && + previousWithdrawable / previousTotalAssets > 0.01 + ) + return; + + problemVaultList.push(vault); + console.log( + `Bad debt in vault ${vault} on ${api.chain}: ${((currentWithdrawable / totalAssets) * 100).toFixed(2)}% liquidity` + ); + }); + + const metadata = await getTokenInfoMap(api.chain, problemVaultList); + + const writes: Write[] = []; + problemVaultList.forEach(async (vault: string) => { + const { symbol, decimals } = metadata[vault]; + if (!symbol || !decimals) return; + addToDBWritesList( + writes, + api.chain, + vault, + 0, + decimals, + symbol, + timestamp, + "morpho", + 1.01 + ); + }); + + return writes; +} + +async function getListaVaults(chain: string) { + const { + data: { list: vaults }, + } = await getConfig('lista-lend-vaults', listaConfig[chain].vaultInfo) + const listaVaults: { [vault: string]: string } = {}; + vaults.map( + (vault: any) => + (listaVaults[vault.address.toLowerCase()] = vault.asset.toLowerCase()) + ); + return listaVaults; +} + +async function lista(timestamp: number = 0) { + return await Promise.all( + Object.keys(listaConfig).map(async (chain) => { + const api = await getApi(chain, timestamp); + const vaults = await getListaVaults(chain); + return await morpho(timestamp, vaults, api, listaConfig[chain].vault); + }) + ); +} + +export async function morphoBlue(timestamp: number = 0) { + const chains = ["ethereum", "base", "hyperliquid", "katana", "arbitrum", "wc", "unichain", "polygon", "optimism", "plume_mainnet", "sei", "lisk", "etlk"]; + return await Promise.all( + chains.map(async (chain) => { + const api = await getApi(chain, timestamp); + if (!api.chainId) return; + const vaults = await fetchMorphoVaultAddresses(api.chainId.toString()); + return await morpho( + timestamp, + vaults, + api, + "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb" + ); + }) + ); +} + +export const adapters = { + // morphoBlue, + lista, +} as any; diff --git a/coins/src/adapters/oracles/pyth.ts b/coins/src/adapters/oracles/pyth.ts index 143ce633f0..2bf0d334d7 100644 --- a/coins/src/adapters/oracles/pyth.ts +++ b/coins/src/adapters/oracles/pyth.ts @@ -43,4 +43,4 @@ export async function pyth(timestamp: number = now) { }) return writes; -} +} \ No newline at end of file diff --git a/coins/src/adapters/other/unknownTokensV3.ts b/coins/src/adapters/other/unknownTokensV3.ts index 00981bf5a1..355305844c 100644 --- a/coins/src/adapters/other/unknownTokensV3.ts +++ b/coins/src/adapters/other/unknownTokensV3.ts @@ -1,5 +1,6 @@ import { getApi } from "../utils/sdk"; import getWrites from "../utils/getWrites"; +import { runInPromisePool } from "@defillama/sdk/build/generalUtil"; const projectName = "unknownTokensV3"; const slot0Abi = @@ -73,6 +74,15 @@ const config: any = { }, }; +const configCustomAbi: any = { + base: { + "0xe66E3A37C3274Ac24FE8590f7D84A2427194DC17": { + pool: "0x9d228792a392838be03293ba93f406f3e8077b8d", // stkWELL + abi: "function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, bool unlocked)" + } + }, +} + export function unknownTokensV3(timestamp: number = 0) { return Promise.all( Object.keys(config).map((chain) => getTokenPrice(chain, timestamp)), @@ -82,11 +92,21 @@ export function unknownTokensV3(timestamp: number = 0) { async function getTokenPrice(chain: string, timestamp: number) { const api = await getApi(chain, timestamp); const pricesObject: any = {}; + const pools: any = Object.values(config[chain]); - const tokens = Object.keys(config[chain]); - const token0s = await api.multiCall({ abi: "address:token0", calls: pools }); - const token1s = await api.multiCall({ abi: "address:token1", calls: pools }); - const slot0s = await api.multiCall({ abi: slot0Abi, calls: pools }); + const customAbiPools: any = Object.values(configCustomAbi[chain] ?? {}); + + const tokens = [...Object.keys(config[chain]), ...Object.keys(configCustomAbi[chain])]; + const token0s = await api.multiCall({ abi: "address:token0", calls: [...pools, ...customAbiPools.map((p: any) => p.pool)] }); + const token1s = await api.multiCall({ abi: "address:token1", calls: [...pools, ...customAbiPools.map((p: any) => p.pool)] }); + + const regularSlot0s = await api.multiCall({ abi: slot0Abi, calls: pools }); + const customSlot0s: any[] = await runInPromisePool({ + items: customAbiPools, + concurrency: 5, + processor: async (p: any) => api.call({ abi: p.abi, target: p.pool }) + }) + const tokens0Decimals = await api.multiCall({ abi: "erc20:decimals", calls: token0s, @@ -96,7 +116,7 @@ async function getTokenPrice(chain: string, timestamp: number) { calls: token1s, }); - slot0s.forEach((v: any, i: number) => { + [...regularSlot0s, ...customSlot0s].forEach((v: any, i: number) => { const token = tokens[i].toLowerCase(); let token0 = token0s[i].toLowerCase(); let price = diff --git a/coins/src/adapters/rwa/midas.ts b/coins/src/adapters/rwa/midas.ts index 6ef4ee789f..e064938d49 100644 --- a/coins/src/adapters/rwa/midas.ts +++ b/coins/src/adapters/rwa/midas.ts @@ -207,6 +207,7 @@ const contracts: Record = { ], plasma: [ { name: 'mHyper', token: '0xb31BeA5c2a43f942a3800558B1aa25978da75F8a', oracle: '0x2EB410e4cb94E2E9E3cdE3F7b405BE4fCC076Bc9' }, + { name: 'mXRP', token: '0xc8739fbBd54C587a2ad43b50CbcC30ae34FE9e34', oracle: '0x3BdE0b7B59769Ec00c44C77090D88feB4516E731', denomination: 'XRP' }, ], bsc: [{ name: 'mXRP', diff --git a/coins/src/adapters/tokenMapping.json b/coins/src/adapters/tokenMapping.json index 189a5143e1..08cd93b70a 100644 --- a/coins/src/adapters/tokenMapping.json +++ b/coins/src/adapters/tokenMapping.json @@ -12833,5 +12833,42 @@ "decimals": 18, "symbol": "KREX" } + }, + "mon": { + "0xea17E5a9efEBf1477dB45082d67010E2245217f1": { + "to": "coingecko#solana", + "decimals": 9, + "symbol": "SOL" + }, + "0x754704Bc059F8C67012fEd69BC8A327a5aafb603": { + "to": "coingecko#usd-coin", + "decimals": 6, + "symbol": "USDC" + }, + "0xe7cd86e13AC4309349F30B3435a9d337750fC82D": { + "to": "coingecko#tether", + "decimals": 6, + "symbol": "USDT0" + }, + "0xEE8c0E9f1BFFb4Eb878d8f15f368A02a35481242": { + "to": "coingecko#ethereum", + "decimals": 18, + "symbol": "WETH" + }, + "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A": { + "to": "coingecko#monad", + "decimals": 18, + "symbol": "WMON" + }, + "0x0000000000000000000000000000000000000000": { + "to": "coingecko#monad", + "decimals": 18, + "symbol": "MON" + }, + "0x01bFF41798a0BcF287b996046Ca68b395DbC1071": { + "to": "coingecko#tether-gold", + "decimals": 6, + "symbol": "XAUt0" + } } } diff --git a/coins/src/adapters/yield/derivs.ts b/coins/src/adapters/yield/derivs.ts index ffc2334f0f..45525aaa4c 100644 --- a/coins/src/adapters/yield/derivs.ts +++ b/coins/src/adapters/yield/derivs.ts @@ -587,6 +587,19 @@ const configs: { [adapter: string]: Config } = { underlying: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", address: "0xcd3c0F51798D1daA92Fb192E57844Ae6cEE8a6c7", }, + ankrFLOWEVM: { + rate: async ({ api }) => { + const rate = await api.call({ + abi: "function sharesToBonds(uint256) external view returns (uint256)", + target: "0x1b97100ea1d7126c4d60027e231ea4cb25314bdb", + params: "1000000", + }); + return 1e6 / rate; + }, + chain: "flow", + underlying: "0xd3bf53dac106a0290b0483ecbc89d40fcc961f3e", + address: "0x1b97100ea1d7126c4d60027e231ea4cb25314bdb", + } }; export async function derivs(timestamp: number) { diff --git a/coins/src/adapters/yield/misc4626/tokens.json b/coins/src/adapters/yield/misc4626/tokens.json index 1a521b9a51..ba9172ccbd 100644 --- a/coins/src/adapters/yield/misc4626/tokens.json +++ b/coins/src/adapters/yield/misc4626/tokens.json @@ -179,7 +179,8 @@ "gtUSDCf": "0x236919F11ff9eA9550A4287696C2FC9e18E6e890", "exmUSDC": "0x23479229e52Ab6aaD312D0B03DF9F33B46753B5e", "steakUSDC": "0xbeeF010f9cb27031ad51e3333f9aF9C6B1228183", - "yETH": "0x1F52Edf2815BfA625890B61d6bf43dDC24671Fe8" + "yETH": "0x1F52Edf2815BfA625890B61d6bf43dDC24671Fe8", + "RE7USDC": "0x12afdefb2237a5963e7bab3e2d46ad0eee70406e" }, "sapphire": { "wstRose": "0x3cabbe76ea8b4e7a2c0a69812cbe671800379ec8"