@swaptoshi/dex-module
Version:
Klayr decentralized exchange (dex) on-chain module
703 lines • 41.8 kB
JavaScript
"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