UNPKG

@swaptoshi/dex-module

Version:

Klayr decentralized exchange (dex) on-chain module

703 lines 41.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEXPool = void 0; const cryptography = require("@klayr/cryptography"); const utils = require("@klayr/utils"); const core_1 = require("../library/core"); const int_1 = require("../library/int"); const periphery_1 = require("../library/periphery"); const pool_1 = require("../pool"); const increase_observation_cardinality_next_1 = require("../../events/increase_observation_cardinality_next"); const pool_initialized_1 = require("../../events/pool_initialized"); const mint_1 = require("../../events/mint"); const collect_1 = require("../../events/collect"); const burn_1 = require("../../events/burn"); const swap_1 = require("../../events/swap"); const flash_1 = require("../../events/flash"); const collect_protocol_1 = require("../../events/collect_protocol"); const tick_info_1 = require("../tick_info"); const position_info_1 = require("../position_info"); const observation_1 = require("../observation"); const tick_bitmap_1 = require("../tick_bitmap"); const defaultStepComputations = Object.freeze({ sqrtPriceStartX96: '0', tickNext: '0', initialized: false, sqrtPriceNextX96: '0', amountIn: '0', amountOut: '0', feeAmount: '0', }); class DEXPool { constructor(pool, stores, events, config, moduleName, simulation = false) { this.address = Buffer.alloc(0); this.klayr32 = ''; this.collectionId = Buffer.alloc(0); this.token0 = Buffer.alloc(0); this.token1 = Buffer.alloc(0); this.fee = '0'; this.tickSpacing = '0'; this.maxLiquidityPerTick = '0'; this.slot0 = { ...pool_1.defaultSlot0 }; this.feeGrowthGlobal0X128 = '0'; this.feeGrowthGlobal1X128 = '0'; this.liquidity = '0'; this.simulation = false; this.feeProtocol = 0; this.mutableDependencyReady = false; this.immutableDependencyReady = false; Object.assign(this, utils.objects.cloneDeep(pool)); this.address = periphery_1.PoolAddress.computeAddress(periphery_1.PoolAddress.getPoolKey(pool.token0, pool.token1, pool.fee)); this.klayr32 = cryptography.address.getKlayr32AddressFromAddress(this.address); this.collectionId = periphery_1.PoolAddress.computePoolId(this.address); this.stores = stores; this.events = events; this.config = config; this.setConfig(config); this.poolStore = this.stores.get(pool_1.PoolStore); this.tickInfoStore = this.stores.get(tick_info_1.TickInfoStore); this.positionInfoStore = this.stores.get(position_info_1.PositionInfoStore); this.observationStore = this.stores.get(observation_1.ObservationStore); this.tickBitmapStore = this.stores.get(tick_bitmap_1.TickBitmapStore); this.simulation = simulation; this.moduleName = moduleName; } createEmulator() { const emulatedPool = new DEXPool(this.toJSON(), this.stores, this.events, this.config, this.moduleName, true); if (this.mutableContext) { emulatedPool.addMutableDependencies(this.mutableContext, this.tokenMethod); } else if (this.immutableContext) { emulatedPool.addImmutableDependencies(this.immutableContext, this.tokenMethod); } return emulatedPool; } setSender(senderAddress) { if (this.mutableContext) { this.mutableContext.senderAddress = senderAddress; } else if (this.immutableContext) { this.immutableContext.senderAddress = senderAddress; } } setConfig(config) { var _a; this.feeProtocol = (_a = config.feeProtocol) !== null && _a !== void 0 ? _a : 0; this.feeProtocolPool = config.feeProtocolPool ? cryptography.address.getAddressFromKlayr32Address(config.feeProtocolPool, config.feeProtocolPool.substring(0, 3)) : undefined; this._validateFeeProtocol(); } addImmutableDependencies(context, tokenMethod) { if (this.mutableDependencyReady || this.immutableDependencyReady) { throw new Error('this instance dependencies already been configured'); } this.immutableContext = context; this.tokenMethod = tokenMethod; this.immutableDependencyReady = true; } addMutableDependencies(context, tokenMethod) { if (this.mutableDependencyReady || this.immutableDependencyReady) { throw new Error('this instance dependencies already been configured'); } this.mutableContext = context; this.immutableContext = context; this.tokenMethod = tokenMethod; this.mutableDependencyReady = true; this.immutableDependencyReady = true; } async snapshotCumulativesInside(tickLower, tickUpper) { this._checkImmutableDependencies(); this._checkTicks(tickLower, tickUpper); const lower = await this.tickInfoStore.getOrDefault(this.immutableContext.context, this.tickInfoStore.getKey(this.address, tickLower)); const upper = await this.tickInfoStore.getOrDefault(this.immutableContext.context, this.tickInfoStore.getKey(this.address, tickUpper)); const tickCumulativeLower = int_1.Int56.from(lower.tickCumulativeOutside); const secondsPerLiquidityOutsideLowerX128 = int_1.Uint160.from(lower.secondsPerLiquidityOutsideX128); const secondsOutsideLower = int_1.Uint32.from(lower.secondsOutside); const initializedLower = lower.initialized; if (!initializedLower) throw new Error('lower not initialized'); const tickCumulativeUpper = int_1.Int56.from(upper.tickCumulativeOutside); const secondsPerLiquidityOutsideUpperX128 = int_1.Uint160.from(upper.secondsPerLiquidityOutsideX128); const secondsOutsideUpper = int_1.Uint32.from(upper.secondsOutside); const initializedUpper = upper.initialized; if (!initializedUpper) throw new Error('upper not initialized'); const _slot0 = this.slot0; if (int_1.Int24.from(_slot0.tick).lt(tickLower)) { return [ tickCumulativeLower.sub(tickCumulativeUpper).toString(), secondsPerLiquidityOutsideLowerX128.sub(secondsPerLiquidityOutsideUpperX128).toString(), secondsOutsideLower.sub(secondsOutsideUpper).toString(), ]; } if (int_1.Int24.from(_slot0.tick).lt(tickUpper)) { const time = this.immutableContext.timestamp; const [tickCumulative, secondsPerLiquidityCumulativeX128] = await core_1.Oracle.observeSingle(this.observationStore, this.immutableContext.context, this.address, time, '0', _slot0.tick, _slot0.observationIndex, this.liquidity, _slot0.observationCardinality); return [ int_1.Int56.from(tickCumulative).sub(tickCumulativeLower).sub(tickCumulativeUpper).toString(), int_1.Uint160.from(secondsPerLiquidityCumulativeX128).sub(secondsPerLiquidityOutsideLowerX128).sub(secondsPerLiquidityOutsideUpperX128).toString(), int_1.Uint32.from(time).sub(secondsOutsideLower).sub(secondsOutsideUpper).toString(), ]; } return [ tickCumulativeUpper.sub(tickCumulativeLower).toString(), secondsPerLiquidityOutsideUpperX128.sub(secondsPerLiquidityOutsideLowerX128).toString(), secondsOutsideUpper.sub(secondsOutsideLower).toString(), ]; } async observe(secondsAgos) { this._checkImmutableDependencies(); return core_1.Oracle.observe(this.observationStore, this.immutableContext.context, this.address, this.immutableContext.timestamp, secondsAgos, this.slot0.tick, this.slot0.observationIndex, this.liquidity, this.slot0.observationCardinality); } async increaseObservationCardinalityNext(observationCardinalityNext) { this._checkMutableDependencies(); const observationCardinalityNextOld = int_1.Uint16.from(this.slot0.observationCardinalityNext); const observationCardinalityNextNew = int_1.Uint16.from(await core_1.Oracle.grow(this.observationStore, this.mutableContext.context, this.address, observationCardinalityNextOld.toString(), observationCardinalityNext, this.simulation)); this.slot0.observationCardinalityNext = observationCardinalityNextNew.toString(); if (!observationCardinalityNextOld.eq(observationCardinalityNextNew)) { const events = this.events.get(increase_observation_cardinality_next_1.IncreaseObservationCardinalityNextEvent); events.add(this.mutableContext.context, { observationCardinalityNextOld: observationCardinalityNextOld.toString(), observationCardinalityNextNew: observationCardinalityNextNew.toString(), }, [this.address]); } if (!this.simulation) { await this._saveStore(); } } async initialize(sqrtPriceX96) { this._checkMutableDependencies(); if (!int_1.Uint160.from(this.slot0.sqrtPriceX96).eq(0)) throw new Error('AI'); const tick = core_1.TickMath.getTickAtSqrtRatio(sqrtPriceX96); const [cardinality, cardinalityNext] = await core_1.Oracle.initialize(this.observationStore, this.mutableContext.context, this.address, this.mutableContext.timestamp, this.simulation); this.slot0 = { sqrtPriceX96, tick, observationIndex: '0', observationCardinality: cardinality, observationCardinalityNext: cardinalityNext, }; if (!this.simulation) { const events = this.events.get(pool_initialized_1.PoolInitializedEvent); events.add(this.mutableContext.context, { sqrtPriceX96, tick }, [this.address]); await this._saveStore(); } } async mint(recipient, tickLower, tickUpper, amount, data, callback) { this._checkMutableDependencies(); if (int_1.Uint128.from(amount).lte(0)) throw new Error('amount must be positive'); const tickLowerBefore = await this.tickInfoStore.getOrDefault(this.mutableContext.context, this.tickInfoStore.getKey(this.address, tickLower)); const tickUpperBefore = await this.tickInfoStore.getOrDefault(this.mutableContext.context, this.tickInfoStore.getKey(this.address, tickUpper)); const [_, amount0Int, amount1Int, tickLowerAfter, tickUpperAfter] = await this._modifyPosition({ owner: recipient, tickLower, tickUpper, liquidityDelta: int_1.Int128.from(int_1.Uint128.from(amount).toString()).toString(), }); const amount0 = int_1.Uint256.from(amount0Int); const amount1 = int_1.Uint256.from(amount1Int); let balance0Before = int_1.Uint256.from(0); let balance1Before = int_1.Uint256.from(0); if (amount0.gt(0)) balance0Before = int_1.Uint256.from(await this._getBalance0()); if (amount1.gt(0)) balance1Before = int_1.Uint256.from(await this._getBalance1()); await callback(amount0.toString(), amount1.toString(), data, this.toJSON()); if (!this.simulation) { if (amount0.gt(0) && balance0Before.add(amount0).gt(await this._getBalance0())) throw new Error('M0'); if (amount1.gt(0) && balance1Before.add(amount1).gt(await this._getBalance1())) throw new Error('M1'); const events = this.events.get(mint_1.MintEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: recipient, tickLower, tickUpper, lowerLiquidityNetBefore: tickLowerBefore.liquidityNet, lowerLiquidityNet: tickLowerAfter.liquidityNet, upperLiquidityNetBefore: tickUpperBefore.liquidityNet, upperLiquidityNet: tickUpperAfter.liquidityNet, }, [this.address, recipient]); await this._saveStore(); } return [amount0.toString(), amount1.toString()]; } async collect(recipient, tickLower, tickUpper, amount0Requested, amount1Requested) { this._checkMutableDependencies(); const position = await core_1.Position.get(this.positionInfoStore, this.mutableContext.context, this.address, this.mutableContext.senderAddress, tickLower, tickUpper); const amount0 = int_1.Uint128.from(amount0Requested).gt(position.tokensOwed0) ? int_1.Uint128.from(position.tokensOwed0) : int_1.Uint128.from(amount0Requested); const amount1 = int_1.Uint128.from(amount1Requested).gt(position.tokensOwed1) ? int_1.Uint128.from(position.tokensOwed1) : int_1.Uint128.from(amount1Requested); if (amount0.gt(0)) { position.tokensOwed0 = int_1.Uint128.from(position.tokensOwed0).sub(amount0).toString(); if (!this.simulation) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, recipient, this.token0, amount0.toBigInt()); } } if (amount1.gt(0)) { position.tokensOwed1 = int_1.Uint128.from(position.tokensOwed1).sub(amount1).toString(); if (!this.simulation) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, recipient, this.token1, amount1.toBigInt()); } } if (!this.simulation) { await core_1.Position.set(this.positionInfoStore, this.mutableContext.context, this.address, this.mutableContext.senderAddress, tickLower, tickUpper, position); const events = this.events.get(collect_1.CollectEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: recipient, tickLower, tickUpper, amount0: amount0.toString(), amount1: amount1.toString(), }, [this.address, recipient]); await this._saveStore(); } return [amount0.toString(), amount1.toString()]; } async burn(tickLower, tickUpper, amount) { this._checkMutableDependencies(); const tickLowerBefore = await this.tickInfoStore.getOrDefault(this.mutableContext.context, this.tickInfoStore.getKey(this.address, tickLower)); const tickUpperBefore = await this.tickInfoStore.getOrDefault(this.mutableContext.context, this.tickInfoStore.getKey(this.address, tickUpper)); const [position, amount0Int, amount1Int, tickLowerAfter, tickUpperAfter] = await this._modifyPosition({ owner: this.mutableContext.senderAddress, tickLower, tickUpper, liquidityDelta: int_1.Int128.from(int_1.Int256.from(amount).mul(-1)).toString(), }); const amount0 = int_1.Uint256.from(0).sub(amount0Int); const amount1 = int_1.Uint256.from(0).sub(amount1Int); if (!this.simulation) { if (amount0.gt(0) || amount1.gt(0)) { position.tokensOwed0 = int_1.Uint128.from(position.tokensOwed0).add(int_1.Uint128.from(amount0)).toString(); position.tokensOwed1 = int_1.Uint128.from(position.tokensOwed1).add(int_1.Uint128.from(amount1)).toString(); } const events = this.events.get(burn_1.BurnEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, tickLower, tickUpper, lowerLiquidityNetBefore: tickLowerBefore.liquidityNet, lowerLiquidityNet: tickLowerAfter.liquidityNet, upperLiquidityNetBefore: tickUpperBefore.liquidityNet, upperLiquidityNet: tickUpperAfter.liquidityNet, }, [this.address, this.mutableContext.senderAddress]); await core_1.Position.set(this.positionInfoStore, this.mutableContext.context, this.address, this.mutableContext.senderAddress, tickLower, tickUpper, position); await this._saveStore(); } return [amount0.toString(), amount1.toString()]; } async swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data, callback) { let context; let timestamp; if (this.simulation) { this._checkImmutableDependencies(); context = this.immutableContext.context; timestamp = this.immutableContext.timestamp; } else { this._checkMutableDependencies(); context = this.mutableContext.context; timestamp = this.mutableContext.timestamp; } let amount0; let amount1; if (int_1.Int256.from(amountSpecified).eq(0)) throw new Error('AS'); const slot0Start = this.slot0; const slot0Before = utils.objects.cloneDeep(this.slot0); const liquidityBefore = this.liquidity; const feeGrowthGlobal0X128Before = this.feeGrowthGlobal0X128; const feeGrowthGlobal1X128Before = this.feeGrowthGlobal1X128; if (zeroForOne ? int_1.Uint160.from(sqrtPriceLimitX96).gte(slot0Start.sqrtPriceX96) || int_1.Uint160.from(sqrtPriceLimitX96).lte(core_1.TickMath.MIN_SQRT_RATIO) : int_1.Uint160.from(sqrtPriceLimitX96).lte(slot0Start.sqrtPriceX96) || int_1.Uint160.from(sqrtPriceLimitX96).gte(core_1.TickMath.MAX_SQRT_RATIO)) { throw new Error('SPL'); } const cache = { liquidityStart: this.liquidity, blockTimestamp: timestamp, feeProtocol: zeroForOne ? int_1.Uint8.from(this.feeProtocol).mod(16).toString() : int_1.Uint8.from(this.feeProtocol).shr(4).toString(), secondsPerLiquidityCumulativeX128: '0', tickCumulative: '0', computedLatestObservation: false, }; const exactInput = int_1.Int256.from(amountSpecified).gt(0); const state = { amountSpecifiedRemaining: amountSpecified, amountCalculated: '0', sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: zeroForOne ? this.feeGrowthGlobal0X128 : this.feeGrowthGlobal1X128, protocolFee: '0', liquidity: cache.liquidityStart, }; while (!int_1.Int256.from(state.amountSpecifiedRemaining).eq(0) && !int_1.Uint160.from(state.sqrtPriceX96).eq(sqrtPriceLimitX96)) { const step = { ...defaultStepComputations }; const [tickNext, initialized] = await core_1.TickBitmap.nextInitializedTickWithinOneWord(this.tickBitmapStore, context, this.address, state.tick, this.tickSpacing, zeroForOne); step.sqrtPriceStartX96 = state.sqrtPriceX96; step.tickNext = tickNext; step.initialized = initialized; if (int_1.Int24.from(step.tickNext).lt(core_1.TickMath.MIN_TICK)) { step.tickNext = core_1.TickMath.MIN_TICK; } else if (int_1.Int24.from(step.tickNext).gt(core_1.TickMath.MAX_TICK)) { step.tickNext = core_1.TickMath.MAX_TICK; } step.sqrtPriceNextX96 = core_1.TickMath.getSqrtRatioAtTick(step.tickNext); const [sqrtPriceX96, amountIn, amountOut, feeAmount] = core_1.SwapMath.computeSwapStep(state.sqrtPriceX96, (zeroForOne ? int_1.Uint160.from(step.sqrtPriceNextX96).lt(sqrtPriceLimitX96) : int_1.Uint160.from(step.sqrtPriceNextX96).gt(sqrtPriceLimitX96)) ? sqrtPriceLimitX96 : step.sqrtPriceNextX96, state.liquidity, state.amountSpecifiedRemaining, this.fee); state.sqrtPriceX96 = sqrtPriceX96; step.amountIn = amountIn; step.amountOut = amountOut; step.feeAmount = feeAmount; if (exactInput) { state.amountSpecifiedRemaining = int_1.Int256.from(state.amountSpecifiedRemaining).sub(int_1.Int256.from(step.amountIn).add(step.feeAmount)).toString(); state.amountCalculated = int_1.Int256.from(state.amountCalculated).sub(int_1.Int256.from(step.amountOut)).toString(); } else { state.amountSpecifiedRemaining = int_1.Int256.from(state.amountSpecifiedRemaining).add(int_1.Int256.from(step.amountOut)).toString(); state.amountCalculated = int_1.Int256.from(state.amountCalculated).add(int_1.Int256.from(step.amountIn).add(step.feeAmount)).toString(); } if (int_1.Uint8.from(cache.feeProtocol).gt(0)) { const delta = int_1.Uint256.from(step.feeAmount).div(cache.feeProtocol); step.feeAmount = int_1.Uint256.from(step.feeAmount).sub(delta).toString(); state.protocolFee = int_1.Uint128.from(state.protocolFee).add(int_1.Uint128.from(delta)).toString(); } if (int_1.Uint128.from(state.liquidity).gt(0)) { state.feeGrowthGlobalX128 = int_1.Uint256.from(state.feeGrowthGlobalX128) .add(core_1.FullMath.mulDiv(step.feeAmount, core_1.FixedPoint128.Q128, state.liquidity)) .toString(); } if (state.sqrtPriceX96 === step.sqrtPriceNextX96) { if (step.initialized) { if (!cache.computedLatestObservation) { const [tickCumulative, secondsPerLiquidityCumulativeX128] = await core_1.Oracle.observeSingle(this.observationStore, context, this.address, cache.blockTimestamp, '0', slot0Start.tick, slot0Start.observationIndex, cache.liquidityStart, slot0Start.observationCardinality); cache.tickCumulative = tickCumulative; cache.secondsPerLiquidityCumulativeX128 = secondsPerLiquidityCumulativeX128; cache.computedLatestObservation = true; } let liquidityNet = int_1.Int128.from(await core_1.Tick.cross(this.tickInfoStore, context, this.address, step.tickNext, zeroForOne ? state.feeGrowthGlobalX128 : this.feeGrowthGlobal0X128, zeroForOne ? this.feeGrowthGlobal1X128 : state.feeGrowthGlobalX128, cache.secondsPerLiquidityCumulativeX128, cache.tickCumulative, cache.blockTimestamp, this.simulation)); if (zeroForOne) liquidityNet = liquidityNet.mul(-1); state.liquidity = core_1.LiquidityMath.addDelta(state.liquidity, liquidityNet.toString()); } state.tick = zeroForOne ? int_1.Int24.from(step.tickNext).sub(1).toString() : step.tickNext; } else if (state.sqrtPriceX96 !== step.sqrtPriceStartX96) { state.tick = core_1.TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); } } if (state.tick !== slot0Start.tick) { const [observationIndex, observationCardinality] = await core_1.Oracle.write(this.observationStore, context, this.address, slot0Start.observationIndex, cache.blockTimestamp, slot0Start.tick, cache.liquidityStart, slot0Start.observationCardinality, slot0Start.observationCardinalityNext, this.simulation); this.slot0.sqrtPriceX96 = state.sqrtPriceX96; this.slot0.tick = state.tick; this.slot0.observationIndex = observationIndex; this.slot0.observationCardinality = observationCardinality; } else { this.slot0.sqrtPriceX96 = state.sqrtPriceX96; } if (cache.liquidityStart !== state.liquidity) this.liquidity = state.liquidity; if (zeroForOne) { this.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; if (!this.simulation && int_1.Uint128.from(state.protocolFee).gt(0)) { if (this._checkFeeProtocol()) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, this.feeProtocolPool, this.token0, BigInt(state.protocolFee)); const events = this.events.get(collect_protocol_1.CollectProtocolEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: this.feeProtocolPool, amount0: state.protocolFee, amount1: '0', }, [this.address, this.feeProtocolPool]); } else { await this.tokenMethod.lock(this.mutableContext.context, this.address, this.moduleName, this.token0, BigInt(state.protocolFee)); } } } else { this.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; if (!this.simulation && int_1.Uint128.from(state.protocolFee).gt(0)) { if (this._checkFeeProtocol()) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, this.feeProtocolPool, this.token1, BigInt(state.protocolFee)); const events = this.events.get(collect_protocol_1.CollectProtocolEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: this.feeProtocolPool, amount0: '0', amount1: state.protocolFee, }, [this.address, this.feeProtocolPool]); } else { await this.tokenMethod.lock(this.mutableContext.context, this.address, this.moduleName, this.token1, BigInt(state.protocolFee)); } } } if (zeroForOne === exactInput) { amount0 = int_1.Int256.from(amountSpecified).sub(state.amountSpecifiedRemaining); amount1 = int_1.Int256.from(state.amountCalculated); } else { amount0 = int_1.Int256.from(state.amountCalculated); amount1 = int_1.Int256.from(amountSpecified).sub(state.amountSpecifiedRemaining); } if (zeroForOne) { if (!this.simulation && amount1.lt(0)) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, recipient, this.token1, int_1.Uint256.from(0).sub(amount1).toBigInt()); } const balance0Before = int_1.Uint256.from(await this._getBalance0()); await callback(amount0.toString(), amount1.toString(), data, this.toJSON()); if (!this.simulation && this.mutableContext.senderAddress.compare(this.address) !== 0 && balance0Before.add(int_1.Uint256.from(amount0)).gt(await this._getBalance0())) { throw new Error('IIA'); } } else { if (!this.simulation && amount0.lt(0)) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, recipient, this.token0, int_1.Uint256.from(0).sub(amount0).toBigInt()); } const balance1Before = int_1.Uint256.from(await this._getBalance1()); await callback(amount0.toString(), amount1.toString(), data, this.toJSON()); if (!this.simulation && this.mutableContext.senderAddress.compare(this.address) !== 0 && balance1Before.add(int_1.Uint256.from(amount1)).gt(await this._getBalance1())) { throw new Error('IIA'); } } if (!this.simulation) { const events = this.events.get(swap_1.SwapEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: recipient, amount0: amount0.toString(), amount1: amount1.toString(), sqrtPriceX96Before: slot0Before.sqrtPriceX96, sqrtPriceX96: state.sqrtPriceX96, liquidityBefore, liquidity: state.liquidity, tickBefore: slot0Before.tick, tick: state.tick, feeGrowthGlobal0X128Before, feeGrowthGlobal0X128: this.feeGrowthGlobal0X128, feeGrowthGlobal1X128Before, feeGrowthGlobal1X128: this.feeGrowthGlobal1X128, }, [this.address, recipient]); await this._saveStore(); } return [amount0.toString(), amount1.toString()]; } async flash(recipient, amount0, amount1, data, callback) { this._checkMutableDependencies(); const _liquidity = this.liquidity; if (int_1.Uint128.from(_liquidity).lte(0)) throw new Error('L'); const fee0 = int_1.Uint256.from(core_1.FullMath.mulDivRoundingUp(amount0, this.fee, '1000000')); const fee1 = int_1.Uint256.from(core_1.FullMath.mulDivRoundingUp(amount1, this.fee, '1000000')); const balance0Before = int_1.Uint256.from(await this._getBalance0()); const balance1Before = int_1.Uint256.from(await this._getBalance1()); if (!this.simulation) { if (int_1.Uint256.from(amount0).gt(0)) await this.tokenMethod.transfer(this.mutableContext.context, this.address, recipient, this.token0, BigInt(amount0)); if (int_1.Uint256.from(amount1).gt(0)) await this.tokenMethod.transfer(this.mutableContext.context, this.address, recipient, this.token1, BigInt(amount1)); } await callback(fee0.toString(), fee1.toString(), data, this.toJSON()); const balance0After = int_1.Uint256.from(await this._getBalance0()); const balance1After = int_1.Uint256.from(await this._getBalance1()); if (!this.simulation) { if (balance0Before.add(fee0).gt(balance0After)) throw new Error('F0'); if (balance1Before.add(fee1).gt(balance1After)) throw new Error('F1'); } const paid0 = int_1.Uint256.from(balance0After).sub(balance0Before); const paid1 = int_1.Uint256.from(balance1After).sub(balance1Before); if (paid0.gt(0)) { const feeProtocol0 = int_1.Uint8.from(this.feeProtocol).mod(16); const fees0 = int_1.Uint256.from(feeProtocol0.eq(0) ? 0 : paid0.div(feeProtocol0)); if (int_1.Uint128.from(fees0).gt(0)) { if (this._checkFeeProtocol()) { if (!this.simulation) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, this.feeProtocolPool, this.token0, fees0.toBigInt()); } } else { if (!this.simulation) { await this.tokenMethod.lock(this.mutableContext.context, this.address, this.moduleName, this.token0, fees0.toBigInt()); } } const events = this.events.get(collect_protocol_1.CollectProtocolEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: this.feeProtocolPool, amount0: fees0.toString(), amount1: '0', }, [this.address, this.feeProtocolPool]); } this.feeGrowthGlobal0X128 = int_1.Uint256.from(this.feeGrowthGlobal0X128) .add(core_1.FullMath.mulDiv(paid0.sub(fees0).toString(), core_1.FixedPoint128.Q128, _liquidity)) .toString(); } if (paid1.gt(0)) { const feeProtocol1 = int_1.Uint8.from(this.feeProtocol).shr(4); const fees1 = int_1.Uint256.from(feeProtocol1.eq(0) ? 0 : paid1.div(feeProtocol1)); if (int_1.Uint128.from(fees1).gt(0)) { if (this._checkFeeProtocol()) { if (!this.simulation) { await this.tokenMethod.transfer(this.mutableContext.context, this.address, this.feeProtocolPool, this.token1, fees1.toBigInt()); } } else { if (!this.simulation) { await this.tokenMethod.lock(this.mutableContext.context, this.address, this.moduleName, this.token1, fees1.toBigInt()); } } const events = this.events.get(collect_protocol_1.CollectProtocolEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: this.feeProtocolPool, amount0: '0', amount1: fees1.toString(), }, [this.address, this.feeProtocolPool]); } this.feeGrowthGlobal1X128 = int_1.Uint256.from(this.feeGrowthGlobal1X128) .add(core_1.FullMath.mulDiv(paid1.sub(fees1).toString(), core_1.FixedPoint128.Q128, _liquidity)) .toString(); } if (!this.simulation) { const events = this.events.get(flash_1.FlashEvent); events.add(this.mutableContext.context, { senderAddress: this.mutableContext.senderAddress, recipientAddress: recipient, amount0, amount1, paid0: paid0.toString(), paid1: paid1.toString(), }, [this.address, recipient]); await this._saveStore(); } } async getTick(tick) { this._checkImmutableDependencies(); return this.tickInfoStore.getOrDefault(this.immutableContext.context, this.tickInfoStore.getKey(this.address, tick)); } async getTickBitmap(index) { this._checkImmutableDependencies(); return (await this.tickBitmapStore.getOrDefault(this.immutableContext.context, this.tickBitmapStore.getKey(this.address, index))).bitmap; } toJSON() { return utils.objects.cloneDeep({ token0: this.token0, token1: this.token1, fee: this.fee, tickSpacing: this.tickSpacing, maxLiquidityPerTick: this.maxLiquidityPerTick, feeGrowthGlobal0X128: this.feeGrowthGlobal0X128, feeGrowthGlobal1X128: this.feeGrowthGlobal1X128, liquidity: this.liquidity, slot0: this.slot0, }); } async _getBalance0() { return this.tokenMethod.getAvailableBalance(this.immutableContext.context, this.address, this.token0); } async _getBalance1() { return this.tokenMethod.getAvailableBalance(this.immutableContext.context, this.address, this.token1); } async _saveStore() { await this.poolStore.set(this.mutableContext.context, this.address, this.toJSON()); } _checkMutableDependencies() { if (!this.mutableDependencyReady) { throw new Error('dependencies not configured'); } } _checkImmutableDependencies() { if (!this.immutableDependencyReady) { throw new Error('dependencies not configured'); } } _checkFeeProtocol() { return this.feeProtocol > 0 && this.feeProtocolPool && this.feeProtocolPool.length === 20; } _checkTicks(tickLower, tickUpper) { if (int_1.Int24.from(tickLower).gte(tickUpper)) throw new Error('TLU'); if (int_1.Int24.from(tickLower).lt(core_1.TickMath.MIN_TICK)) throw new Error('TLM'); if (int_1.Int24.from(tickUpper).gt(core_1.TickMath.MAX_TICK)) throw new Error('TUM'); } async _modifyPosition(params) { this._checkTicks(params.tickLower, params.tickUpper); let amount0 = int_1.Int256.from(0); let amount1 = int_1.Int256.from(0); const _slot0 = this.slot0; const [position, lowerTickInfo, upperTickInfo] = await this._updatePosition(params.owner, params.tickLower, params.tickUpper, params.liquidityDelta, _slot0.tick); if (!int_1.Int128.from(params.liquidityDelta).eq(0)) { if (int_1.Int24.from(_slot0.tick).lt(params.tickLower)) { amount0 = int_1.Int256.from(core_1.SqrtPriceMath.getAmount0Delta(core_1.TickMath.getSqrtRatioAtTick(params.tickLower), core_1.TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta)); } else if (int_1.Int24.from(_slot0.tick).lt(params.tickUpper)) { const liquidityBefore = int_1.Uint128.from(this.liquidity); const [observationIndex, observationCardinality] = await core_1.Oracle.write(this.observationStore, this.mutableContext.context, this.address, _slot0.observationIndex, this.mutableContext.timestamp, _slot0.tick, liquidityBefore.toString(), _slot0.observationCardinality, _slot0.observationCardinalityNext, this.simulation); this.slot0.observationIndex = observationIndex; this.slot0.observationCardinality = observationCardinality; amount0 = int_1.Int256.from(core_1.SqrtPriceMath.getAmount0Delta(_slot0.sqrtPriceX96, core_1.TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta)); amount1 = int_1.Int256.from(core_1.SqrtPriceMath.getAmount1Delta(core_1.TickMath.getSqrtRatioAtTick(params.tickLower), _slot0.sqrtPriceX96, params.liquidityDelta)); this.liquidity = core_1.LiquidityMath.addDelta(liquidityBefore.toString(), params.liquidityDelta); } else { amount1 = int_1.Int256.from(core_1.SqrtPriceMath.getAmount1Delta(core_1.TickMath.getSqrtRatioAtTick(params.tickLower), core_1.TickMath.getSqrtRatioAtTick(params.tickUpper), params.liquidityDelta)); } } return [position, amount0.toString(), amount1.toString(), lowerTickInfo, upperTickInfo]; } async _updatePosition(owner, tickLower, tickUpper, liquidityDelta, tick) { const position = await core_1.Position.get(this.positionInfoStore, this.mutableContext.context, this.address, owner, tickLower, tickUpper); const _feeGrowthGlobal0X128 = int_1.Uint256.from(this.feeGrowthGlobal0X128); const _feeGrowthGlobal1X128 = int_1.Uint256.from(this.feeGrowthGlobal1X128); let flippedLower; let flippedUpper; let lowerTickInfo = await this.tickInfoStore.getOrDefault(this.mutableContext.context, this.tickInfoStore.getKey(this.address, tickLower)); let upperTickInfo = await this.tickInfoStore.getOrDefault(this.mutableContext.context, this.tickInfoStore.getKey(this.address, tickUpper)); if (!int_1.Int128.from(liquidityDelta).eq(0)) { const time = this.mutableContext.timestamp; const [tickCumulative, secondsPerLiquidityCumulativeX128] = await core_1.Oracle.observeSingle(this.observationStore, this.mutableContext.context, this.address, time, '0', this.slot0.tick, this.slot0.observationIndex, this.liquidity, this.slot0.observationCardinality); [flippedLower, lowerTickInfo] = await core_1.Tick.update(this.tickInfoStore, this.mutableContext.context, this.address, tickLower, tick, liquidityDelta, _feeGrowthGlobal0X128.toString(), _feeGrowthGlobal1X128.toString(), secondsPerLiquidityCumulativeX128, tickCumulative, time, false, this.maxLiquidityPerTick, this.simulation); [flippedUpper, upperTickInfo] = await core_1.Tick.update(this.tickInfoStore, this.mutableContext.context, this.address, tickUpper, tick, liquidityDelta, _feeGrowthGlobal0X128.toString(), _feeGrowthGlobal1X128.toString(), secondsPerLiquidityCumulativeX128, tickCumulative, time, true, this.maxLiquidityPerTick, this.simulation); if (flippedLower) { await core_1.TickBitmap.flipTick(this.tickBitmapStore, this.mutableContext.context, this.address, tickLower, this.tickSpacing, this.simulation); } if (flippedUpper) { await core_1.TickBitmap.flipTick(this.tickBitmapStore, this.mutableContext.context, this.address, tickUpper, this.tickSpacing, this.simulation); } } const [feeGrowthInside0X128, feeGrowthInside1X128] = await core_1.Tick.getFeeGrowthInside(this.tickInfoStore, this.mutableContext.context, this.address, tickLower, tickUpper, tick, _feeGrowthGlobal0X128.toString(), _feeGrowthGlobal1X128.toString()); core_1.Position.update(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); if (!this.simulation) { await core_1.Position.set(this.positionInfoStore, this.mutableContext.context, this.address, owner, tickLower, tickUpper, position); } if (int_1.Int128.from(liquidityDelta).lt(0)) { if (flippedLower) { await core_1.Tick.clear(this.tickInfoStore, this.mutableContext.context, this.address, tickLower, this.simulation); } if (flippedUpper) { await core_1.Tick.clear(this.tickInfoStore, this.mutableContext.context, this.address, tickUpper, this.simulation); } } return [position, lowerTickInfo, upperTickInfo]; } _validateFeeProtocol() { if (this._checkFeeProtocol()) { const feeProtocol0 = int_1.Uint8.from(this.feeProtocol).mod(16); const feeProtocol1 = int_1.Uint8.from(this.feeProtocol).shr(4); if (!(int_1.Uint8.from(feeProtocol0).eq(0) || (int_1.Uint8.from(feeProtocol0).gte(4) && int_1.Uint8.from(feeProtocol0).lte(10))) || !(int_1.Uint8.from(feeProtocol1).eq(0) || (int_1.Uint8.from(feeProtocol1).gte(4) && int_1.Uint8.from(feeProtocol1).lte(10)))) { throw new Error('setFeeeProtocol failed'); } } } } exports.DEXPool = DEXPool; //# sourceMappingURL=pool.js.map