diff --git a/src/chaincode/dex/swap.helper.spec.ts b/src/chaincode/dex/swap.helper.spec.ts index ed8fbda..6662bd2 100644 --- a/src/chaincode/dex/swap.helper.spec.ts +++ b/src/chaincode/dex/swap.helper.spec.ts @@ -14,7 +14,10 @@ */ import { fixture } from "@gala-chain/test"; import BigNumber from "bignumber.js"; -import { plainToInstance } from "class-transformer"; +import { instanceToInstance, plainToInstance } from "class-transformer"; +import dns from "dns"; +import { monitorEventLoopDelay, performance } from "perf_hooks"; +import { setImmediate } from "timers/promises"; import { DexFeePercentageTypes, Pool, SwapState, TickData, sqrtPriceToTick } from "../../api"; import { DexV3Contract } from "../DexV3Contract"; @@ -279,4 +282,152 @@ describe("swap.helper", () => { expect(resultState.amountCalculated.toNumber()).toBeLessThan(0); }); }); + test("loop performance", async () => { + // Given + const poolHash = "loop-pool"; + const poolWithDustLiquidity = plainToInstance(Pool, { + fee: 500, + bitmap: { + "-2": "0", + "-3": "0", + "-4": "0", + "-5": "0", + "-6": "6129982163463555433433388108601236734474956488734408704", + "-7": "0", + "-8": "0", + "-9": "0", + "-10": "0", + "-11": "0", + "-12": "0", + "-13": "0", + "-14": "0", + "-15": "0", + "-16": "0", + "-17": "0", + "-18": "0", + "-19": "0", + "-20": "0", + "-21": "0", + "-22": "0", + "-23": "0", + "-24": "0", + "-25": "0", + "-26": "8" + }, + token0: "GALA$Unit$none$none", + token1: "GOSMI$Unit$none$none", + liquidity: "0.034029613108643226", + sqrtPrice: "0.86759926423373788029", + tickSpacing: 10, + protocolFees: 0.1, + token0ClassKey: { + type: "none", + category: "Unit", + collection: "GALA", + additionalKey: "none" + }, + token1ClassKey: { + type: "none", + category: "Unit", + collection: "GOSMI", + additionalKey: "none" + }, + feeGrowthGlobal0: "0", + feeGrowthGlobal1: "0.00037637760823854262", + grossPoolLiquidity: "1803.22919862700454574", + protocolFeesToken0: "0", + protocolFeesToken1: "0.0000014231093767904548846723852534735862941902344", + maxLiquidityPerTick: "1917565579412846627735051215301243.08110657663841167978" + }); + poolWithDustLiquidity.genPoolHash = () => poolHash; + + const { ctx } = fixture(DexV3Contract).savedState(poolWithDustLiquidity); + + const state: SwapState = { + amountSpecifiedRemaining: new BigNumber("-41.62"), + amountCalculated: new BigNumber("0"), + sqrtPrice: new BigNumber(poolWithDustLiquidity.sqrtPrice), + tick: sqrtPriceToTick(poolWithDustLiquidity.sqrtPrice), + liquidity: new BigNumber("0.034029613108643226"), + feeGrowthGlobalX: new BigNumber("0"), + protocolFee: new BigNumber("500") + }; + + const bitmapEntriesStart = Object.keys(poolWithDustLiquidity.bitmap).length; + + // When + const exactInput = true; + const zeroForOne = false; + + // logic from quoteExactAmount + const sqrtPriceLimit = zeroForOne + ? new BigNumber("0.000000000000000000054212147") + : new BigNumber("18446050999999999999"); + + const start = performance.eventLoopUtilization(); + + const h = monitorEventLoopDelay({ resolution: 20 }); + h.enable(); + + let timeoutCallbackCount = 0; + let dnsLookupCallbackCount = 0; + + setTimeout(() => { + timeoutCallbackCount++; + }, 0); + dns.lookup("1.1.1.1", {}, () => { + dnsLookupCallbackCount++; + }); + + const iterations = 2000; + + for (let i = 0; i < iterations; i++) { + setTimeout(() => { + timeoutCallbackCount++; + }, 0); + dns.lookup("1.1.1.1", {}, () => { + dnsLookupCallbackCount++; + }); + const tmpState = i === 0 ? state : instanceToInstance(state); + await processSwapSteps( + ctx, + tmpState, + poolWithDustLiquidity, + sqrtPriceLimit, + exactInput, + zeroForOne + ).catch((e) => e); + setTimeout(() => { + timeoutCallbackCount++; + }, 0); + dns.lookup("1.1.1.1", {}, () => { + dnsLookupCallbackCount++; + }); + } + + await setImmediate(); + + h.disable(); + const end = performance.eventLoopUtilization(start); + + // Then + const bitmapEntriesEnd = Object.keys(poolWithDustLiquidity.bitmap).length; + + // started with 25, iterated through to 373! + expect(bitmapEntriesStart).toBe(25); + expect(bitmapEntriesEnd).toBe(373); + + console.log(h.min); + console.log(h.max); + console.log(h.mean); + console.log(h.stddev); + console.log(h.percentiles); + console.log(h.percentile(50)); + console.log(h.percentile(99)); + console.log(end.idle); + console.log(end.utilization); + + expect(timeoutCallbackCount).toBeGreaterThan(iterations); + expect(dnsLookupCallbackCount).toBeGreaterThan(iterations); + }); }); diff --git a/src/chaincode/dex/swap.helper.ts b/src/chaincode/dex/swap.helper.ts index 4b8cc8f..80484a5 100644 --- a/src/chaincode/dex/swap.helper.ts +++ b/src/chaincode/dex/swap.helper.ts @@ -15,6 +15,7 @@ import { ConflictError } from "@gala-chain/api"; import { GalaChainContext } from "@gala-chain/chaincode"; import BigNumber from "bignumber.js"; +import { setImmediate } from "timers/promises"; import { Pool, @@ -150,6 +151,8 @@ export async function processSwapSteps( // Update tick based on new sqrtPrice state.tick = sqrtPriceToTick(state.sqrtPrice); } + + await setImmediate(); } return state;