diff --git a/src/api/types/DexDtos.ts b/src/api/types/DexDtos.ts index 412ace6..c1cdb9d 100644 --- a/src/api/types/DexDtos.ts +++ b/src/api/types/DexDtos.ts @@ -329,6 +329,61 @@ export class BurnDto extends SubmitCallDTO { } } +export class UpdateTickFeeGrowthDto extends SubmitCallDTO { + @IsNotEmpty() + @IsString() + poolHash: string; + + @IsNotEmpty() + @IsString() + tickNumber: string; + + @BigNumberIsPositive() + @BigNumberProperty() + newFeeGrowthOutside0: BigNumber; + + @BigNumberIsPositive() + @BigNumberProperty() + newFeeGrowthOutside1: BigNumber; +} + +export class UpdatePositionFeeGrowthDto extends SubmitCallDTO { + @IsNotEmpty() + @IsString() + poolHash: string; + + @IsNotEmpty() + @IsInt() + @Max(TickData.MAX_TICK) + public tickUpper: number; + + @IsNotEmpty() + @IsInt() + @Min(TickData.MIN_TICK) + @IsLessThan("tickUpper") + public tickLower: number; + + @IsNotEmpty() + @IsString() + public positionId: string; + + @BigNumberIsPositive() + @BigNumberProperty() + newFeeGrowthInside0Last: BigNumber; + + @BigNumberIsPositive() + @BigNumberProperty() + newFeeGrowthInside1Last: BigNumber; + + @BigNumberIsPositive() + @BigNumberProperty() + newTokenOwed0: BigNumber; + + @BigNumberIsPositive() + @BigNumberProperty() + newTokenOwed1: BigNumber; +} + export class GetPoolDto extends ChainCallDTO { @IsNotEmpty() @ValidateNested() diff --git a/src/chaincode/DexV3Contract.ts b/src/chaincode/DexV3Contract.ts index fad73db..8db3e42 100644 --- a/src/chaincode/DexV3Contract.ts +++ b/src/chaincode/DexV3Contract.ts @@ -89,7 +89,9 @@ import { SwapDto, SwapResDto, TickData, - TransferDexPositionDto + TransferDexPositionDto, + UpdatePositionFeeGrowthDto, + UpdateTickFeeGrowthDto } from "../api/"; import { addLiquidity, @@ -128,7 +130,9 @@ import { setGlobalLimitOrderConfig, setProtocolFee, swap, - transferDexPosition + transferDexPosition, + updatePositionFeeGrowth, + updateTickFeeGrowth } from "./dex"; import { getTickData } from "./dex/tickData.helper"; import { @@ -472,6 +476,25 @@ export class DexV3Contract extends GalaContract { return await getBalanceDelta(ctx, dto); } + @Submit({ + in: UpdateTickFeeGrowthDto, + out: DexPositionOwner + }) + public async UpdateTickFeeGrowth(ctx: GalaChainContext, dto: UpdateTickFeeGrowthDto): Promise { + return updateTickFeeGrowth(ctx, dto); + } + + @Submit({ + in: UpdatePositionFeeGrowthDto, + out: DexPositionOwner + }) + public async UpdatePositionFeeGrowth( + ctx: GalaChainContext, + dto: UpdatePositionFeeGrowthDto + ): Promise { + return updatePositionFeeGrowth(ctx, dto); + } + @Submit({ in: TransferDexPositionDto, out: DexPositionOwner, diff --git a/src/chaincode/dex/index.ts b/src/chaincode/dex/index.ts index 512349e..6940151 100644 --- a/src/chaincode/dex/index.ts +++ b/src/chaincode/dex/index.ts @@ -45,3 +45,5 @@ export * from "./manageWhitelist"; export * from "./privatePoolUtils"; export * from "./updateBitmap"; export * from "./transferUnclaimedFunds"; +export * from "./updatePositionFeeGrowth"; +export * from "./updateTicksFeeGrowth"; diff --git a/src/chaincode/dex/updatePositionFeeGrowth.spec.ts b/src/chaincode/dex/updatePositionFeeGrowth.spec.ts new file mode 100644 index 0000000..bb937b4 --- /dev/null +++ b/src/chaincode/dex/updatePositionFeeGrowth.spec.ts @@ -0,0 +1,66 @@ +/* + * Copyright (c) Gala Games Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { TokenClassKey, randomUniqueKey } from "@gala-chain/api"; +import { currency, fixture, users } from "@gala-chain/test"; +import BigNumber from "bignumber.js"; + +import { DexPositionData, UpdatePositionFeeGrowthDto } from "../../api"; +import { DexV3Contract } from "../DexV3Contract"; +import dex from "../test/dex"; + +describe("update position", () => { + it("should update position fee growth with provided value", async () => { + // Given + const currencyClassKey: TokenClassKey = currency.tokenClassKey(); + const dexClassKey: TokenClassKey = dex.tokenClassKey(); + + const position = new DexPositionData( + "test-pool-hash", + "test-id", + 200, + 100, + currencyClassKey, + dexClassKey, + 10000 + ); + + // Setup the fixture + const { ctx, contract } = fixture(DexV3Contract).registeredUsers(users.testUser1).savedState(position); + + const updateDto = new UpdatePositionFeeGrowthDto(); + updateDto.poolHash = "test-pool-hash"; + updateDto.positionId = "test-id"; + updateDto.tickUpper = 200; + updateDto.tickLower = 100; + + updateDto.newFeeGrowthInside0Last = new BigNumber("0.5"); + updateDto.newFeeGrowthInside1Last = new BigNumber("1.5"); + updateDto.newTokenOwed0 = new BigNumber("10"); + updateDto.newTokenOwed1 = new BigNumber("20"); + + updateDto.uniqueKey = randomUniqueKey(); + + const signedDto = updateDto.signed(users.testUser1.privateKey); + + // When + const response = await contract.UpdatePositionFeeGrowth(ctx, signedDto); + + // Then + expect(response.Data?.feeGrowthInside0Last.toString()).toBe("0.5"); + expect(response.Data?.feeGrowthInside1Last.toString()).toBe("1.5"); + expect(response.Data?.tokensOwed0.toString()).toBe("10"); + expect(response.Data?.tokensOwed1.toString()).toBe("20"); + }); +}); diff --git a/src/chaincode/dex/updatePositionFeeGrowth.ts b/src/chaincode/dex/updatePositionFeeGrowth.ts new file mode 100644 index 0000000..3da830c --- /dev/null +++ b/src/chaincode/dex/updatePositionFeeGrowth.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) Gala Games Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { GalaChainContext, getObjectByKey, putChainObject } from "@gala-chain/chaincode"; + +import { DexPositionData, UpdatePositionFeeGrowthDto } from "../../api"; + +export async function updatePositionFeeGrowth( + ctx: GalaChainContext, + dto: UpdatePositionFeeGrowthDto +): Promise { + const tickKey = ctx.stub.createCompositeKey(DexPositionData.INDEX_KEY, [ + dto.poolHash, + dto.tickUpper.toString(), + dto.tickLower.toString(), + dto.positionId + ]); + const positionData = await getObjectByKey(ctx, DexPositionData, tickKey); + + positionData.feeGrowthInside0Last = dto.newFeeGrowthInside0Last; + positionData.feeGrowthInside1Last = dto.newFeeGrowthInside1Last; + positionData.tokensOwed0 = dto.newTokenOwed0; + positionData.tokensOwed1 = dto.newTokenOwed1; + + await putChainObject(ctx, positionData); + return positionData; +} diff --git a/src/chaincode/dex/updateTickFeeGrowth.spec.ts b/src/chaincode/dex/updateTickFeeGrowth.spec.ts new file mode 100644 index 0000000..3f29a98 --- /dev/null +++ b/src/chaincode/dex/updateTickFeeGrowth.spec.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) Gala Games Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { randomUniqueKey } from "@gala-chain/api"; +import { fixture, users } from "@gala-chain/test"; +import BigNumber from "bignumber.js"; + +import { TickData, UpdateTickFeeGrowthDto } from "../../api"; +import { DexV3Contract } from "../DexV3Contract"; + +describe("update ticks", () => { + it("should update tick fee growth with provided value", async () => { + // Given + const tick = new TickData("test-pool-hash", 100); + + // Setup the fixture + const { ctx, contract } = fixture(DexV3Contract).registeredUsers(users.testUser1).savedState(tick); + + const updateDto = new UpdateTickFeeGrowthDto(); + updateDto.poolHash = "test-pool-hash"; + updateDto.tickNumber = "100"; + updateDto.newFeeGrowthOutside0 = new BigNumber("0.5"); + updateDto.newFeeGrowthOutside1 = new BigNumber("1.5"); + + updateDto.uniqueKey = randomUniqueKey(); + + const signedDto = updateDto.signed(users.testUser1.privateKey); + + // When + const response = await contract.UpdateTickFeeGrowth(ctx, signedDto); + + // Then + + expect(response.Data?.feeGrowthOutside0.toString()).toBe("0.5"); + expect(response.Data?.feeGrowthOutside1.toString()).toBe("1.5"); + }); +}); diff --git a/src/chaincode/dex/updateTicksFeeGrowth.ts b/src/chaincode/dex/updateTicksFeeGrowth.ts new file mode 100644 index 0000000..93d2669 --- /dev/null +++ b/src/chaincode/dex/updateTicksFeeGrowth.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) Gala Games Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { GalaChainContext, getObjectByKey, putChainObject } from "@gala-chain/chaincode"; + +import { TickData, UpdateTickFeeGrowthDto } from "../../api"; + +export async function updateTickFeeGrowth( + ctx: GalaChainContext, + dto: UpdateTickFeeGrowthDto +): Promise { + const tickKey = ctx.stub.createCompositeKey(TickData.INDEX_KEY, [dto.poolHash, dto.tickNumber]); + const tickData = await getObjectByKey(ctx, TickData, tickKey); + + tickData.feeGrowthOutside0 = dto.newFeeGrowthOutside0; + tickData.feeGrowthOutside1 = dto.newFeeGrowthOutside1; + + await putChainObject(ctx, tickData); + return tickData; +}