diff --git a/projects/mole/index.js b/projects/mole/index.js index 2e7e443aa5c9..3fe52bf72bca 100644 --- a/projects/mole/index.js +++ b/projects/mole/index.js @@ -15,8 +15,10 @@ async function aptosTvl() { return {...lyfTvl}; } -async function suiTvl(api) { - return calLyfTvlSui(api) +async function suiTvl() { + const { api } = arguments[3] + + const lyfTvl = await calLyfTvlSui(api) } // run command: node test.js projects/mole/index.js diff --git a/projects/mole/lyf.js b/projects/mole/lyf.js index 966b803e48fa..a6609af7202e 100644 --- a/projects/mole/lyf.js +++ b/projects/mole/lyf.js @@ -1,5 +1,6 @@ const sdk = require("@defillama/sdk"); const abi = require("./abi.json"); +const BN = require("bn.js"); const BigNumber = require("bignumber.js"); const { coreTokens } = require("../helper/chain/aptos"); const { getResources } = require("../helper/chain/aptos"); @@ -7,7 +8,8 @@ const { getConfig } = require('../helper/cache') const { unwrapUniswapLPs } = require("../helper/unwrapLPs"); const sui = require('../helper/chain/sui') const { transformBalances } = require("../helper/portedTokens"); -const { i32BitsToNumber } = require("./utils") +const { getObject } = require("../helper/chain/sui"); +const { i32BitsToNumber, getCoinAmountFromLiquidity, tickIndexToSqrtPriceX64 } = require("./utils") async function getProcolAddresses(chain) { // if (chain === 'avax') { @@ -17,16 +19,17 @@ async function getProcolAddresses(chain) { // ) // ); // } - - if (chain === 'aptos') { + + if(chain === 'aptos') { return ( - await getConfig('mole/' + chain, + await getConfig('mole/'+chain, "https://raw.githubusercontent.com/Mole-Fi/mole-protocol/main/.aptos_mainnet.json" ) ); - } else if (chain === 'sui') { + }else if(chain === 'sui') { return ( - await getConfig('mole/' + chain, + // modify the hosts for raw.githubusercontent.com ip if it cannot be retrieved. + await getConfig('mole/'+chain, "https://raw.githubusercontent.com/Mole-Fi/mole-protocol/main/.sui_mainnet.json" ) ); @@ -37,7 +40,7 @@ async function getProcolAddresses(chain) { async function calLyfTvl(chain, block) { /// @dev Initialized variables const balances = {}; - const transform = addr => 'avax:' + addr + const transform = addr => 'avax:'+addr /// @dev Getting all addresses from Github const addresses = await getProcolAddresses(chain); @@ -125,18 +128,18 @@ async function calLyfTvlAptos() { /// @dev unwrap LP to get underlaying token balances for workers that are working with LPs await unwrapPancakeSwapLps({ - balances, - lps, - account: '0xc7efb4076dbe143cbcd98cfaaa929ecfc8f299203dfff63b95ccb6bfe19850fa', - poolStr: 'swap::TokenPairReserve', - token0Reserve: i => i.data.reserve_x, - token1Reserve: i => i.data.reserve_y + balances, + lps, + account: '0xc7efb4076dbe143cbcd98cfaaa929ecfc8f299203dfff63b95ccb6bfe19850fa', + poolStr: 'swap::TokenPairReserve', + token0Reserve: i => i.data.reserve_x, + token1Reserve: i => i.data.reserve_y }) /// @dev getting all unused liquidity on each vault resources.filter(i => i.type.includes("vault::VaultInfo")) .map(i => { - const token = i.type.split('<')[1].replace('>', ''); + const token = i.type.split('<')[1].replace('>',''); sdk.util.sumSingleBalance(balances, token, new BigNumber(i.data.coin.value).minus(i.data.reserve_pool).toFixed(0)) }) @@ -149,7 +152,7 @@ function sumPancakeWorkerStakingLps(resources, lps, workers) { i.type.includes("pancake_worker::WorkerInfo") || i.type.includes("delta_neutral_pancake_asset_worker::WorkerInfo") || i.type.includes("delta_neutral_pancake_stable_worker::WorkerInfo") - ) && workers[i.type.split('::', 1)[0]] + ) && workers[i.type.split('::',1)[0]] ) workerInfos.forEach(i => { @@ -158,7 +161,7 @@ function sumPancakeWorkerStakingLps(resources, lps, workers) { const lp = lpWithSuffix.substr(0, lpWithSuffix.length - 1); const amount = new BigNumber(i.data.total_balance ?? i.data.total_lp_balance) - if (lps[lp] === undefined) { + if(lps[lp] === undefined) { lps[lp] = { amount: amount } } else { lps[lp].amount = lps[lp].amount.plus(amount); @@ -174,13 +177,13 @@ async function unwrapPancakeSwapLps({ token0Reserve = i => i.data.coin_x_reserve.value, token1Reserve = i => i.data.coin_y_reserve.value, getTokens = i => i.type.split('<')[1].replace('>', '').split(', ') -}) { +}){ const coinInfos = {} const lpReserves = {} for (const lpType in lps) { - if (lps.hasOwnProperty(lpType)) { + if(lps.hasOwnProperty(lpType)){ coinInfos[`0x1::coin::CoinInfo<${lpType}>`] = lpType; - const tokens = getTokens({ type: lpType }) + const tokens = getTokens({type: lpType}) lpReserves[`${account}::${poolStr}<${tokens[0]}, ${tokens[1]}>`] = lpType; } } @@ -188,7 +191,7 @@ async function unwrapPancakeSwapLps({ let pools = await getResources(account); let lpInfos = pools; pools = pools.filter((i) => { - if (!i.type.includes(poolStr)) { + if(!i.type.includes(poolStr)){ return false } @@ -197,7 +200,7 @@ async function unwrapPancakeSwapLps({ }); lpInfos.forEach(i => { const lpType = coinInfos[i.type]; - if (lpType) { + if(lpType){ lps[lpType].totalSupply = new BigNumber(i.data.supply.vec[0].integer.vec[0].value) } }); @@ -238,71 +241,57 @@ async function calLyfTvlSui(api) { const workerInfos = await sui.getObjects(workerInfoIds) let poolIds = [] - workerInfos.forEach(workerInfo => { - let poolId = workerInfo.fields.position_nft.fields.pool - // poolId = poolId.replace('0x0', '0x') - if (!poolIds.includes(poolId)) { - poolIds.push(poolId) + workerInfos.forEach(workerInfo => + { + let poolId = workerInfo.fields.position_nft.fields.pool + // poolId = poolId.replace('0x0', '0x') + if (!poolIds.includes(poolId)) { + poolIds.push(poolId) + } } - } ) - const poolInfos = await sui.getObjects(poolIds) + const poolInfos = await sui.getObjects(poolIds) let poolMap = new Map() - poolInfos.forEach(poolInfo => { - // const poolId = poolInfo.fields.id.id.replace('0x0', '0x') - poolMap.set(poolInfo.fields.id.id, poolInfo) - } + poolInfos.forEach(poolInfo => + { + // const poolId = poolInfo.fields.id.id.replace('0x0', '0x') + poolMap.set(poolInfo.fields.id.id, poolInfo) + } ) - workerInfos.forEach(workerInfo => { - let poolId = workerInfo.fields.position_nft.fields.pool - - let coinAamount = 0 - let coinBamount = 0 - computeCLMMPositionBalances() - - const [coinA, coinB] = poolMap.get(poolId).type.replace('>', '').split('<')[1].split(', ') - api.add(coinA, coinAamount) - api.add(coinB, coinBamount) - - // code copied from uni v3 NFT resolver - function computeCLMMPositionBalances() { - const tickToPrice = (tick) => 1.0001 ** tick - + workerInfos.forEach(workerInfo => + { const liquidity = workerInfo.fields.position_nft.fields.liquidity const tickLowerIndex = i32BitsToNumber(workerInfo.fields.position_nft.fields.tick_lower_index.fields.bits) const tickUpperIndex = i32BitsToNumber(workerInfo.fields.position_nft.fields.tick_upper_index.fields.bits) - const bottomTick = tickLowerIndex - const topTick = tickUpperIndex - const tick = i32BitsToNumber(poolMap.get(poolId).fields.current_tick_index.fields.bits) - const sa = tickToPrice(bottomTick / 2) - const sb = tickToPrice(topTick / 2) - - if (tick < bottomTick) { - coinAamount = liquidity * (sb - sa) / (sa * sb) - } else if (tick < topTick) { - const price = tickToPrice(tick) - const sp = price ** 0.5 - - coinAamount = liquidity * (sb - sp) / (sp * sb) - coinBamount = liquidity * (sp - sa) - } else { - coinBamount = liquidity * (sb - sa) - } + let poolId = workerInfo.fields.position_nft.fields.pool + // poolId = poolId.replace('0x0', '0x') + const currentSqrtPrice = poolMap.get(poolId).fields.current_sqrt_price + + const coinAmounts = getCoinAmountFromLiquidity(new BN(liquidity), new BN(currentSqrtPrice), tickIndexToSqrtPriceX64(tickLowerIndex), + tickIndexToSqrtPriceX64(tickUpperIndex)) + + let coinAamount = coinAmounts.coinA + let coinBamount = coinAmounts.coinB + + const [coinA, coinB] = poolMap.get(poolId).type.replace('>', '').split('<')[1].split(', ') + api.add(coinA, coinAamount.toString()) + api.add(coinB, coinBamount.toString()) } - } ) // calculate the Vault TVL. const vaultInfoIds = addresses.Vaults.map(valut => valut.vaultInfo) const vaultInfos = await sui.getObjects(vaultInfoIds) - + for (let i = 0; i < vaultInfos.length; i++) { const baseToken = addresses.Vaults[i].baseToken const tokenAmount = vaultInfos[i].fields.value.fields.coin - api.add(baseToken, tokenAmount) + const vaultDebtVal = vaultInfos[i].fields.value.fields.vault_debt_val + const vaultAmount = parseInt(tokenAmount) + parseInt(vaultDebtVal) + api.add(baseToken, vaultAmount.toString()) } } @@ -312,3 +301,4 @@ module.exports = { calLyfTvlAptos, calLyfTvlSui, } + \ No newline at end of file diff --git a/projects/mole/utils.js b/projects/mole/utils.js index fc5220af102e..175f7da9c85d 100644 --- a/projects/mole/utils.js +++ b/projects/mole/utils.js @@ -1,4 +1,7 @@ +const BigNumber = require("bignumber.js"); +const import_bn6 = require("bn.js"); + function asIntN(int, bits = 32) { return Number(BigInt.asIntN(bits, BigInt(int))); } @@ -7,6 +10,198 @@ function i32BitsToNumber(v) { return asIntN(BigInt(v), 32); } +function toX64_Decimal(num) { + return (new BigNumber(num)).times(new BigNumber(2).pow(64)); +} + +function fromX64_Decimal(num) { + return (new BigNumber(num)).times(new BigNumber(2).pow(-64)); +} + +function signedShiftRight(n0, shiftBy, bitWidth) { + const twoN0 = n0.toTwos(bitWidth).shrn(shiftBy); + twoN0.imaskn(bitWidth - shiftBy + 1); + return twoN0.fromTwos(bitWidth - shiftBy); +} + +function tickIndexToSqrtPricePositive(tick) { + let ratio; + if ((tick & 1) !== 0) { + ratio = new import_bn6("79232123823359799118286999567"); + } else { + ratio = new import_bn6("79228162514264337593543950336"); + } + if ((tick & 2) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79236085330515764027303304731")), 96, 256); + } + if ((tick & 4) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79244008939048815603706035061")), 96, 256); + } + if ((tick & 8) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79259858533276714757314932305")), 96, 256); + } + if ((tick & 16) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79291567232598584799939703904")), 96, 256); + } + if ((tick & 32) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79355022692464371645785046466")), 96, 256); + } + if ((tick & 64) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79482085999252804386437311141")), 96, 256); + } + if ((tick & 128) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("79736823300114093921829183326")), 96, 256); + } + if ((tick & 256) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("80248749790819932309965073892")), 96, 256); + } + if ((tick & 512) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("81282483887344747381513967011")), 96, 256); + } + if ((tick & 1024) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("83390072131320151908154831281")), 96, 256); + } + if ((tick & 2048) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("87770609709833776024991924138")), 96, 256); + } + if ((tick & 4096) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("97234110755111693312479820773")), 96, 256); + } + if ((tick & 8192) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("119332217159966728226237229890")), 96, 256); + } + if ((tick & 16384) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("179736315981702064433883588727")), 96, 256); + } + if ((tick & 32768) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("407748233172238350107850275304")), 96, 256); + } + if ((tick & 65536) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("2098478828474011932436660412517")), 96, 256); + } + if ((tick & 131072) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("55581415166113811149459800483533")), 96, 256); + } + if ((tick & 262144) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("38992368544603139932233054999993551")), 96, 256); + } + return signedShiftRight(ratio, 32, 256); +} + +function tickIndexToSqrtPriceNegative(tickIndex) { + const tick = Math.abs(tickIndex); + let ratio; + if ((tick & 1) !== 0) { + ratio = new import_bn6("18445821805675392311"); + } else { + ratio = new import_bn6("18446744073709551616"); + } + if ((tick & 2) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18444899583751176498")), 64, 256); + } + if ((tick & 4) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18443055278223354162")), 64, 256); + } + if ((tick & 8) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18439367220385604838")), 64, 256); + } + if ((tick & 16) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18431993317065449817")), 64, 256); + } + if ((tick & 32) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18417254355718160513")), 64, 256); + } + if ((tick & 64) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18387811781193591352")), 64, 256); + } + if ((tick & 128) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18329067761203520168")), 64, 256); + } + if ((tick & 256) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("18212142134806087854")), 64, 256); + } + if ((tick & 512) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("17980523815641551639")), 64, 256); + } + if ((tick & 1024) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("17526086738831147013")), 64, 256); + } + if ((tick & 2048) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("16651378430235024244")), 64, 256); + } + if ((tick & 4096) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("15030750278693429944")), 64, 256); + } + if ((tick & 8192) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("12247334978882834399")), 64, 256); + } + if ((tick & 16384) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("8131365268884726200")), 64, 256); + } + if ((tick & 32768) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("3584323654723342297")), 64, 256); + } + if ((tick & 65536) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("696457651847595233")), 64, 256); + } + if ((tick & 131072) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("26294789957452057")), 64, 256); + } + if ((tick & 262144) !== 0) { + ratio = signedShiftRight(ratio.mul(new import_bn6("37481735321082")), 64, 256); + } + return ratio; + } + +function tickIndexToSqrtPriceX64(tickIndex) { + if (tickIndex > 0) { + return new import_bn6(tickIndexToSqrtPricePositive(tickIndex)); + } + return new import_bn6(tickIndexToSqrtPriceNegative(tickIndex)); +} + +/** + * Get token amount fron liquidity. + * @param liquidity - liquidity + * @param curSqrtPrice - Pool current sqrt price + * @param lowerPrice - lower price + * @param upperPrice - upper price + * @param roundUp - is round up + * @returns + */ +function getCoinAmountFromLiquidity(liquidity, curSqrtPrice, lowerPrice, upperPrice, roundUp) { + const liq = new BigNumber(liquidity.toString()); + const curSqrtPriceStr = new BigNumber(curSqrtPrice.toString()); + const lowerPriceStr = new BigNumber(lowerPrice.toString()); + const upperPriceStr = new BigNumber(upperPrice.toString()); + let coinA; + let coinB; + if (curSqrtPrice.lt(lowerPrice)) { + coinA = toX64_Decimal(liq).times(upperPriceStr.minus(lowerPriceStr)).div(lowerPriceStr.times(upperPriceStr)); + coinB = new BigNumber(0); + } else if (curSqrtPrice.lt(upperPrice)) { + coinA = toX64_Decimal(liq).times(upperPriceStr.minus(curSqrtPriceStr)).div(curSqrtPriceStr.times(upperPriceStr)); + coinB = fromX64_Decimal(liq.times(curSqrtPriceStr.minus(lowerPriceStr))); + } else { + coinA = new BigNumber(0); + coinB = fromX64_Decimal(liq.times(upperPriceStr.minus(lowerPriceStr))); + } + if (roundUp) { + return { + coinA: new import_bn6(coinA.integerValue(BigNumber.ROUND_CEIL).toFixed()), + coinB: new import_bn6(coinB.integerValue(BigNumber.ROUND_CEIL).toFixed()) + }; + } + + return { + coinA: new import_bn6(coinA.integerValue(BigNumber.ROUND_FLOOR).toFixed()), + coinB: new import_bn6(coinB.integerValue(BigNumber.ROUND_FLOOR).toFixed()) + }; + } + module.exports = { + asIntN, i32BitsToNumber, + getCoinAmountFromLiquidity, + tickIndexToSqrtPriceX64, }