|
| 1 | +const sdk = require('@defillama/sdk'); |
| 2 | +const { request, gql } = require('graphql-request'); |
| 3 | +const BigNumber = require('bignumber.js'); |
| 4 | +const ethers = require('ethers'); |
| 5 | + |
| 6 | +const utils = require('../utils'); |
| 7 | + |
| 8 | +BigNumber.config({ EXPONENTIAL_AT: [-1e9, 1e9] }); |
| 9 | + |
| 10 | +const XAI = '0xd7c9f0e536dc865ae858b0c0453fe76d13c3beac' |
| 11 | +const blacklistedSilos = ["0x6543ee07cf5dd7ad17aeecf22ba75860ef3bbaaa",]; |
| 12 | + |
| 13 | +const getAssetAbiV2 = "address:asset"; |
| 14 | +const getSiloConfigMarketId = "uint256:SILO_ID"; |
| 15 | +const getAssetBalanceAbi = "function balanceOf(address account) external view returns (uint256)"; |
| 16 | +const getAssetSymbolAbi = "function symbol() public view returns (string memory)"; |
| 17 | +const getAssetDecimalsAbi = "function decimals() public view returns (uint256)"; |
| 18 | +const getSiloStorageAbi = "function getSiloStorage() external view returns (uint192 daoAndDeployerRevenue, uint64 interestRateTimestamp, uint256 protectedAssets, uint256 collateralAssets, uint256 debtAssets)"; |
| 19 | +const getSiloAssetBorrowAprAbi = "function getBorrowAPR(address _silo) external view returns (uint256 borrowAPR)"; |
| 20 | +const getSiloAssetDepositAprAbi = "function getDepositAPR(address _silo) external view returns (uint256 depositAPR)"; |
| 21 | +const getSiloAssetMaxLtvAbi = "function getMaxLtv(address _silo) external view returns (uint256 maxLtv)"; |
| 22 | +const getAssetStateAbiV2 = 'function getTotalAssetsStorage(uint8 _assetType) external view returns (uint256 totalAssetsByType)'; |
| 23 | + |
| 24 | +const configV2 = { |
| 25 | + sonic: { |
| 26 | + chainName: "Sonic", |
| 27 | + deployments: [ |
| 28 | + { |
| 29 | + START_BLOCK: 2672166, |
| 30 | + SILO_FACTORY: '0xa42001d6d2237d2c74108fe360403c4b796b7170', // Silo V2 Sonic (Main) |
| 31 | + SILO_LENS: "0xB6AdBb29f2D8ae731C7C72036A7FD5A7E970B198", |
| 32 | + } |
| 33 | + ] |
| 34 | + }, |
| 35 | + // arbitrum: { |
| 36 | + // chainName: "Arbitrum", |
| 37 | + // factories: [ |
| 38 | + // { |
| 39 | + // START_BLOCK: 291201890, |
| 40 | + // SILO_FACTORY: '0xf7dc975C96B434D436b9bF45E7a45c95F0521442', // Silo V2 Arbitrum (Main) |
| 41 | + // } |
| 42 | + // ] |
| 43 | + // } |
| 44 | +} |
| 45 | + |
| 46 | +async function getSiloData(api, deploymentData) { |
| 47 | + |
| 48 | + // Handle V2 silos |
| 49 | + let siloData = []; |
| 50 | + let assetAddressToSymbol = {}; |
| 51 | + let assetAddressToDecimals = {}; |
| 52 | + let siloAddressToMarketId = {}; |
| 53 | + let siloAddressToConfigAddress = {}; |
| 54 | + if(configV2[api.chain]) { |
| 55 | + const { |
| 56 | + siloAddresses: siloArrayV2, |
| 57 | + siloAddressesToSiloConfigAddress, |
| 58 | + } = await getSilosV2(api, deploymentData); |
| 59 | + const assetsV2 = await api.multiCall({ |
| 60 | + abi: getAssetAbiV2, |
| 61 | + calls: siloArrayV2.map(i => ({ target: i })), |
| 62 | + }); |
| 63 | + const assetBalancesV2 = await api.multiCall({ |
| 64 | + abi: getAssetBalanceAbi, |
| 65 | + calls: assetsV2.map((asset, i) => ({ target: asset, params: [siloArrayV2[i]] })), |
| 66 | + }); |
| 67 | + const assetBorrowAPR = await api.multiCall({ |
| 68 | + abi: getSiloAssetBorrowAprAbi, |
| 69 | + calls: siloArrayV2.map((siloAddress, i) => ({ target: deploymentData.SILO_LENS, params: [siloAddress] })), |
| 70 | + }); |
| 71 | + const assetDepositAPR = await api.multiCall({ |
| 72 | + abi: getSiloAssetDepositAprAbi, |
| 73 | + calls: siloArrayV2.map((siloAddress, i) => ({ target: deploymentData.SILO_LENS, params: [siloAddress] })), |
| 74 | + }); |
| 75 | + const assetMaxLTV = await api.multiCall({ |
| 76 | + abi: getSiloAssetMaxLtvAbi, |
| 77 | + calls: siloArrayV2.map((siloAddress, i) => ({ target: deploymentData.SILO_LENS, params: [siloAddress] })), |
| 78 | + }); |
| 79 | + const siloStorage = await api.multiCall({ |
| 80 | + abi: getSiloStorageAbi, |
| 81 | + calls: siloArrayV2.map((siloAddress, i) => ({ target: siloAddress, params: [] })), |
| 82 | + }); |
| 83 | + siloData = assetsV2.map((asset, i) => [ |
| 84 | + asset, |
| 85 | + siloArrayV2[i], |
| 86 | + assetBalancesV2[i], |
| 87 | + assetBorrowAPR[i], |
| 88 | + assetDepositAPR[i], |
| 89 | + assetMaxLTV[i], |
| 90 | + siloStorage[i].protectedAssets, |
| 91 | + siloStorage[i].collateralAssets, |
| 92 | + siloStorage[i].debtAssets, |
| 93 | + ]); |
| 94 | + let uniqueAssetAddresses = [...new Set(assetsV2)]; |
| 95 | + const assetSymbols = await api.multiCall({ |
| 96 | + abi: getAssetSymbolAbi, |
| 97 | + calls: uniqueAssetAddresses.map((asset, i) => ({ target: asset })), |
| 98 | + }); |
| 99 | + const assetDecimals = await api.multiCall({ |
| 100 | + abi: getAssetDecimalsAbi, |
| 101 | + calls: uniqueAssetAddresses.map((asset, i) => ({ target: asset })), |
| 102 | + }); |
| 103 | + for(let [index, assetAddress] of uniqueAssetAddresses.entries()) { |
| 104 | + assetAddressToSymbol[assetAddress] = assetSymbols[index]; |
| 105 | + assetAddressToDecimals[assetAddress] = assetDecimals[index]; |
| 106 | + } |
| 107 | + let uniqueSiloConfigAddresses = [...new Set(Object.values(siloAddressesToSiloConfigAddress))]; |
| 108 | + const siloConfigMarketIds = await api.multiCall({ |
| 109 | + abi: getSiloConfigMarketId, |
| 110 | + calls: uniqueSiloConfigAddresses.map((siloConfig, i) => ({ target: siloConfig })), |
| 111 | + }); |
| 112 | + let siloConfigAddressToMarketId = {}; |
| 113 | + for(let [index, siloConfigAddress] of uniqueSiloConfigAddresses.entries()) { |
| 114 | + siloConfigAddressToMarketId[siloConfigAddress] = siloConfigMarketIds[index]; |
| 115 | + } |
| 116 | + for(let [siloAddress, siloConfigAddress] of Object.entries(siloAddressesToSiloConfigAddress)) { |
| 117 | + siloAddressToConfigAddress[siloAddress] = siloConfigAddress; |
| 118 | + siloAddressToMarketId[siloAddress] = siloConfigAddressToMarketId[siloConfigAddress]; |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + let assetPrices = await utils.getPrices(siloData.map(entry => entry[0]), api.chain); |
| 123 | + |
| 124 | + let assetDataBySilo = {}; |
| 125 | + for( |
| 126 | + let [ |
| 127 | + assetAddress, |
| 128 | + siloAddress, |
| 129 | + assetBalance, |
| 130 | + assetBorrowAPR, |
| 131 | + assetDepositAPR, |
| 132 | + assetMaxLTV, |
| 133 | + protectedAssetBalance, |
| 134 | + collateralAssetBalance, |
| 135 | + debtAssetBalance |
| 136 | + ] of siloData |
| 137 | + ) { |
| 138 | + |
| 139 | + let assetBalanceFormatted = new BigNumber( |
| 140 | + ethers.utils.formatUnits( |
| 141 | + assetBalance, |
| 142 | + assetAddressToDecimals[assetAddress] |
| 143 | + ) |
| 144 | + ).toString(); |
| 145 | + |
| 146 | + let assetBalanceValueUSD = new BigNumber( |
| 147 | + ethers.utils.formatUnits( |
| 148 | + assetBalance, |
| 149 | + assetAddressToDecimals[assetAddress] |
| 150 | + ) |
| 151 | + ) |
| 152 | + .multipliedBy(assetPrices.pricesByAddress[assetAddress.toLowerCase()]) |
| 153 | + .toString(); |
| 154 | + |
| 155 | + let assetBorrowAprFormatted = new BigNumber( |
| 156 | + ethers.utils.formatUnits( |
| 157 | + assetBorrowAPR, |
| 158 | + 16 |
| 159 | + ) |
| 160 | + ).toString(); |
| 161 | + |
| 162 | + let assetMaxLtvFormatted = new BigNumber( |
| 163 | + ethers.utils.formatUnits( |
| 164 | + assetMaxLTV, |
| 165 | + 18 |
| 166 | + ) |
| 167 | + ).toString(); |
| 168 | + |
| 169 | + let assetDepositAprFormatted = new BigNumber( |
| 170 | + ethers.utils.formatUnits( |
| 171 | + assetDepositAPR, |
| 172 | + 16 |
| 173 | + ) |
| 174 | + ).toString(); |
| 175 | + |
| 176 | + let totalBorrowValueUSD = new BigNumber( |
| 177 | + ethers.utils.formatUnits( |
| 178 | + debtAssetBalance, |
| 179 | + assetAddressToDecimals[assetAddress] |
| 180 | + ) |
| 181 | + ) |
| 182 | + .multipliedBy(assetPrices.pricesByAddress[assetAddress.toLowerCase()]) |
| 183 | + .toString(); |
| 184 | + |
| 185 | + let totalSupplyRaw = new BigNumber(collateralAssetBalance).toString(); |
| 186 | + let totalSupplyValueUSD = new BigNumber( |
| 187 | + ethers.utils.formatUnits( |
| 188 | + totalSupplyRaw, |
| 189 | + assetAddressToDecimals[assetAddress] |
| 190 | + ) |
| 191 | + ) |
| 192 | + .multipliedBy(assetPrices.pricesByAddress[assetAddress.toLowerCase()]) |
| 193 | + .toString(); |
| 194 | + |
| 195 | + assetDataBySilo[siloAddress] = { |
| 196 | + assetAddress: assetAddress, |
| 197 | + assetSymbol: assetAddressToSymbol[assetAddress], |
| 198 | + assetDecimals: assetAddressToDecimals[assetAddress], |
| 199 | + assetBalanceRaw: assetBalance, |
| 200 | + assetBalanceFormatted: assetBalanceFormatted, |
| 201 | + assetBalanceValueUSD: assetBalanceValueUSD, |
| 202 | + assetPrice: assetPrices.pricesByAddress[assetAddress.toLowerCase()], |
| 203 | + assetBorrowAprRaw: assetBorrowAPR, |
| 204 | + assetDepositAprRaw: assetDepositAPR, |
| 205 | + assetBorrowAprFormatted: assetBorrowAprFormatted, |
| 206 | + assetDepositAprFormatted: assetDepositAprFormatted, |
| 207 | + marketId: siloAddressToMarketId[siloAddress], |
| 208 | + siloConfig: siloAddressToConfigAddress[siloAddress], |
| 209 | + totalSupplyRaw: totalSupplyRaw, |
| 210 | + totalSupplyValueUSD: totalSupplyValueUSD, |
| 211 | + totalBorrowRaw: debtAssetBalance, |
| 212 | + totalBorrowValueUSD: totalBorrowValueUSD, |
| 213 | + assetMaxLtvRaw: assetMaxLTV, |
| 214 | + assetMaxLtvFormatted: assetMaxLtvFormatted, |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + return assetDataBySilo; |
| 219 | +} |
| 220 | + |
| 221 | +async function getSilosV2(chainApi, deploymentData) { |
| 222 | + const chain = chainApi.chain; |
| 223 | + let logs = []; |
| 224 | + let siloAddresses = []; |
| 225 | + let siloAddressesToSiloConfigAddress = {}; |
| 226 | + if(configV2[chain]) { |
| 227 | + let latestBlock = await sdk.api.util.getLatestBlock(chain); |
| 228 | + const iface = new ethers.utils.Interface([ |
| 229 | + 'event NewSilo(address indexed implementation, address indexed token0, address indexed token1, address silo0, address silo1, address siloConfig)', |
| 230 | + ]); |
| 231 | + const { SILO_FACTORY, START_BLOCK } = deploymentData; |
| 232 | + let logChunk = await sdk.api2.util.getLogs({ |
| 233 | + target: SILO_FACTORY, |
| 234 | + topics: ['0x3d6b896c73b628ec6ba0bdfe3cdee1356ea2af31af2a97bbd6b532ca6fa00acb'], |
| 235 | + keys: [], |
| 236 | + fromBlock: START_BLOCK, |
| 237 | + toBlock: latestBlock.block, |
| 238 | + chain: chain, |
| 239 | + }); |
| 240 | + logs = [...logs, ...logChunk.output.map((event) => iface.parseLog(event))]; |
| 241 | + |
| 242 | + siloAddresses = logs.flatMap((log) => { |
| 243 | + |
| 244 | + let silo0 = log.args[3]; |
| 245 | + let silo1 = log.args[4]; |
| 246 | + let siloConfig = log.args[5]; |
| 247 | + |
| 248 | + siloAddressesToSiloConfigAddress[silo0] = siloConfig; |
| 249 | + siloAddressesToSiloConfigAddress[silo1] = siloConfig; |
| 250 | + |
| 251 | + return [silo0, silo1].filter( |
| 252 | + (address) => blacklistedSilos.indexOf(address.toLowerCase()) === -1 |
| 253 | + ); |
| 254 | + }); |
| 255 | + |
| 256 | + } |
| 257 | + |
| 258 | + return { siloAddresses, siloAddressesToSiloConfigAddress }; |
| 259 | +} |
| 260 | + |
| 261 | +const main = async () => { |
| 262 | + |
| 263 | + const markets = []; |
| 264 | + |
| 265 | + for(let [chain, config] of Object.entries(configV2)) { |
| 266 | + |
| 267 | + const api = new sdk.ChainApi({ chain }); |
| 268 | + |
| 269 | + for(let deploymentData of config.deployments) { |
| 270 | + |
| 271 | + let siloData = await getSiloData(api, deploymentData); |
| 272 | + |
| 273 | + for(let [siloAddress, siloInfo] of Object.entries(siloData)) { |
| 274 | + |
| 275 | + let marketData = { |
| 276 | + pool: `${siloInfo.marketId}-${siloAddress}-${chain}`, |
| 277 | + chain: config.chainName, |
| 278 | + project: 'silo-v2', |
| 279 | + symbol: utils.formatSymbol(siloInfo.assetSymbol), |
| 280 | + tvlUsd: new BigNumber(siloInfo.assetBalanceValueUSD).toNumber(), |
| 281 | + apyBase: new BigNumber(siloInfo.assetDepositAprFormatted).toNumber(), |
| 282 | + apyBaseBorrow: new BigNumber(siloInfo.assetBorrowAprFormatted).toNumber(), |
| 283 | + url: `https://v2.silo.finance/markets/${chain}/${siloInfo.marketId}`, |
| 284 | + underlyingTokens: [siloInfo.assetAddress], |
| 285 | + ltv: Number(siloInfo.assetMaxLtvFormatted), |
| 286 | + totalBorrowUsd: Number(Number(siloInfo.totalBorrowValueUSD).toFixed(2)), |
| 287 | + totalSupplyUsd: Number(Number(siloInfo.totalSupplyValueUSD).toFixed(2)), |
| 288 | + poolMeta: `${siloInfo.marketId}`, |
| 289 | + }; |
| 290 | + |
| 291 | + markets.push(marketData); |
| 292 | + } |
| 293 | + |
| 294 | + } |
| 295 | + |
| 296 | + } |
| 297 | + |
| 298 | + return markets; |
| 299 | +}; |
| 300 | + |
| 301 | +module.exports = { |
| 302 | + timetravel: false, |
| 303 | + apy: main, |
| 304 | +}; |
0 commit comments