diff --git a/.env.dev b/.env.dev index 6296fb2d..51611970 100644 --- a/.env.dev +++ b/.env.dev @@ -20,6 +20,7 @@ ZKSYNC-ERA_NODE_URL=https://mainnet.era.zksync.io ELYSIUM_NODE_URL=https://rpc.elysiumchain.tech CORE_NODE_URL=https://rpc-core.icecreamswap.com BASE_NODE_URL=https://base.llamarpc.com +LINEA_NODE_URL=https://rpc.linea.build ##======================== LOGSTASH ======================== LOGSTASH_PORT= LOGSTASH_HOST= diff --git a/src/app.config.ts b/src/app.config.ts index aaa8e0ae..c85b03e7 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -56,5 +56,6 @@ nodeUrls['ZKSYNC-ERA_NODE_URL'] = process.env['ZKSYNC-ERA_NODE_URL']; nodeUrls['ELYSIUM_NODE_URL'] = process.env['ELYSIUM_NODE_URL']; nodeUrls['BASE_NODE_URL'] = process.env['BASE_NODE_URL']; nodeUrls['CORE_NODE_URL'] = process.env['CORE_NODE_URL']; +nodeUrls['LINEA_NODE_URL'] = process.env['LINEA_NODE_URL']; export { config, nodeUrls }; diff --git a/src/constants/contracts.json b/src/constants/contracts.json index 4fa57447..5d32f220 100644 --- a/src/constants/contracts.json +++ b/src/constants/contracts.json @@ -14,7 +14,8 @@ "arbitrum": "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", "zksync-era": "0x5aea5775959fbc2557cc8789bc1bf90a239d9a91", "base": "0x4200000000000000000000000000000000000006", - "core": "0x40375C92d9FAf44d2f9db9Bd9ba41a3317a2404f" + "core": "0x40375C92d9FAf44d2f9db9Bd9ba41a3317a2404f", + "linea": "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f" }, "BULK_BALANCE_ADDRESSES": { "ethereum": "0xb173393e08496209ad1cd9d57c769de76bdcea5a", @@ -31,7 +32,8 @@ "arbitrum": "0x63acca16a1fcfd406d3ea8ec5137884861863828", "zksync-era": "0x19b86d343394427ccc6c82c921b68305feaee833", "base": "0x63acca16a1fcfd406d3ea8ec5137884861863828", - "core": "0xad4E66ac498FD091Af2E6185f204654B6Ac7bFcc" + "core": "0xad4E66ac498FD091Af2E6185f204654B6Ac7bFcc", + "linea": "0x63aCCa16a1fcfD406d3EA8Ec5137884861863828" }, "BULK_BALANCE_DEPOLYED": { "ethereum": 11439603, @@ -48,7 +50,8 @@ "arbitrum": 86897490, "zksync-era": 6397812, "base": 4724504, - "core": 7653313 + "core": 7653313, + "linea": 957303 }, "BULK_RESERVES_ADDRESSES": { "ethereum": "0x4261b012f3d0d752ee9d738a4664df7117a148ab", @@ -65,7 +68,8 @@ "arbitrum": "0xbb90c1a84f51d57c2046741b15156810d20f3592", "zksync-era": "0xe7b54a13e456532675b4edc360a1c2818ef9f707", "base": "0xbb90c1a84f51d57c2046741b15156810d20f3592", - "core": "0xc79D535188b27C8f7f3011931f7417A584b237Fc" + "core": "0xc79D535188b27C8f7f3011931f7417A584b237Fc", + "linea": "0xBb90C1A84F51d57c2046741B15156810D20f3592" }, "BULK_RESERVES_DEPOLYED": { "ethereum": 11438212, @@ -82,7 +86,8 @@ "arbitrum": 86898048, "zksync-era": 6398730, "base": 4729900, - "core": 7653332 + "core": 7653332, + "linea": 957341 }, "MULTIBALANCES_ADDRESSES": { "ethereum": "0x09b6fd79f8c6bdaba4d94731f0fc90edd15742af", @@ -99,7 +104,8 @@ "arbitrum": "0x47012c5a5a20893f342d279ddc6fd0847f196898", "zksync-era": "0x7d1f4f2bc62bd7e7d1208ae4d8f3ad1b35467244", "base": "0x7d9965aee6b51c944b20beb8954fe5018781fb19", - "core": "0x73AAB8664A7213517ecF93443EB3a2A6137fC892" + "core": "0x73AAB8664A7213517ecF93443EB3a2A6137fC892", + "linea": "0x7d9965AeE6b51C944b20beb8954Fe5018781fb19" }, "MULTIBALANCES_DEPOLYED": { "ethereum": 11606340, @@ -116,7 +122,8 @@ "arbitrum": 86899235, "zksync-era": 6399985, "base": 4729971, - "core": 7653372 + "core": 7653372, + "linea": 957390 }, "BULK_METADATA_ADDRESSES": { "ethereum": "0x570B73444cC1077398f83777f34Bd672a50235cD", @@ -133,7 +140,8 @@ "arbitrum": "0x7d9965aee6b51c944b20beb8954fe5018781fb19", "zksync-era": "0x9ccc6685b99cc682e432441f704775550d8c5664", "base": "0x47012c5a5a20893f342d279ddc6fd0847f196898", - "core": "0x6679aA0f478293eA24614f30f4eC23e855747bEB" + "core": "0x6679aA0f478293eA24614f30f4eC23e855747bEB", + "linea": "0x47012c5a5A20893f342d279DdC6Fd0847f196898" }, "BULK_METADATA_DEPOLYED": { "ethereum": 12825801, @@ -150,7 +158,8 @@ "arbitrum": 86899489, "zksync-era": 6399344, "base": 4729939, - "core": 7653353 + "core": 7653353, + "linea": 957366 }, "MULTICALL_ADDRESSES": { "ethereum": "0x092C637b120Ad6C5DF6CFc2b2A9b284ed2E5b11F", @@ -167,7 +176,8 @@ "arbitrum": "0x5a3cf2c199bbb28cd0a2f380d1f96b8d73d74b78", "zksync-era": "0x9ce596f561ca117591bfb0f6f9a20f721290976b", "base": "0x5a3cf2c199bbb28cd0a2f380d1f96b8d73d74b78", - "core": "0x97fFB44f17018475260332dcc3f4D9C45D498514" + "core": "0x97fFB44f17018475260332dcc3f4D9C45D498514", + "linea": "0x5a3CF2C199bBB28cD0A2f380d1f96B8d73D74B78" }, "MULTICALL_DEPOLYED": { "ethereum": 12949381, @@ -184,6 +194,7 @@ "arbitrum": 86899646, "zksync-era": 6400154, "base": 4730173, - "core": 7653400 + "core": 7653400, + "linea": 957405 } } \ No newline at end of file diff --git a/src/factory/providers/base/horizon/index.ts b/src/factory/providers/base/horizon/index.ts new file mode 100644 index 00000000..de7b48c9 --- /dev/null +++ b/src/factory/providers/base/horizon/index.ts @@ -0,0 +1,25 @@ +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import uniswapV3 from '../../../../util/calculators/uniswapV3chain'; + +const V3_START_BLOCK = 2053334; +const V3_FACTORY_ADDRESS = '0x07AceD5690e09935b1c0e6E88B772d9440F64718'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < V3_START_BLOCK) { + return { balances: {} }; + } + + const balances = await uniswapV3.getTvl( + V3_FACTORY_ADDRESS, + V3_START_BLOCK, + block, + chain, + provider, + web3, + ); + + return { balances, poolBalances: {} }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/echodex/index.ts b/src/factory/providers/linea/echodex/index.ts new file mode 100644 index 00000000..57a1fe0b --- /dev/null +++ b/src/factory/providers/linea/echodex/index.ts @@ -0,0 +1,42 @@ +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import uniswapV2 from '../../../../util/calculators/uniswapV2'; +import uniswapV3 from '../../../../util/calculators/uniswapV3chain'; +import BigNumber from 'bignumber.js'; + +const START_BLOCK = 2344; +const V2_FACTORY_ADDRESS = '0x6D1063F2187442Cc9adbFAD2f55A96B846FCB399'; +const V3_START_BLOCK = 120029; +const V3_FACTORY_ADDRESS = '0x559Fa53Be355835a038aC303A750E8788668636B'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < START_BLOCK) { + return { balances: {} }; + } + + const { balances, poolBalances } = await uniswapV2.getTvl( + V2_FACTORY_ADDRESS, + block, + chain, + provider, + web3, + ); + + const v3Balances = await uniswapV3.getTvl( + V3_FACTORY_ADDRESS, + V3_START_BLOCK, + block, + chain, + provider, + web3, + ); + + for (const [key, value] of Object.entries(v3Balances)) { + balances[key] = new BigNumber(balances[key] || 0).plus(value).toFixed(); + } + formatter.convertBalancesToFixed(balances); + return { balances, poolBalances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/horizon/index.ts b/src/factory/providers/linea/horizon/index.ts new file mode 100644 index 00000000..6bc6ccad --- /dev/null +++ b/src/factory/providers/linea/horizon/index.ts @@ -0,0 +1,25 @@ +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import uniswapV3 from '../../../../util/calculators/uniswapV3chain'; + +const V3_START_BLOCK = 1150; +const V3_FACTORY_ADDRESS = '0x9Fe607e5dCd0Ea318dBB4D8a7B04fa553d6cB2c5'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < V3_START_BLOCK) { + return { balances: {} }; + } + + const balances = await uniswapV3.getTvl( + V3_FACTORY_ADDRESS, + V3_START_BLOCK, + block, + chain, + provider, + web3, + ); + + return { balances, poolBalances: {} }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/izumi/index.ts b/src/factory/providers/linea/izumi/index.ts new file mode 100644 index 00000000..7e3bc9c7 --- /dev/null +++ b/src/factory/providers/linea/izumi/index.ts @@ -0,0 +1,105 @@ +import util from '../../../../util/blockchainUtil'; +import basicUtil from '../../../../util/basicUtil'; +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import LIQUIDITY_MANAGER_ABI from './liquidityManagerAbi.json'; + +const START_BLOCK = 2094; +const LIQUIDITY_MANAGERS = [ + { address: '0x1CB60033F61e4fc171c963f0d2d3F63Ece24319c', startBlock: 2094 }, +]; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + const balances = {}; + if (block < START_BLOCK) { + return { balances }; + } + + for (const liquidityManager of LIQUIDITY_MANAGERS) { + if (block >= liquidityManager.startBlock) { + let pools = {}; + try { + pools = await basicUtil.readFromCache( + `${liquidityManager.address}_pools.json`, + chain, + provider, + ); + } catch {} + + while (true) { + let exit = false; + + const poolMetas = await util.executeMultiCallsOfTarget( + liquidityManager.address, + LIQUIDITY_MANAGER_ABI, + 'poolMetas', + Array.from({ length: 10 }, (v, index) => [ + index + 1 + Object.keys(pools).length, + ]), + block, + chain, + web3, + ); + const poolAddresses = await util.executeMultiCallsOfTarget( + liquidityManager.address, + LIQUIDITY_MANAGER_ABI, + 'pool', + poolMetas + .filter((poolMeta) => { + if ( + poolMeta.tokenX !== '0x0000000000000000000000000000000000000000' + ) { + return poolMeta; + } else { + exit = true; + } + }) + .map( + (poolMeta: { tokenX: string; tokenY: string; fee: string }) => [ + poolMeta.tokenX, + poolMeta.tokenY, + poolMeta.fee, + ], + ), + block, + chain, + web3, + ); + + poolAddresses.forEach((poolAddress, index) => { + pools[poolAddress] = [ + poolMetas[index].tokenX, + poolMetas[index].tokenY, + ]; + }); + + if (exit) break; + } + + await basicUtil.saveIntoCache( + `${liquidityManager.address}_pools.json`, + 'pools.json', + chain, + provider, + ); + + const tokenBalances = await util.getTokenBalancesOfHolders( + Object.keys(pools).flatMap((i) => [i, i]), + Object.keys(pools) + .map((tokens) => pools[tokens]) + .flat(1), + block, + chain, + web3, + ); + + formatter.sumMultiBalanceOf(balances, tokenBalances); + } + } + + formatter.convertBalancesToFixed(balances); + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/izumi/liquidityManagerAbi.json b/src/factory/providers/linea/izumi/liquidityManagerAbi.json new file mode 100644 index 00000000..9a51f91b --- /dev/null +++ b/src/factory/providers/linea/izumi/liquidityManagerAbi.json @@ -0,0 +1,1108 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "weth", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "nftId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidityDelta", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountX", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountY", + "type": "uint256" + } + ], + "name": "AddLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "nftId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidityDelta", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountX", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountY", + "type": "uint256" + } + ], + "name": "DecLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "WETH9", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "lid", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "xLim", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "yLim", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amountXMin", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amountYMin", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct LiquidityManager.AddLiquidityParam", + "name": "addLiquidityParam", + "type": "tuple" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityDelta", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amountX", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountY", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "lid", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "lid", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "amountXLim", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amountYLim", + "type": "uint128" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint256", + "name": "amountX", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountY", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenX", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenY", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "initialPoint", + "type": "int24" + } + ], + "name": "createPool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "lid", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "liquidDelta", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amountXMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountYMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "decLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountX", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountY", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "liquidities", + "outputs": [ + { + "internalType": "int24", + "name": "leftPt", + "type": "int24" + }, + { + "internalType": "int24", + "name": "rightPt", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "lastFeeScaleX_128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastFeeScaleY_128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "remainTokenX", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "remainTokenY", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "poolId", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidityNum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "miner", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenX", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenY", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "pl", + "type": "int24" + }, + { + "internalType": "int24", + "name": "pr", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "xLim", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "yLim", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amountXMin", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amountYMin", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct LiquidityManager.MintParam", + "name": "mintParam", + "type": "tuple" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "lid", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amountX", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountY", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mintDepositCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenX", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenY", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "pool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "poolIds", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "name": "poolMetas", + "outputs": [ + { + "internalType": "address", + "name": "tokenX", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenY", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "refundETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "newBaseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "unwrapWETH9", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/src/factory/providers/linea/layerbank/abi.json b/src/factory/providers/linea/layerbank/abi.json new file mode 100644 index 00000000..7c6e53b0 --- /dev/null +++ b/src/factory/providers/linea/layerbank/abi.json @@ -0,0 +1,45 @@ +[ + { + "inputs": [], + "name": "allMarkets", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCash", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "signature": "0x3b1d21a2", + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "underlying", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6f307dc3" + } +] \ No newline at end of file diff --git a/src/factory/providers/linea/layerbank/index.ts b/src/factory/providers/linea/layerbank/index.ts new file mode 100644 index 00000000..c08f499f --- /dev/null +++ b/src/factory/providers/linea/layerbank/index.ts @@ -0,0 +1,123 @@ +import BigNumber from 'bignumber.js'; +import Web3 from 'web3'; +import { IBalances } from '../../../../interfaces/ITvl'; +import formatter from '../../../../util/formatter'; +import basicUtil from '../../../../util/basicUtil'; +import util from '../../../../util/blockchainUtil'; +import abi from './abi.json'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; + +const START_BLOCK = 7379; +const UNITROLLER_ADDRESSES = ['0x009a0b7C38B542208936F1179151CD08E2943833']; +const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; + +async function getTvl( + unitrollerAddresses: string[], + block: number, + chain: string, + provider: string, + web3: Web3, +): Promise { + let qiTokens = {}; + try { + qiTokens = await basicUtil.readFromCache( + 'cache/pools.json', + chain, + provider, + ); + } catch {} + + const marketResults = await util.executeCallOfMultiTargets( + unitrollerAddresses, + abi, + 'allMarkets', + [], + block, + chain, + web3, + ); + + const allMarkets = []; + marketResults.forEach((result) => { + if (result) { + allMarkets.push(...result); + } + }); + + const newMarkets = allMarkets.filter((market) => !qiTokens[market]); + + if (newMarkets.length > 0) { + const underlyings = await util.executeCallOfMultiTargets( + newMarkets, + abi, + 'underlying', + [], + block, + chain, + web3, + ); + + underlyings.forEach((underlying, index) => { + qiTokens[newMarkets[index]] = ( + underlying || basicUtil.getWmainAddress(chain) + ).toLowerCase(); + }); + + await basicUtil.saveIntoCache( + qiTokens, + 'cache/pools.json', + chain, + provider, + ); + } + + const results = await util.executeCallOfMultiTargets( + allMarkets, + abi, + 'getCash', + [], + block, + chain, + web3, + ); + + const tokenBalances = []; + results.forEach((result, index) => { + if (result) { + tokenBalances.push({ + token: qiTokens[allMarkets[index]], + balance: BigNumber(result), + }); + } + }); + + const balances = {}; + formatter.sumMultiBalanceOf(balances, tokenBalances, chain, provider); + return balances; +} + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + + if (block < START_BLOCK) { + return { balances: {} }; + } + + const balances = await getTvl( + UNITROLLER_ADDRESSES, + block, + chain, + provider, + web3, + ); + + if (balances[ZERO_ADDRESS]) { + balances['eth'] = balances[ZERO_ADDRESS]; + delete balances[ZERO_ADDRESS]; + } + + formatter.convertBalancesToFixed(balances); + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/lynex/index.ts b/src/factory/providers/linea/lynex/index.ts new file mode 100644 index 00000000..62726080 --- /dev/null +++ b/src/factory/providers/linea/lynex/index.ts @@ -0,0 +1,26 @@ +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import uniswapV3 from '../../../../util/calculators/uniswapV3chain'; + +const V3_START_BLOCK = 143660; +const V3_FACTORY_ADDRESS = '0x622b2c98123D303ae067DB4925CD6282B3A08D0F'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < V3_START_BLOCK) { + return { balances: {} }; + } + + const balances = await uniswapV3.getTvl( + V3_FACTORY_ADDRESS, + V3_START_BLOCK, + block, + chain, + provider, + web3, + true, + ); + + return { balances, poolBalances: {} }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/mendi/index.ts b/src/factory/providers/linea/mendi/index.ts new file mode 100644 index 00000000..0f20544c --- /dev/null +++ b/src/factory/providers/linea/mendi/index.ts @@ -0,0 +1,26 @@ +import unitroller from '../../../../util/calculators/unitroller'; +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; + +const START_BLOCK = 215970; +const UNITROLLER_ADDRESSES = ['0x1b4d3b0421dDc1eB216D230Bc01527422Fb93103']; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + + if (block < START_BLOCK) { + return {}; + } + + const balances = await unitroller.getTvl( + UNITROLLER_ADDRESSES, + block, + chain, + provider, + web3, + ); + + formatter.convertBalancesToFixed(balances); + return { balances }; +} +export { tvl }; diff --git a/src/factory/providers/linea/metavault/index.ts b/src/factory/providers/linea/metavault/index.ts new file mode 100644 index 00000000..ca6f01e0 --- /dev/null +++ b/src/factory/providers/linea/metavault/index.ts @@ -0,0 +1,25 @@ +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import uniswapV3 from '../../../../util/calculators/uniswapV3chain'; + +const V3_START_BLOCK = 652486; +const V3_FACTORY_ADDRESS = '0x9367c561915f9D062aFE3b57B18e30dEC62b8488'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < V3_START_BLOCK) { + return { balances: {} }; + } + + const balances = await uniswapV3.getTvl( + V3_FACTORY_ADDRESS, + V3_START_BLOCK, + block, + chain, + provider, + web3, + ); + + return { balances, poolBalances: {} }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/pancakeswapv3/index.ts b/src/factory/providers/linea/pancakeswapv3/index.ts new file mode 100644 index 00000000..666f818e --- /dev/null +++ b/src/factory/providers/linea/pancakeswapv3/index.ts @@ -0,0 +1,25 @@ +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import uniswapV3 from '../../../../util/calculators/uniswapV3chain'; + +const V3_START_BLOCK = 1445; +const V3_FACTORY_ADDRESS = '0x0bfbcf9fa4f9c56b0f40a671ad40e0805a091865'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < V3_START_BLOCK) { + return { balances: {} }; + } + + const balances = await uniswapV3.getTvl( + V3_FACTORY_ADDRESS, + V3_START_BLOCK, + block, + chain, + provider, + web3, + ); + + return { balances, poolBalances: {} }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/satori/index.ts b/src/factory/providers/linea/satori/index.ts new file mode 100644 index 00000000..07f49e62 --- /dev/null +++ b/src/factory/providers/linea/satori/index.ts @@ -0,0 +1,32 @@ +import util from '../../../../util/blockchainUtil'; +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; + +const START_BLOCK = 156461; +const STAKING_ADDRESSES = [ + '0xfb371E70eEB32f4054F40514924e77213ca18425', + '0xF96116e124eB3F62Ddc6a9cfbdc58d7F8A37c50A', +]; +const USDC_ADDRESS = '0x176211869ca2b568f2a7d4ee941e073a821ee1ff'; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < START_BLOCK) { + return {}; + } + + const proxyBalance = await util.getTokenBalancesOfEachHolder( + STAKING_ADDRESSES, + [USDC_ADDRESS], + block, + chain, + web3, + ); + + const balances = {}; + formatter.sumMultiBalanceOf(balances, proxyBalance, chain, provider); + formatter.convertBalancesToFixed(balances); + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/stargate/abi.json b/src/factory/providers/linea/stargate/abi.json new file mode 100644 index 00000000..edfbdc55 --- /dev/null +++ b/src/factory/providers/linea/stargate/abi.json @@ -0,0 +1,79 @@ +[ + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "contract Factory", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allPoolsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPools", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "pools", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/src/factory/providers/linea/stargate/index.ts b/src/factory/providers/linea/stargate/index.ts new file mode 100644 index 00000000..5992462d --- /dev/null +++ b/src/factory/providers/linea/stargate/index.ts @@ -0,0 +1,105 @@ +import util from '../../../../util/blockchainUtil'; +import ABI from './abi.json'; +import CONSTANTS from '../../../../constants/contracts.json'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import basicUtil from '../../../../util/basicUtil'; +import formatter from '../../../../util/formatter'; + +const START_BLOCK = 48078; +const ROUTER = '0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590'; +const BASE_TOKENS = { + '0x224d8fd7ab6ad4c6eb4611ce56ef35dec2277f03': CONSTANTS.WMAIN_ADDRESS.linea, +}; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + if (block < START_BLOCK) { + return {}; + } + + let store = { + pools: [], + tokens: {}, + }; + try { + store = await basicUtil.readFromCache('cache/store.json', chain, provider); + } catch {} + + const factory = await util.executeCall( + ROUTER, + ABI, + 'factory', + [], + block, + chain, + web3, + ); + + const allPoolsLength = await util.executeCall( + factory, + ABI, + 'allPoolsLength', + [], + block, + chain, + web3, + ); + + if (store.pools.length < allPoolsLength) { + const newPools = await util.executeMultiCallsOfTarget( + factory, + ABI, + 'allPools', + [ + Array.from( + { length: allPoolsLength - store.pools.length }, + (_, i) => store.pools.length + i, + ), + ], + block, + chain, + web3, + ); + const poolTokens = await util.executeCallOfMultiTargets( + newPools, + ABI, + 'token', + [], + block, + chain, + web3, + ); + + for (const index in newPools) { + if (newPools[index] && poolTokens[index]) { + store.pools.push(newPools[index]); + store.tokens[newPools[index]] = poolTokens[index]; + } else { + break; + } + } + await basicUtil.saveIntoCache(store, 'cache/store.json', chain, provider); + } + + const tokenBalances = await util.getTokenBalancesOfHolders( + Object.keys(store.tokens), + Object.values(store.tokens), + block, + chain, + web3, + ); + tokenBalances.forEach((tokenBalance) => { + if (tokenBalance && BASE_TOKENS[tokenBalance.token]) { + tokenBalance.token = BASE_TOKENS[tokenBalance.token]; + } + }); + + const balances = {}; + + formatter.sumMultiBalanceOf(balances, tokenBalances); + formatter.convertBalancesToFixed(balances); + + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/syncswap/index.ts b/src/factory/providers/linea/syncswap/index.ts new file mode 100644 index 00000000..ff2ba7e2 --- /dev/null +++ b/src/factory/providers/linea/syncswap/index.ts @@ -0,0 +1,86 @@ +import BigNumber from 'bignumber.js'; +import util from '../../../../util/blockchainUtil'; +import basicUtil from '../../../../util/basicUtil'; +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import CLASSIC_POOL_ABI from '../../../../constants/abi/syncswapClassicPoolAbi.json'; + +const START_BLOCK = 716; +const FACTORIES = [ + '0xE4CF807E351b56720B17A59094179e7Ed9dD3727', + '0x37BAc764494c8db4e54BDE72f6965beA9fa0AC2d', +]; +const TOPIC = + '0x9c5d829b9b23efc461f9aeef91979ec04bb903feb3bee4f26d22114abfc7335b'; +const BLOCK_LIMIT = 10000; + +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + const balances = {}; + if (block < START_BLOCK) { + return { balances }; + } + + let cache: { + start: number; + pools: object; + } = { start: START_BLOCK, pools: {} }; + try { + cache = await basicUtil.readFromCache('cache.json', chain, provider); + } catch {} + + for (const factory of FACTORIES) { + for ( + let i = Math.max(cache.start, START_BLOCK); + i < block; + i += BLOCK_LIMIT + ) { + const logs = await util.getLogs( + i, + Math.min(i + BLOCK_LIMIT, block), + TOPIC, + factory, + web3, + ); + + logs.output.forEach((log) => { + const token0 = `0x${log.topics[1].substring(26, 66)}`; + const token1 = `0x${log.topics[2].substring(26, 66)}`; + const pool = `0x${log.data.substring(26, 66)}`; + + cache.pools[pool] = { token0, token1 }; + }); + } + } + + cache.start = block; + await basicUtil.saveIntoCache(cache, 'cache.json', chain, provider); + + const reserves = await util.executeCallOfMultiTargets( + Object.keys(cache.pools), + CLASSIC_POOL_ABI, + 'getReserves', + [], + block, + chain, + web3, + ); + + Object.entries(cache.pools).forEach(([, pool], index) => { + if (BigNumber(reserves[index]._reserve0).isGreaterThan(0)) { + balances[pool.token0] = BigNumber(balances[pool.token0] || 0).plus( + reserves[index]._reserve0, + ); + } + if (BigNumber(reserves[index]._reserve1).isGreaterThan(0)) { + balances[pool.token1] = BigNumber(balances[pool.token1] || 0).plus( + reserves[index]._reserve1, + ); + } + }); + + formatter.convertBalancesToFixed(balances); + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/tomo/index.ts b/src/factory/providers/linea/tomo/index.ts new file mode 100644 index 00000000..3023f2c0 --- /dev/null +++ b/src/factory/providers/linea/tomo/index.ts @@ -0,0 +1,22 @@ +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; + +const START_BLOCK = 507512; +const TOMO = '0x9e813d7661d7b56cbcd3f73e958039b208925ef8'; +async function tvl(params: ITvlParams): Promise> { + const { block, web3 } = params; + if (block < START_BLOCK) { + return { balances: {} }; + } + const balances = { + ['0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f']: await web3.eth.getBalance( + TOMO, + block, + ), + }; + + formatter.convertBalancesToFixed(balances); + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/velocore/factoryAbi.json b/src/factory/providers/linea/velocore/factoryAbi.json new file mode 100644 index 00000000..834e6a19 --- /dev/null +++ b/src/factory/providers/linea/velocore/factoryAbi.json @@ -0,0 +1,362 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "begin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxLength", + "type": "uint256" + } + ], + "name": "canonicalPools", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "gauge", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "string", + "name": "poolType", + "type": "string" + }, + { + "internalType": "Token[]", + "name": "lpTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "mintedLPTokens", + "type": "uint256[]" + }, + { + "internalType": "Token[]", + "name": "listedTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "reserves", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "poolParams", + "type": "bytes" + } + ], + "internalType": "struct PoolData", + "name": "poolData", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "killed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "totalVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userClaimable", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "emissionRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userEmissionRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakedValueInHubToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userStakedValueInHubToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "averageInterestRatePerSecond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userInterestRatePerSecond", + "type": "uint256" + }, + { + "internalType": "Token[]", + "name": "stakeableTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "stakedAmounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userStakedAmounts", + "type": "uint256[]" + }, + { + "internalType": "Token[]", + "name": "underlyingTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "stakedUnderlying", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userUnderlying", + "type": "uint256[]" + }, + { + "components": [ + { + "internalType": "Token[]", + "name": "tokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "rates", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userClaimable", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userRates", + "type": "uint256[]" + } + ], + "internalType": "struct BribeData[]", + "name": "bribes", + "type": "tuple[]" + } + ], + "internalType": "struct GaugeData[]", + "name": "gaugeDataArray", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "wombatGauges", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "gauge", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "string", + "name": "poolType", + "type": "string" + }, + { + "internalType": "Token[]", + "name": "lpTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "mintedLPTokens", + "type": "uint256[]" + }, + { + "internalType": "Token[]", + "name": "listedTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "reserves", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "poolParams", + "type": "bytes" + } + ], + "internalType": "struct PoolData", + "name": "poolData", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "killed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "totalVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userVotes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userClaimable", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "emissionRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userEmissionRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakedValueInHubToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userStakedValueInHubToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "averageInterestRatePerSecond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userInterestRatePerSecond", + "type": "uint256" + }, + { + "internalType": "Token[]", + "name": "stakeableTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "stakedAmounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userStakedAmounts", + "type": "uint256[]" + }, + { + "internalType": "Token[]", + "name": "underlyingTokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "stakedUnderlying", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userUnderlying", + "type": "uint256[]" + }, + { + "components": [ + { + "internalType": "Token[]", + "name": "tokens", + "type": "bytes32[]" + }, + { + "internalType": "uint256[]", + "name": "rates", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userClaimable", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "userRates", + "type": "uint256[]" + } + ], + "internalType": "struct BribeData[]", + "name": "bribes", + "type": "tuple[]" + } + ], + "internalType": "struct GaugeData[]", + "name": "gaugeDataArray", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/src/factory/providers/linea/velocore/index.ts b/src/factory/providers/linea/velocore/index.ts new file mode 100644 index 00000000..791a9cbe --- /dev/null +++ b/src/factory/providers/linea/velocore/index.ts @@ -0,0 +1,58 @@ +import formatter from '../../../../util/formatter'; +import util from '../../../../util/blockchainUtil'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import factoryAbi from './factoryAbi.json'; + +const START_BLOCK = 101965; +const FACTORY_ADDRESS = '0xaA18cDb16a4DD88a59f4c2f45b5c91d009549e06'; +const VAULT_ADDRESS = '0x1d0188c4B276A09366D05d6Be06aF61a73bC7535'; +const E_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; + +async function tvl(params: ITvlParams): Promise> { + const balances = {}; + const { block, chain, provider, web3 } = params; + if (block < START_BLOCK) { + return { balances }; + } + + const contract = new web3.eth.Contract(factoryAbi, FACTORY_ADDRESS); + + const canonicalPools = await contract.methods['canonicalPools']( + FACTORY_ADDRESS, + 0, + 1000, + ).call(null, block); + + const wombatGauges = await contract.methods['wombatGauges']( + FACTORY_ADDRESS, + ).call(null, block); + + const tokens: string[] = [ + ...new Set( + canonicalPools + .concat(wombatGauges) + .map((g) => g.poolData.listedTokens) + .flat() + .map((i: string) => '0x' + i.slice(2 + 24)) + .filter((address) => address !== E_ADDRESS), + ), + ]; + + const tokenBalances = await util.getTokenBalances( + VAULT_ADDRESS, + tokens, + block, + chain, + web3, + ); + + const balance = await web3.eth.getBalance(VAULT_ADDRESS, block); + balances['eth'] = balance; + + formatter.sumMultiBalanceOf(balances, tokenBalances, chain, provider); + formatter.convertBalancesToFixed(balances); + + return { balances }; +} + +export { tvl }; diff --git a/src/factory/providers/linea/xfai/index.ts b/src/factory/providers/linea/xfai/index.ts new file mode 100644 index 00000000..7be4ba03 --- /dev/null +++ b/src/factory/providers/linea/xfai/index.ts @@ -0,0 +1,77 @@ +import util from '../../../../util/blockchainUtil'; +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import FACTORY_ABI from '../../arbitrum/stargate/abi/factoryAbi.json'; + +const START_BLOCK = 222864; +const FACTORY_ADDRESS = '0xa5136eAd459F0E61C99Cec70fe8F5C24cF3ecA26'; +const WETH = '0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f'; +async function tvl(params: ITvlParams): Promise> { + const { block, chain, web3 } = params; + const balances = {}; + if (block < START_BLOCK) { + return { balances }; + } + + const allPoolsLength = await util.executeCall( + FACTORY_ADDRESS, + FACTORY_ABI, + 'allPoolsLength', + [], + block, + chain, + web3, + ); + + const contractParams = []; + for (let i = 0; i < allPoolsLength; i++) { + contractParams.push([i]); + } + + const pools = await util.executeMultiCallsOfTarget( + FACTORY_ADDRESS, + FACTORY_ABI, + 'allPools', + contractParams, + block, + chain, + web3, + ); + + const tokens = await util.executeCallOfMultiTargets( + pools, + [ + { + inputs: [], + name: 'poolToken', + outputs: [ + { + type: 'address', + name: 'token', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + ], + 'poolToken', + [], + block, + chain, + web3, + ); + + const balanceResults = await util.getTokenBalancesOfEachHolder( + pools, + [WETH, ...tokens], + block, + chain, + web3, + ); + + formatter.sumMultiBalanceOf(balances, balanceResults); + formatter.convertBalancesToFixed(balances); + return { balances }; +} + +export { tvl }; diff --git a/src/util/basicUtil.ts b/src/util/basicUtil.ts index 751f7d99..cc21660a 100644 --- a/src/util/basicUtil.ts +++ b/src/util/basicUtil.ts @@ -16,7 +16,7 @@ function basicUtil() { } function getDelay(chain) { - return data.CHAINS[chain].delay || DEFAULT_DELAY; + return data.CHAINS[chain]?.delay || DEFAULT_DELAY; } async function saveIntoCache(data, fileName, chain, provider) { diff --git a/src/util/calculators/uniswapV3chain.ts b/src/util/calculators/uniswapV3chain.ts new file mode 100644 index 00000000..198c5171 --- /dev/null +++ b/src/util/calculators/uniswapV3chain.ts @@ -0,0 +1,183 @@ +import formatter from '../../util/formatter'; +import basicUtil from '../../util/basicUtil'; +import util from '../../util/blockchainUtil'; +import { log } from '../../util/logger/logger'; +import PAIR_ABI from '../../constants/abi/uni.json'; +import { IBalances } from '../../interfaces/ITvl'; +import Web3 from 'web3'; + +const UNISWAP_TOPIC = + '0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118'; +const ALGEBRA_TOPIC = + '0x91ccaa7a278130b65168c3a0c8d3bcae84cf5e43704342bd3ec0b59e59c036db'; + +/** + * Gets TVL of Uniswap V2 (or it's clone) using factory address + * + * @param factoryAddress - The address of factory + * @param startBlock - The block number of factory deploying transaction + * @param block - The block number for which data is requested + * @param chain - EVM chain name (providers parent folder name) + * @param provider - the provider folder name + * @param web3 - The Web3 object + * @param algebra - true if algebra methods are used + * @returns The object containing object with token addresses and their locked values and object with pool balances + * + */ +async function getTvl( + factoryAddress: string, + startBlock: number, + block: number, + chain: string, + provider: string, + web3: Web3, + isAlgebra = false, +): Promise { + const balances = {}; + + let topic: string; + if (isAlgebra) { + topic = ALGEBRA_TOPIC; + } else { + topic = UNISWAP_TOPIC; + } + + let v3Pairs = { block: startBlock, pairs: [], token01: [] }; + try { + v3Pairs = await basicUtil.readFromCache( + 'cache/v3Pairs.json', + chain, + provider, + ); + } catch {} + + let logs = []; + console.log('[v3] start getting tvl'); + console.log('[v3] get logs1'); + + let offset = 10000; + for (let i = Math.max(v3Pairs.block, startBlock); ; ) { + console.log(`Trying from ${i} with offset ${offset}`); + let eventLog = []; + try { + eventLog = ( + await util.getLogs( + i, + Math.min(block, i + offset), + topic, + factoryAddress, + web3, + ) + ).output; + console.log(`Trying from ${i} with offset ${offset}`); + } catch (e) { + log.error({ + message: e?.message || '', + stack: e?.stack || '', + detail: `Error: tvl of ethereum/uniswapv3`, + endpoint: 'tvl', + }); + if (offset > 1000) { + offset -= 2000; + } else if (offset > 100) { + offset -= 200; + } else if (offset > 10) { + offset -= 20; + } else { + break; + } + continue; + } + logs = logs.concat(eventLog); + + i += offset; + if (block < i) { + break; + } + } + + const pairs = v3Pairs.token01; + let pairAddresses = v3Pairs.pairs; + const start = 128 - 40 + 2; + const end = 128 + 2; + + const pairExist = {}; + pairAddresses.forEach((address) => (pairExist[address] = true)); + + pairAddresses = pairAddresses.concat( + logs + .map((log) => { + let pairAddress: string; + if (isAlgebra) { + pairAddress = `0x${log.data.slice(-40)}`; + } else { + pairAddress = `0x${log.data.slice(start, end)}`; + } + pairAddress = pairAddress.toLowerCase(); + + pairs[pairAddress] = { + token0Address: `0x${log.topics[1].slice(26)}`, + token1Address: `0x${log.topics[2].slice(26)}`, + }; + + return pairAddress; + }) + .filter((address) => !pairExist[address]), + ); + + await basicUtil.saveIntoCache( + { + block, + pairs: pairAddresses, + token01: pairs, + }, + 'cache/v3Pairs.json', + chain, + provider, + ); + + const tokens0 = await util.executeCallOfMultiTargets( + pairAddresses, + PAIR_ABI, + 'token0', + [], + block, + chain, + web3, + ); + const tokens1 = await util.executeCallOfMultiTargets( + pairAddresses, + PAIR_ABI, + 'token1', + [], + block, + chain, + web3, + ); + + const token0Balances = await util.getTokenBalancesOfHolders( + pairAddresses, + tokens0.filter(Boolean), + block, + chain, + web3, + ); + + const token1Balances = await util.getTokenBalancesOfHolders( + pairAddresses, + tokens1.filter(Boolean), + block, + chain, + web3, + ); + + formatter.sumMultiBalanceOf(balances, token1Balances); + formatter.sumMultiBalanceOf(balances, token0Balances); + formatter.convertBalancesToFixed(balances); + + return balances; +} + +export default { + getTvl, +};