From 4d686ec985b9a14d3fd8700ce7addf0088d0815c Mon Sep 17 00:00:00 2001 From: Mantas S Date: Wed, 19 Jun 2024 16:15:08 +0300 Subject: [PATCH] spookyswap --- .env.dev | 1 + src/app.config.ts | 1 + src/constants/contracts.json | 3 +- .../providers/bttc/spookyswap/index.ts | 316 ++++++++++++++++++ 4 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 src/factory/providers/bttc/spookyswap/index.ts diff --git a/.env.dev b/.env.dev index bae77065..731691ec 100644 --- a/.env.dev +++ b/.env.dev @@ -21,6 +21,7 @@ ELYSIUM_NODE_URL=https://rpc.elysiumchain.tech CORE_NODE_URL=https://core.public.infstones.com BASE_NODE_URL=https://base.llamarpc.com LINEA_NODE_URL=https://rpc.linea.build +BTTC_NODE_URL=https://rpc.bittorrentchain.io ##======================== LOGSTASH ======================== LOGSTASH_PORT= LOGSTASH_HOST= diff --git a/src/app.config.ts b/src/app.config.ts index f2f1b01d..95ddd27f 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -66,5 +66,6 @@ nodeUrls['CORE_NODE_URL'] = process.env['CORE_NODE_URL']; nodeUrls['LINEA_NODE_URL'] = process.env['LINEA_NODE_URL']; nodeUrls['WAX_NODE_URL'] = process.env['WAX_NODE_URL']; nodeUrls['APTOS_NODE_URL'] = process.env['APTOS_NODE_URL']; +nodeUrls['BTTC_NODE_URL'] = process.env['BTTC_NODE_URL']; export { config, nodeUrls }; diff --git a/src/constants/contracts.json b/src/constants/contracts.json index 5d32f220..7a08f16d 100644 --- a/src/constants/contracts.json +++ b/src/constants/contracts.json @@ -15,7 +15,8 @@ "zksync-era": "0x5aea5775959fbc2557cc8789bc1bf90a239d9a91", "base": "0x4200000000000000000000000000000000000006", "core": "0x40375C92d9FAf44d2f9db9Bd9ba41a3317a2404f", - "linea": "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f" + "linea": "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f", + "bttc": "0x0000000000000000000000000000000000001010" }, "BULK_BALANCE_ADDRESSES": { "ethereum": "0xb173393e08496209ad1cd9d57c769de76bdcea5a", diff --git a/src/factory/providers/bttc/spookyswap/index.ts b/src/factory/providers/bttc/spookyswap/index.ts new file mode 100644 index 00000000..d64af61d --- /dev/null +++ b/src/factory/providers/bttc/spookyswap/index.ts @@ -0,0 +1,316 @@ +import formatter from '../../../../util/formatter'; +import { ITvlParams, ITvlReturn } from '../../../../interfaces/ITvl'; +import BigNumber from 'bignumber.js'; +import { PromisePool } from '@supercharge/promise-pool'; +import Web3 from 'web3'; +import { AbiItem } from 'web3-utils'; +import FACTORY_ABI from '../../../../constants/abi/factory.json'; +import PAIR_ABI from '../../../../constants/abi/uni.json'; +import RESERVES_ABI from '../../../../constants/abi/uniReserves.json'; +import basicUtil from '../../../../util/basicUtil'; +import util from '../../../../util/blockchainUtil'; +import { log } from '../../../../util/logger/logger'; + +const START_BLOCK = 23534368; +const FACTORY_ADDRESS = '0xee4bc42157cf65291ba2fe839ae127e3cc76f741'; + +async function getTvl( + factoryAddress: string, + block: number, + chain: string, + provider: string, + web3: Web3, + usePoolMethods = false, +): Promise { + const balances = {}; + const poolBalances = {}; + + let _pairs = []; + let _token01 = {}; + try { + _pairs = await basicUtil.readFromCache( + `cache/${factoryAddress.toLowerCase()}_pairs.json`, + chain, + provider, + ); + } catch { + try { + _pairs = await basicUtil.readFromCache( + 'cache/pairs.json', + chain, + provider, + ); + } catch {} + } + try { + _token01 = await basicUtil.readFromCache( + `cache/${factoryAddress.toLowerCase()}_token01.json`, + chain, + provider, + ); + } catch { + try { + _token01 = await basicUtil.readFromCache( + 'cache/token01.json', + chain, + provider, + ); + } catch {} + } + const contract = new web3.eth.Contract( + FACTORY_ABI as AbiItem[], + factoryAddress, + ); + + let len = 0; + + try { + if (!usePoolMethods) { + len = await contract.methods.allPairsLength().call(null, block); + } else { + len = await contract.methods.allPoolsLength().call(null, block); + } + len = Number(len); + } catch (e) { + console.log(e); + return; + } + + log.info({ + message: `Pair counts: ${len}`, + endpoint: 'getTvl', + }); + + let poolInfos = _pairs; + const pairLength = _pairs.length; + + for (let start = pairLength; start < len; start += 1) { + let pInfos = []; + const end = Math.min(start + 1, len); + log.info({ + message: `Getting Pairs from ${start} to ${end}`, + endpoint: 'getTvl', + }); + for (let i = start; i < end; i++) { + if (!usePoolMethods) { + pInfos.push(contract.methods.allPairs(i).call()); + } else { + pInfos.push(contract.methods.allPools(i).call()); + } + } + try { + pInfos = await Promise.all(pInfos); + pInfos.forEach((info) => poolInfos.push(info)); + } catch (e) { + log.error({ + message: e?.message || '', + stack: e?.stack || '', + detail: `Error: getTvl of ${chain}/${provider}`, + endpoint: 'getTvl', + }); + break; + } + } + + if (pairLength < len) { + await basicUtil.saveIntoCache( + poolInfos, + `cache/${factoryAddress.toLowerCase()}_pairs.json`, + chain, + provider, + ); + } + + poolInfos = poolInfos.slice(0, len); + + const token01Infos = _token01; + const newPools = poolInfos.filter((info) => !_token01[info]); + const newPoolLength = newPools.length; + for (let start = 0; start < newPoolLength; start += 1) { + const end = Math.min(newPoolLength, start + 1); + log.info({ + message: `Getting Token01 from ${start} to ${end}`, + endpoint: 'getTvl', + }); + const subPools = newPools.slice(start, end); + try { + const tokens0 = await util.executeCallOfMultiTargets( + subPools, + PAIR_ABI, + 'token0', + [], + block, + chain, + web3, + ); + const tokens1 = await util.executeCallOfMultiTargets( + subPools, + PAIR_ABI, + 'token1', + [], + block, + chain, + web3, + ); + subPools.forEach((subPool, index) => { + token01Infos[subPool] = {}; + token01Infos[subPool].token0 = tokens0[index]; + token01Infos[subPool].token1 = tokens1[index]; + }); + } catch (e) { + log.error({ + message: e?.message || '', + stack: e?.stack || '', + detail: `Getting Token01 got issue at ${start}`, + endpoint: 'getTvl', + }); + } + } + + await basicUtil.saveIntoCache( + token01Infos, + `cache/${factoryAddress.toLowerCase()}_token01.json`, + chain, + provider, + ); + console.time('Getting PairInfo'); + for (let first = 0; first < poolInfos.length; first += 1) { + const last = Math.min(poolInfos.length, first + 1); + log.info({ + message: `Getting PairInfo from ${first} to ${last}`, + endpoint: 'getTvl', + }); + const getMultiPoolsReserves = []; + for (let start = first; start < last; start += 50) { + const end = Math.min(last, start + 50); + getMultiPoolsReserves.push( + getPoolsReserves( + poolInfos.slice(start, end), + block, + chain, + web3, + provider, + ), + ); + } + + const { results: poolReserves } = await PromisePool.withConcurrency(1) + .for(getMultiPoolsReserves) + .process(async (data) => { + return data; + }); + + poolReserves.forEach((reserves) => { + reserves.forEach((reserve) => { + poolBalances[reserve.pool_address.toLowerCase()] = { + tokens: [ + token01Infos[reserve.pool_address].token0?.toLowerCase(), + token01Infos[reserve.pool_address].token1?.toLowerCase(), + ], + balances: [reserve.reserve0, reserve.reserve1], + }; + + if ( + token01Infos[reserve.pool_address] && + token01Infos[reserve.pool_address].token0 && + reserve.reserve0 + ) { + const token = + token01Infos[reserve.pool_address]?.token0?.toLowerCase(); + if (!balances[token]) { + balances[token] = BigNumber(0); + } + balances[token] = BigNumber(balances[token]).plus( + BigNumber(reserve.reserve0.toString()), + ); + } + if ( + token01Infos[reserve.pool_address] && + token01Infos[reserve.pool_address].token1 && + reserve.reserve1 + ) { + const token = + token01Infos[reserve.pool_address]?.token1?.toLowerCase(); + if (!balances[token]) { + balances[token] = BigNumber(0); + } + balances[token] = BigNumber(balances[token]).plus( + BigNumber(reserve.reserve1.toString()), + ); + } + }); + }); + } + console.timeEnd('Getting PairInfo'); + return { balances, poolBalances }; +} + +async function getPoolsReserves(pInfos, block, chain, web3, provider) { + let poolReserves = []; + try { + poolReserves = await Promise.all( + pInfos.map((pool) => getReserves(pool, block, web3, chain, provider)), + ); + + return poolReserves; + } catch (e) { + log.error({ + message: e?.message || '', + stack: e?.stack || '', + detail: `Error: out getPoolsReserves chain: ${chain} provider: ${provider}`, + endpoint: 'getPoolsReserves', + }); + } + return poolReserves; +} + +async function getReserves(address, block, web3, chain, provider) { + try { + const contract = new web3.eth.Contract(PAIR_ABI, address); + const reserves = await contract.methods.getReserves().call(null, block); + return { + pool_address: address, + reserve0: BigNumber(reserves._reserve0.toString()), + reserve1: BigNumber(reserves._reserve1.toString()), + }; + } catch (e) { + try { + const contract = new web3.eth.Contract(RESERVES_ABI, address); + const reserves = await contract.methods.getReserves().call(null, block); + return { + pool_address: address, + reserve0: BigNumber(reserves._reserve0.toString()), + reserve1: BigNumber(reserves._reserve1.toString()), + }; + } catch (e) { + log.error({ + message: e?.message || '', + stack: e?.stack || '', + detail: `Error: uniswapV2.getReserves chain: ${chain} provider: ${provider}`, + endpoint: 'getReserves', + }); + } + } + return {}; +} +async function tvl(params: ITvlParams): Promise> { + const { block, chain, provider, web3 } = params; + + if (block < START_BLOCK) { + return {}; + } + + const { balances, poolBalances } = await getTvl( + FACTORY_ADDRESS, + block, + chain, + provider, + web3, + ); + + formatter.convertBalancesToFixed(balances); + + return { balances, poolBalances }; +} + +export { tvl };