From 2704ab1f185bfe98d3253df01bcf8be3c8132d00 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 2 Oct 2023 13:18:05 +0300 Subject: [PATCH 001/313] MEX-372: add user total farm position from farm contract Signed-off-by: Claudiu Lataretu --- .../v2/farm-with-locked-rewards.abi.json | 123 +++++++++++++----- .../farm/v2/services/farm.v2.abi.service.ts | 28 ++++ src/modules/farm/v2/services/interfaces.ts | 9 +- 3 files changed, 128 insertions(+), 32 deletions(-) diff --git a/src/abis/farms/v2/farm-with-locked-rewards.abi.json b/src/abis/farms/v2/farm-with-locked-rewards.abi.json index 460c564b3..6ec271006 100644 --- a/src/abis/farms/v2/farm-with-locked-rewards.abi.json +++ b/src/abis/farms/v2/farm-with-locked-rewards.abi.json @@ -1,20 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.71.0-nightly", - "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de", - "commitDate": "2023-05-25", + "version": "1.73.0-nightly", + "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", + "commitDate": "2023-07-15", "channel": "Nightly", - "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)" + "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" }, "contractCrate": { "name": "farm-with-locked-rewards", "version": "0.0.0", - "gitVersion": "v1.6.0-1401-g81fa7ee8" + "gitVersion": "v1.6.0-1513-g4dc02033" }, "framework": { "name": "multiversx-sc", - "version": "0.39.4" + "version": "0.43.3" } }, "name": "Farm", @@ -100,10 +100,6 @@ "*" ], "inputs": [ - { - "name": "exit_amount", - "type": "BigUint" - }, { "name": "opt_orig_caller", "type": "optional
", @@ -114,46 +110,39 @@ { "type": "EsdtTokenPayment" }, - { - "type": "EsdtTokenPayment" - }, { "type": "EsdtTokenPayment" } ] }, { - "name": "calculateRewardsForGivenPosition", - "mutability": "readonly", + "name": "mergeFarmTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], "inputs": [ { - "name": "user", - "type": "Address" - }, - { - "name": "farm_token_amount", - "type": "BigUint" - }, - { - "name": "attributes", - "type": "FarmTokenAttributes" + "name": "opt_orig_caller", + "type": "optional
", + "multi_arg": true } ], "outputs": [ { - "type": "BigUint" + "type": "EsdtTokenPayment" + }, + { + "type": "EsdtTokenPayment" } ] }, { - "name": "mergeFarmTokens", + "name": "claimBoostedRewards", "mutability": "mutable", - "payableInTokens": [ - "*" - ], "inputs": [ { - "name": "opt_orig_caller", + "name": "opt_user", "type": "optional
", "multi_arg": true } @@ -187,6 +176,29 @@ ], "outputs": [] }, + { + "name": "calculateRewardsForGivenPosition", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + }, + { + "name": "farm_token_amount", + "type": "BigUint" + }, + { + "name": "attributes", + "type": "FarmTokenAttributes" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, { "name": "getRewardPerShare", "mutability": "readonly", @@ -207,6 +219,17 @@ } ] }, + { + "name": "allowExternalClaimBoostedRewards", + "mutability": "mutable", + "inputs": [ + { + "name": "allow_external_claim", + "type": "bool" + } + ], + "outputs": [] + }, { "name": "getFarmingTokenId", "mutability": "readonly", @@ -257,6 +280,31 @@ } ] }, + { + "name": "getUserTotalFarmPosition", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "UserTotalFarmPosition" + } + ] + }, + { + "name": "getFarmPositionMigrationNonce", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, { "name": "setLockingScAddress", "onlyOwner": true, @@ -1294,6 +1342,19 @@ "discriminant": 2 } ] + }, + "UserTotalFarmPosition": { + "type": "struct", + "fields": [ + { + "name": "total_farm_position", + "type": "BigUint" + }, + { + "name": "allow_external_claim_boosted_rewards", + "type": "bool" + } + ] } } } diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index af3930d96..c7e54ce0c 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -382,4 +382,32 @@ export class FarmAbiServiceV2 const response = await this.getGenericData(interaction); return response.firstValue.valueOf().toFixed(); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async userTotalFarmPosition( + farmAddress: string, + userAddress: string, + ): Promise { + return await this.getUserTotalFarmPositionRaw(farmAddress, userAddress); + } + + async getUserTotalFarmPositionRaw( + farmAddress: string, + userAddress: string, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(farmAddress); + const interaction: Interaction = + contract.methodsExplicit.getUserTotalFarmPosition([ + new AddressValue(Address.fromString(userAddress)), + ]); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toFixed(); + } } diff --git a/src/modules/farm/v2/services/interfaces.ts b/src/modules/farm/v2/services/interfaces.ts index 5dff92749..8c9af6cff 100644 --- a/src/modules/farm/v2/services/interfaces.ts +++ b/src/modules/farm/v2/services/interfaces.ts @@ -1,5 +1,8 @@ import { TokenDistributionModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; -import { IFarmAbiService, IFarmComputeService } from '../../base-module/services/interfaces'; +import { + IFarmAbiService, + IFarmComputeService, +} from '../../base-module/services/interfaces'; import { BoostedYieldsFactors } from '../../models/farm.v2.model'; import { EsdtTokenPayment } from '../../../../models/esdtTokenPayment.model'; @@ -18,6 +21,10 @@ export interface IFarmAbiServiceV2 extends IFarmAbiService { boostedYieldsFactors(farmAddress: string): Promise; accumulatedRewardsForWeek(scAddress: string, week: number): Promise; energyFactoryAddress(farmAddress: string): Promise; + userTotalFarmPosition( + farmAddress: string, + userAddress: string, + ): Promise; } export interface IFarmComputeServiceV2 extends IFarmComputeService { From 2aa9ec7c5165082307fe0685e2eab42b89200df3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 3 Oct 2023 15:18:49 +0300 Subject: [PATCH 002/313] MEX-372: use farm events to update user total farm position Signed-off-by: Claudiu Lataretu --- .../v2/services/farm.v2.setter.service.ts | 13 ++++ .../rabbitmq/handlers/farm.handler.service.ts | 64 +++++++++++++++---- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.setter.service.ts b/src/modules/farm/v2/services/farm.v2.setter.service.ts index 179e655fb..2db2a9b39 100644 --- a/src/modules/farm/v2/services/farm.v2.setter.service.ts +++ b/src/modules/farm/v2/services/farm.v2.setter.service.ts @@ -33,4 +33,17 @@ export class FarmSetterServiceV2 extends FarmSetterService { CacheTtlInfo.ContractBalance.localTtl, ); } + + async setUserTotalFarmPosition( + farmAddress: string, + userAddress: string, + value: string, + ): Promise { + return this.setData( + this.getCacheKey('userTotalFarmPosition', farmAddress, userAddress), + value, + CacheTtlInfo.ContractInfo.remoteTtl, + CacheTtlInfo.ContractInfo.localTtl, + ); + } } diff --git a/src/modules/rabbitmq/handlers/farm.handler.service.ts b/src/modules/rabbitmq/handlers/farm.handler.service.ts index ca9747e82..1791a31d3 100644 --- a/src/modules/rabbitmq/handlers/farm.handler.service.ts +++ b/src/modules/rabbitmq/handlers/farm.handler.service.ts @@ -22,12 +22,16 @@ import { Logger } from 'winston'; import { FarmVersion } from '../../farm/models/farm.model'; import { FarmSetterFactory } from '../../farm/farm.setter.factory'; import { FarmAbiFactory } from '../../farm/farm.abi.factory'; +import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; +import { FarmSetterServiceV2 } from 'src/modules/farm/v2/services/farm.v2.setter.service'; @Injectable() export class FarmHandlerService { constructor( private readonly farmAbiFactory: FarmAbiFactory, + private readonly farmAbiV2: FarmAbiServiceV2, private readonly farmSetterFactory: FarmSetterFactory, + private readonly farmSetterV2: FarmSetterServiceV2, @Inject(PUB_SUB) private pubSub: RedisPubSub, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} @@ -134,26 +138,62 @@ export class FarmHandlerService { private async handleEnterFarmEventV2( event: EnterFarmEventV2, ): Promise { - const cacheKey = await this.farmSetterFactory - .useSetter(event.address) - .setFarmTokenSupply(event.address, event.farmSupply.toFixed()); - await this.deleteCacheKeys([cacheKey]); + const userTotalFarmPosition = + await this.farmAbiV2.getUserTotalFarmPositionRaw( + event.address, + event.decodedTopics.caller.bech32(), + ); + const cacheKeys = await Promise.all([ + this.farmSetterFactory + .useSetter(event.address) + .setFarmTokenSupply(event.address, event.farmSupply.toFixed()), + this.farmSetterV2.setUserTotalFarmPosition( + event.address, + event.decodedTopics.caller.bech32(), + userTotalFarmPosition, + ), + ]); + await this.deleteCacheKeys(cacheKeys); } private async handleExitFarmEventV2(event: ExitFarmEventV2): Promise { - const cacheKey = await this.farmSetterFactory - .useSetter(event.address) - .setFarmTokenSupply(event.address, event.farmSupply.toFixed()); - await this.deleteCacheKeys([cacheKey]); + const userTotalFarmPosition = + await this.farmAbiV2.getUserTotalFarmPositionRaw( + event.address, + event.decodedTopics.caller.bech32(), + ); + const cacheKeys = await Promise.all([ + this.farmSetterFactory + .useSetter(event.address) + .setFarmTokenSupply(event.address, event.farmSupply.toFixed()), + this.farmSetterV2.setUserTotalFarmPosition( + event.address, + event.decodedTopics.caller.bech32(), + userTotalFarmPosition, + ), + ]); + await this.deleteCacheKeys(cacheKeys); } private async handleClaimRewardsEventV2( event: ClaimRewardsEventV2, ): Promise { - const cacheKey = await this.farmSetterFactory - .useSetter(event.address) - .setFarmTokenSupply(event.address, event.farmSupply.toFixed()); - await this.deleteCacheKeys([cacheKey]); + const userTotalFarmPosition = + await this.farmAbiV2.getUserTotalFarmPositionRaw( + event.address, + event.decodedTopics.caller.bech32(), + ); + const cacheKeys = await Promise.all([ + this.farmSetterFactory + .useSetter(event.address) + .setFarmTokenSupply(event.address, event.farmSupply.toFixed()), + this.farmSetterV2.setUserTotalFarmPosition( + event.address, + event.decodedTopics.caller.bech32(), + userTotalFarmPosition, + ), + ]); + await this.deleteCacheKeys(cacheKeys); } private async deleteCacheKeys(invalidatedKeys: string[]) { From 3ec30c55aaadc8cbf701f2401b59775db397660e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 3 Oct 2023 16:13:45 +0300 Subject: [PATCH 003/313] MEX-372: add missing import for rabbitmq module Signed-off-by: Claudiu Lataretu --- src/modules/rabbitmq/rabbitmq.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/rabbitmq/rabbitmq.module.ts b/src/modules/rabbitmq/rabbitmq.module.ts index e7970d10e..7151a4b1b 100644 --- a/src/modules/rabbitmq/rabbitmq.module.ts +++ b/src/modules/rabbitmq/rabbitmq.module.ts @@ -36,6 +36,7 @@ import { EscrowHandlerService } from './handlers/escrow.handler.service'; import { EscrowModule } from '../escrow/escrow.module'; import { GovernanceHandlerService } from './handlers/governance.handler.service'; import { GovernanceModule } from '../governance/governance.module'; +import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; @Module({ imports: [ @@ -47,6 +48,7 @@ import { GovernanceModule } from '../governance/governance.module'; FarmModule, FarmModuleV1_2, FarmModuleV1_3, + FarmModuleV2, RouterModule, MetabondingModule, PriceDiscoveryModule, From b785824385dc887c6e99e366c7248d582613fd12 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 3 Oct 2023 16:14:16 +0300 Subject: [PATCH 004/313] MEX-372: add default return for user total farm position Signed-off-by: Claudiu Lataretu --- src/modules/farm/mocks/farm.v2.abi.service.mock.ts | 13 +++++++++++-- src/modules/farm/v2/services/farm.v2.abi.service.ts | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts index c4b3e89ff..d0e73969c 100644 --- a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts +++ b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts @@ -22,7 +22,10 @@ export class FarmAbiServiceMockV2 return '1000'; } - accumulatedRewardsForWeek(scAddress: string, week: number): Promise { + accumulatedRewardsForWeek( + scAddress: string, + week: number, + ): Promise { throw new Error('Method not implemented.'); } @@ -45,8 +48,14 @@ export class FarmAbiServiceMockV2 lockingScAddress(farmAddress: string): Promise { throw new Error('Method not implemented.'); } -} + userTotalFarmPosition( + farmAddress: string, + userAddress: string, + ): Promise { + throw new Error('Method not implemented.'); + } +} export const FarmAbiServiceProviderV2 = { provide: FarmAbiServiceV2, diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index c7e54ce0c..2a8af0016 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -7,6 +7,7 @@ import { Field, FieldDefinition, Interaction, + ReturnCode, Struct, StructType, U32Value, @@ -408,6 +409,11 @@ export class FarmAbiServiceV2 new AddressValue(Address.fromString(userAddress)), ]); const response = await this.getGenericData(interaction); + + if (response.returnCode.equals(ReturnCode.FunctionNotFound)) { + return '0'; + } + return response.firstValue.valueOf().toFixed(); } } From 4dffeaec52727127e8d439bd90146ee4a32f4db2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 3 Oct 2023 16:15:01 +0300 Subject: [PATCH 005/313] MEX-372: add query for user total farm position Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.resolver.ts | 28 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 2850404ed..9f7be3950 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -1,18 +1,18 @@ -import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'; import { BoostedYieldsFactors, FarmModelV2 } from '../models/farm.v2.model'; import { FarmResolver } from '../base-module/farm.resolver'; import { FarmServiceV2 } from './services/farm.v2.service'; -import { - GlobalInfoByWeekModel, -} from '../../../submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; +import { GlobalInfoByWeekModel } from '../../../submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { WeekTimekeepingModel } from '../../../submodules/week-timekeeping/models/week-timekeeping.model'; import { FarmComputeServiceV2 } from './services/farm.v2.compute.service'; import { constantsConfig } from '../../../config'; import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; -import { - WeeklyRewardsSplittingAbiService, -} from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; +import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { FarmAbiServiceV2 } from './services/farm.v2.abi.service'; +import { UseGuards } from '@nestjs/common'; +import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard'; +import { UserAuthResult } from 'src/modules/auth/user.auth.result'; +import { AuthUser } from 'src/modules/auth/auth.user'; @Resolver(() => FarmModelV2) export class FarmResolverV2 extends FarmResolver { @@ -118,7 +118,10 @@ export class FarmResolverV2 extends FarmResolver { const currentWeek = await this.weekTimekeepingAbi.currentWeek( parent.address, ); - return this.farmCompute.undistributedBoostedRewards(parent.address, currentWeek); + return this.farmCompute.undistributedBoostedRewards( + parent.address, + currentWeek, + ); } @ResolveField() @@ -139,4 +142,13 @@ export class FarmResolverV2 extends FarmResolver { async energyFactoryAddress(@Parent() parent: FarmModelV2): Promise { return this.farmAbi.energyFactoryAddress(parent.address); } + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => String) + async userTotalFarmPosition( + @Args('farmAddress') farmAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.farmAbi.userTotalFarmPosition(farmAddress, user.address); + } } From 4537c218436a108a6f4fd5e7477652397bfce861 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:07:32 +0300 Subject: [PATCH 006/313] MEX-376: add method to get farm position migration nonce Signed-off-by: Claudiu Lataretu --- .../farm/mocks/farm.v2.abi.service.mock.ts | 4 ++++ .../farm/v2/services/farm.v2.abi.service.ts | 22 +++++++++++++++++++ src/modules/farm/v2/services/interfaces.ts | 1 + 3 files changed, 27 insertions(+) diff --git a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts index d0e73969c..848590e74 100644 --- a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts +++ b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts @@ -55,6 +55,10 @@ export class FarmAbiServiceMockV2 ): Promise { throw new Error('Method not implemented.'); } + + async farmPositionMigrationNonce(farmAddress: string): Promise { + return 10; + } } export const FarmAbiServiceProviderV2 = { diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index 2a8af0016..255bffefe 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -416,4 +416,26 @@ export class FarmAbiServiceV2 return response.firstValue.valueOf().toFixed(); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async farmPositionMigrationNonce(farmAddress: string): Promise { + return this.getFarmPositionMigrationNonceRaw(farmAddress); + } + + async getFarmPositionMigrationNonceRaw( + farmAddress: string, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(farmAddress); + const interaction: Interaction = + contract.methodsExplicit.getFarmPositionMigrationNonce(); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toNumber(); + } } diff --git a/src/modules/farm/v2/services/interfaces.ts b/src/modules/farm/v2/services/interfaces.ts index 8c9af6cff..bc49dabc2 100644 --- a/src/modules/farm/v2/services/interfaces.ts +++ b/src/modules/farm/v2/services/interfaces.ts @@ -25,6 +25,7 @@ export interface IFarmAbiServiceV2 extends IFarmAbiService { farmAddress: string, userAddress: string, ): Promise; + farmPositionMigrationNonce(farmAddress: string): Promise; } export interface IFarmComputeServiceV2 extends IFarmComputeService { From 5833375fcfc80fd31087e90cf08f5b94b6a708cb Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:09:34 +0300 Subject: [PATCH 007/313] MEX-376: add method to generate migration transactions to initialize user total farm position Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.module.ts | 2 + .../farm/v2/farm.v2.transaction.resolver.ts | 24 +++++++++++ .../services/farm.v2.transaction.service.ts | 42 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 src/modules/farm/v2/farm.v2.transaction.resolver.ts diff --git a/src/modules/farm/v2/farm.v2.module.ts b/src/modules/farm/v2/farm.v2.module.ts index a52e86abd..783feaa30 100644 --- a/src/modules/farm/v2/farm.v2.module.ts +++ b/src/modules/farm/v2/farm.v2.module.ts @@ -12,6 +12,7 @@ import { FarmSetterServiceV2 } from './services/farm.v2.setter.service'; import { WeekTimekeepingModule } from '../../../submodules/week-timekeeping/week-timekeeping.module'; import { WeeklyRewardsSplittingModule } from '../../../submodules/weekly-rewards-splitting/weekly-rewards-splitting.module'; import { EnergyModule } from '../../energy/energy.module'; +import { FarmTransactionResolverV2 } from './farm.v2.transaction.resolver'; @Module({ imports: [ @@ -30,6 +31,7 @@ import { EnergyModule } from '../../energy/energy.module'; FarmComputeServiceV2, FarmTransactionServiceV2, FarmResolverV2, + FarmTransactionResolverV2, ], exports: [ FarmServiceV2, diff --git a/src/modules/farm/v2/farm.v2.transaction.resolver.ts b/src/modules/farm/v2/farm.v2.transaction.resolver.ts new file mode 100644 index 000000000..9cb5d1fb5 --- /dev/null +++ b/src/modules/farm/v2/farm.v2.transaction.resolver.ts @@ -0,0 +1,24 @@ +import { Args, Query, Resolver } from '@nestjs/graphql'; +import { FarmTransactionServiceV2 } from './services/farm.v2.transaction.service'; +import { UseGuards } from '@nestjs/common'; +import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard'; +import { TransactionModel } from 'src/models/transaction.model'; +import { AuthUser } from 'src/modules/auth/auth.user'; +import { UserAuthResult } from 'src/modules/auth/user.auth.result'; + +@Resolver() +export class FarmTransactionResolverV2 { + constructor(private readonly farmTransaction: FarmTransactionServiceV2) {} + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => [TransactionModel]) + async migrateTotalFarmPositions( + @Args('farmAddress') farmAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.farmTransaction.migrateTotalFarmPosition( + farmAddress, + user.address, + ); + } +} diff --git a/src/modules/farm/v2/services/farm.v2.transaction.service.ts b/src/modules/farm/v2/services/farm.v2.transaction.service.ts index 527699290..7a879affd 100644 --- a/src/modules/farm/v2/services/farm.v2.transaction.service.ts +++ b/src/modules/farm/v2/services/farm.v2.transaction.service.ts @@ -17,6 +17,7 @@ import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.s import { FarmAbiServiceV2 } from './farm.v2.abi.service'; import { PairService } from 'src/modules/pair/services/pair.service'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; @Injectable() export class FarmTransactionServiceV2 extends TransactionsFarmService { @@ -25,6 +26,7 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { protected readonly farmAbi: FarmAbiServiceV2, protected readonly pairService: PairService, protected readonly pairAbi: PairAbiService, + private readonly mxApi: MXApiService, ) { super(mxProxy, farmAbi, pairService, pairAbi); } @@ -136,4 +138,44 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { ): Promise { throw new Error('Method not implemented.'); } + + async migrateTotalFarmPosition( + farmAddress: string, + userAddress: string, + ): Promise { + const [farmTokenID, migrationNonce, userNftsCount] = await Promise.all([ + this.farmAbi.farmTokenID(farmAddress), + this.farmAbi.farmPositionMigrationNonce(farmAddress), + this.mxApi.getNftsCountForUser(userAddress), + ]); + + if (userNftsCount === 0) { + return []; + } + + const userNfts = await this.mxApi.getNftsForUser( + userAddress, + 0, + userNftsCount, + 'MetaESDT', + [farmTokenID], + ); + + const promises: Promise[] = []; + userNfts.forEach((nft) => { + if (nft.nonce < migrationNonce) { + promises.push( + this.claimRewards(userAddress, { + farmAddress, + farmTokenID: nft.collection, + farmTokenNonce: nft.nonce, + amount: nft.balance, + lockRewards: true, + }), + ); + } + }); + + return Promise.all(promises); + } } From 8efd440be2a31f0bc247ef9622254452ac0c20bb Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:10:56 +0300 Subject: [PATCH 008/313] MEX-376: proxy dex generate user total farm position migration transactions Signed-off-by: Claudiu Lataretu --- .../proxy/proxy.transaction.resolver.ts | 12 ++++ .../services/proxy-farm/proxy.farm.module.ts | 2 + .../proxy.farm.transactions.service.ts | 66 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/modules/proxy/proxy.transaction.resolver.ts b/src/modules/proxy/proxy.transaction.resolver.ts index 7bca6af60..f64a67662 100644 --- a/src/modules/proxy/proxy.transaction.resolver.ts +++ b/src/modules/proxy/proxy.transaction.resolver.ts @@ -195,4 +195,16 @@ export class ProxyTransactionResolver { args, ); } + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => [TransactionModel]) + async migrateTotalFarmPositionsProxy( + @Args('proxyAddress') proxyAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.transactionsProxyFarmService.migrateTotalFarmPosition( + user.address, + proxyAddress, + ); + } } diff --git a/src/modules/proxy/services/proxy-farm/proxy.farm.module.ts b/src/modules/proxy/services/proxy-farm/proxy.farm.module.ts index 68363eddb..48c7fea04 100644 --- a/src/modules/proxy/services/proxy-farm/proxy.farm.module.ts +++ b/src/modules/proxy/services/proxy-farm/proxy.farm.module.ts @@ -8,6 +8,7 @@ import { ProxyModule } from '../../proxy.module'; import { PairModule } from 'src/modules/pair/pair.module'; import { TokenModule } from 'src/modules/tokens/token.module'; import { FarmModule } from 'src/modules/farm/farm.module'; +import { FarmModuleV2 } from 'src/modules/farm/v2/farm.v2.module'; @Module({ imports: [ @@ -16,6 +17,7 @@ import { FarmModule } from 'src/modules/farm/farm.module'; ProxyPairModule, forwardRef(() => ProxyModule), FarmModule, + FarmModuleV2, PairModule, TokenModule, ], diff --git a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts index 4165f8f4f..a69e78324 100644 --- a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts +++ b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts @@ -26,14 +26,24 @@ import { PairService } from 'src/modules/pair/services/pair.service'; import { proxyVersion } from 'src/utils/proxy.utils'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { FarmAbiFactory } from 'src/modules/farm/farm.abi.factory'; +import { ProxyFarmAbiService } from './proxy.farm.abi.service'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { + WrappedFarmTokenAttributes, + WrappedFarmTokenAttributesV2, +} from '@multiversx/sdk-exchange'; +import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; @Injectable() export class ProxyFarmTransactionsService { constructor( private readonly mxProxy: MXProxyService, + private readonly mxApi: MXApiService, private readonly farmAbi: FarmAbiFactory, + private readonly farmAbiV2: FarmAbiServiceV2, private readonly pairService: PairService, private readonly pairAbi: PairAbiService, + private readonly proxyFarmAbi: ProxyFarmAbiService, ) {} async enterFarmProxy( @@ -264,6 +274,62 @@ export class ProxyFarmTransactionsService { .toPlainObject(); } + async migrateTotalFarmPosition( + sender: string, + proxyAddress: string, + ): Promise { + const wrappedFarmTokenID = await this.proxyFarmAbi.wrappedFarmTokenID( + proxyAddress, + ); + const userNftsCount = await this.mxApi.getNftsCountForUser(sender); + const userNfts = await this.mxApi.getNftsForUser( + sender, + 0, + userNftsCount, + 'MetaESDT', + [wrappedFarmTokenID], + ); + const promises: Promise[] = []; + for (const nft of userNfts) { + const version = proxyVersion(proxyAddress); + + let farmTokenID: string; + let farmTokenNonce: number; + if (version === 'v2') { + const decodedAttributes = + WrappedFarmTokenAttributesV2.fromAttributes(nft.attributes); + farmTokenID = decodedAttributes.farmToken.tokenIdentifier; + farmTokenNonce = decodedAttributes.farmToken.tokenNonce; + } else { + const decodedAttributes = + WrappedFarmTokenAttributes.fromAttributes(nft.attributes); + farmTokenID = decodedAttributes.farmTokenID; + farmTokenNonce = decodedAttributes.farmTokenNonce; + } + + const farmAddress = await this.farmAbi.getFarmAddressByFarmTokenID( + farmTokenID, + ); + + const migrationNonce = + await this.farmAbiV2.farmPositionMigrationNonce(farmAddress); + + if (farmTokenNonce < migrationNonce) { + promises.push( + this.claimFarmRewardsProxy(sender, proxyAddress, { + farmAddress, + wrappedFarmTokenID: nft.collection, + wrappedFarmTokenNonce: nft.nonce, + amount: nft.balance, + lockRewards: true, + }), + ); + } + } + + return Promise.all(promises); + } + private async getExitFarmProxyGasLimit( args: ExitFarmProxyArgs, ): Promise { From 8eed3adbeb626649fa0c28aede8cdd11b9acfc5d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:12:25 +0300 Subject: [PATCH 009/313] MEX-376: unit tests for farm v2 transactions service - add unit tests for generate migration transactions for user total farm position Signed-off-by: Claudiu Lataretu --- src/config/test.json | 8 +- src/modules/farm/mocks/farm.constants.ts | 12 ++ .../farm.v2.transactions.service.spec.ts | 127 ++++++++++++++++++ .../mx.api.service.mock.ts | 4 + 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/modules/farm/specs/farm.v2.transactions.service.spec.ts diff --git a/src/config/test.json b/src/config/test.json index 066923fe8..e02ba68d1 100644 --- a/src/config/test.json +++ b/src/config/test.json @@ -4,7 +4,8 @@ "MEX-123456": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfqktvqkr", "WEGLD_USDC": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfsr2ycrs", "proxyDexAddress": { - "v1": "erd1qqqqqqqqqqqqqpgqrc4pg2xarca9z34njcxeur622qmfjp8w2jps89fxnl" + "v1": "erd1qqqqqqqqqqqqqpgqrc4pg2xarca9z34njcxeur622qmfjp8w2jps89fxnl", + "v2": "erd1qqqqqqqqqqqqqpgqt6ltx52ukss9d2qag2k67at28a36xc9lkp2sr06394" } }, "farms": { @@ -17,6 +18,11 @@ "customRewards": [ "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqes9lzxht" ] + }, + "v2": { + "lockedRewards": [ + "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqsdtp6mh" + ] } }, "tokenProviderUSD": "WEGLD-123456", diff --git a/src/modules/farm/mocks/farm.constants.ts b/src/modules/farm/mocks/farm.constants.ts index 0c00a43b9..e54a42958 100644 --- a/src/modules/farm/mocks/farm.constants.ts +++ b/src/modules/farm/mocks/farm.constants.ts @@ -49,4 +49,16 @@ export const farms = [ rewardsPerBlock: '2000000000000000000', rewardPerShare: '0', }, + { + address: Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000041', + ).bech32(), + farmedTokenID: 'MEX-123456', + farmTokenID: 'EGLDMEXFL-ghijkl', + farmingTokenID: 'EGLDMEXLP-abcdef', + farmTotalSupply: '1000000000000000000', + farmingTokenReserve: '1000000000000000000', + rewardsPerBlock: '2000000000000000000', + rewardPerShare: '0', + }, ]; diff --git a/src/modules/farm/specs/farm.v2.transactions.service.spec.ts b/src/modules/farm/specs/farm.v2.transactions.service.spec.ts new file mode 100644 index 000000000..bc6266891 --- /dev/null +++ b/src/modules/farm/specs/farm.v2.transactions.service.spec.ts @@ -0,0 +1,127 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { FarmTransactionServiceV2 } from '../v2/services/farm.v2.transaction.service'; +import { ConfigModule } from '@nestjs/config'; +import { WinstonModule } from 'nest-winston'; +import winston from 'winston'; +import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { MXProxyServiceProvider } from 'src/services/multiversx-communication/mx.proxy.service.mock'; +import { FarmAbiServiceProviderV2 } from '../mocks/farm.v2.abi.service.mock'; +import { PairService } from 'src/modules/pair/services/pair.service'; +import { PairAbiServiceProvider } from 'src/modules/pair/mocks/pair.abi.service.mock'; +import { PairComputeServiceProvider } from 'src/modules/pair/mocks/pair.compute.service.mock'; +import { RouterAbiServiceProvider } from 'src/modules/router/mocks/router.abi.service.mock'; +import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; +import { WrapAbiServiceProvider } from 'src/modules/wrapping/mocks/wrap.abi.service.mock'; +import { ApiConfigService } from 'src/helpers/api.config.service'; +import { TokenServiceProvider } from 'src/modules/tokens/mocks/token.service.mock'; +import { ContextGetterServiceProvider } from 'src/services/context/mocks/context.getter.service.mock'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { Address } from '@multiversx/sdk-core/out'; +import { encodeTransactionData } from 'src/helpers/helpers'; + +describe('FarmTransactionsServiceV2', () => { + let module: TestingModule; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({}), + WinstonModule.forRoot({ + transports: [new winston.transports.Console({})], + }), + DynamicModuleUtils.getCacheModule(), + ], + providers: [ + ApiConfigService, + MXProxyServiceProvider, + MXApiServiceProvider, + FarmAbiServiceProviderV2, + PairService, + PairAbiServiceProvider, + PairComputeServiceProvider, + RouterAbiServiceProvider, + WrapAbiServiceProvider, + TokenServiceProvider, + ContextGetterServiceProvider, + FarmTransactionServiceV2, + ], + }).compile(); + }); + + it('should be defined', () => { + const service = module.get( + FarmTransactionServiceV2, + ); + expect(service).toBeDefined(); + }); + + it('should NOT get migrate total farm position transaction', async () => { + const service = module.get( + FarmTransactionServiceV2, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'EGLDMEXFL-ghijkl-0a', + collection: 'EGLDMEXFL-ghijkl', + attributes: + 'AAAACBEpiw/pcSUIAAAAAAAADNIAAAAAAAAABysPQGpo9rtgu2ARp4Hri1PWHXkEdFCtqkaXfiAjkxKVEmLBBX0QkA==', + nonce: 10, + type: 'MetaESDT', + name: 'EGLDMEXLPStakedLK', + creator: + 'erd1qqqqqqqqqqqqqpgqna8cqqzdtdg849hr0pd5cmft8ujaguhv295qgchtqa', + balance: '12120193336145595', + decimals: 18, + ticker: 'EGLDMEXFL-2d2bba', + }, + ]); + + const transactions = await service.migrateTotalFarmPosition( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000041', + ).bech32(), + Address.Zero().bech32(), + ); + + expect(transactions.length).toEqual(0); + }); + + it('should NOT get migrate total farm position transaction', async () => { + const service = module.get( + FarmTransactionServiceV2, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'EGLDMEXFL-ghijkl-01', + collection: 'EGLDMEXFL-ghijkl', + attributes: + 'AAAACBEpiw/pcSUIAAAAAAAADNIAAAAAAAAABysPQGpo9rtgu2ARp4Hri1PWHXkEdFCtqkaXfiAjkxKVEmLBBX0QkA==', + nonce: 1, + type: 'MetaESDT', + name: 'EGLDMEXLPStakedLK', + creator: + 'erd1qqqqqqqqqqqqqpgqna8cqqzdtdg849hr0pd5cmft8ujaguhv295qgchtqa', + balance: '12120193336145595', + decimals: 18, + ticker: 'EGLDMEXFL-2d2bba', + }, + ]); + + const transactions = await service.migrateTotalFarmPosition( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000041', + ).bech32(), + Address.Zero().bech32(), + ); + console.log(transactions); + expect(transactions[0].data).toEqual( + encodeTransactionData( + `ESDTNFTTransfer@EGLDMEXFL-ghijkl@01@12120193336145595@${Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000041', + ).bech32()}@claimRewards`, + ), + ); + }); +}); diff --git a/src/services/multiversx-communication/mx.api.service.mock.ts b/src/services/multiversx-communication/mx.api.service.mock.ts index d7e87c87f..888a003bb 100644 --- a/src/services/multiversx-communication/mx.api.service.mock.ts +++ b/src/services/multiversx-communication/mx.api.service.mock.ts @@ -33,6 +33,10 @@ export class MXApiServiceMock { ]; } + async getNftsCountForUser(address: string): Promise { + return 1; + } + async getNftsForUser(address: string): Promise { return [ { From 2781ffdce99ac3b54caceb513a031cd0a05557bc Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:13:51 +0300 Subject: [PATCH 010/313] MEX-376: unit tests for proxy farm transactions service - add unit tests for generate migration transactions for user total farm positions Signed-off-by: Claudiu Lataretu --- .../proxy/mocks/proxy.abi.service.mock.ts | 6 +- .../proxy-pair-transactions.service.spec.ts | 8 +- .../proxy.farm.transactions.service.spec.ts | 134 ++++++++++++++++++ 3 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/modules/proxy/specs/proxy.farm.transactions.service.spec.ts diff --git a/src/modules/proxy/mocks/proxy.abi.service.mock.ts b/src/modules/proxy/mocks/proxy.abi.service.mock.ts index c54984dd6..8ab85f045 100644 --- a/src/modules/proxy/mocks/proxy.abi.service.mock.ts +++ b/src/modules/proxy/mocks/proxy.abi.service.mock.ts @@ -9,13 +9,13 @@ import { ProxyFarmAbiService } from '../services/proxy-farm/proxy.farm.abi.servi export class ProxyAbiServiceMock implements IProxyAbiService { async lockedAssetTokenID(proxyAddress: string): Promise { - return ['LKMEX-1234']; + return ['LKMEX-123456']; } } export class ProxyPairAbiServiceMock implements IProxyPairAbiService { async wrappedLpTokenID(proxyAddress: string): Promise { - return 'LKLP-abcd'; + return 'LKLP-abcdef'; } intermediatedPairs(proxyAddress: string): Promise { throw new Error('Method not implemented.'); @@ -24,7 +24,7 @@ export class ProxyPairAbiServiceMock implements IProxyPairAbiService { export class ProxyFarmAbiServiceMock implements IProxyFarmAbiService { async wrappedFarmTokenID(proxyAddress: string): Promise { - return 'LKFARM-1234'; + return 'LKFARM-123456'; } intermediatedFarms(proxyAddress: string): Promise { throw new Error('Method not implemented.'); diff --git a/src/modules/proxy/specs/proxy-pair-transactions.service.spec.ts b/src/modules/proxy/specs/proxy-pair-transactions.service.spec.ts index a2423ab50..6286835a6 100644 --- a/src/modules/proxy/specs/proxy-pair-transactions.service.spec.ts +++ b/src/modules/proxy/specs/proxy-pair-transactions.service.spec.ts @@ -102,7 +102,7 @@ describe('TransactionProxyPairService', () => { amount: firstTokenAmount, }, { - tokenID: 'LKMEX-1234', + tokenID: 'LKMEX-123456', nonce: 1, amount: secondTokenAmount, }, @@ -116,7 +116,7 @@ describe('TransactionProxyPairService', () => { expect(wrapEgldTransaction.value).toEqual(firstTokenAmount); expect(addLiquidityProxy.data).toEqual( encodeTransactionData( - 'MultiESDTNFTTransfer@000000000000000005001e2a1428dd1e3a5146b3960d9e0f4a50369904ee5483@02@WEGLD-123456@@10@LKMEX-1234@01@09@addLiquidityProxy@0000000000000000000000000000000000000000000000000000000000000000@09@08', + 'MultiESDTNFTTransfer@000000000000000005001e2a1428dd1e3a5146b3960d9e0f4a50369904ee5483@02@WEGLD-123456@@10@LKMEX-123456@01@09@addLiquidityProxy@0000000000000000000000000000000000000000000000000000000000000000@09@08', ), ); }); @@ -148,7 +148,7 @@ describe('TransactionProxyPairService', () => { pairAddress: Address.Zero().bech32(), tokens: [ { - tokenID: 'LKMEX-1234', + tokenID: 'LKMEX-123456', nonce: 1, amount: firstTokenAmount, }, @@ -167,7 +167,7 @@ describe('TransactionProxyPairService', () => { expect(wrapEgldTransaction.value).toEqual(secondTokenAmount); expect(addLiquidityProxy.data).toEqual( encodeTransactionData( - 'MultiESDTNFTTransfer@000000000000000005001e2a1428dd1e3a5146b3960d9e0f4a50369904ee5483@02@WEGLD-123456@@09@LKMEX-1234@01@10@addLiquidityProxy@0000000000000000000000000000000000000000000000000000000000000000@08@09', + 'MultiESDTNFTTransfer@000000000000000005001e2a1428dd1e3a5146b3960d9e0f4a50369904ee5483@02@WEGLD-123456@@09@LKMEX-123456@01@10@addLiquidityProxy@0000000000000000000000000000000000000000000000000000000000000000@08@09', ), ); }); diff --git a/src/modules/proxy/specs/proxy.farm.transactions.service.spec.ts b/src/modules/proxy/specs/proxy.farm.transactions.service.spec.ts new file mode 100644 index 000000000..107e7f575 --- /dev/null +++ b/src/modules/proxy/specs/proxy.farm.transactions.service.spec.ts @@ -0,0 +1,134 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ProxyFarmTransactionsService } from '../services/proxy-farm/proxy.farm.transactions.service'; +import { WinstonModule } from 'nest-winston'; +import winston from 'winston'; +import { ConfigModule } from '@nestjs/config'; +import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { MXProxyServiceProvider } from 'src/services/multiversx-communication/mx.proxy.service.mock'; +import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; +import { FarmAbiServiceProviderV1_2 } from 'src/modules/farm/mocks/farm.v1.2.abi.service.mock'; +import { FarmAbiServiceProviderV1_3 } from 'src/modules/farm/mocks/farm.v1.3.abi.service.mock'; +import { FarmAbiServiceProviderV2 } from 'src/modules/farm/mocks/farm.v2.abi.service.mock'; +import { PairService } from 'src/modules/pair/services/pair.service'; +import { PairAbiServiceProvider } from 'src/modules/pair/mocks/pair.abi.service.mock'; +import { ProxyFarmAbiServiceProvider } from '../mocks/proxy.abi.service.mock'; +import { PairComputeServiceProvider } from 'src/modules/pair/mocks/pair.compute.service.mock'; +import { RouterAbiServiceProvider } from 'src/modules/router/mocks/router.abi.service.mock'; +import { WrapAbiServiceProvider } from 'src/modules/wrapping/mocks/wrap.abi.service.mock'; +import { TokenServiceProvider } from 'src/modules/tokens/mocks/token.service.mock'; +import { ContextGetterServiceProvider } from 'src/services/context/mocks/context.getter.service.mock'; +import { FarmAbiFactory } from 'src/modules/farm/farm.abi.factory'; +import { ApiConfigService } from 'src/helpers/api.config.service'; +import { Address } from '@multiversx/sdk-core/out'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { encodeTransactionData } from 'src/helpers/helpers'; + +describe('ProxyFarmTransactionsService', () => { + let module: TestingModule; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [ + WinstonModule.forRoot({ + transports: [new winston.transports.Console({})], + }), + ConfigModule.forRoot({}), + DynamicModuleUtils.getCacheModule(), + ], + providers: [ + ProxyFarmTransactionsService, + MXProxyServiceProvider, + MXApiServiceProvider, + FarmAbiFactory, + FarmAbiServiceProviderV1_2, + FarmAbiServiceProviderV1_3, + FarmAbiServiceProviderV2, + PairService, + PairAbiServiceProvider, + PairComputeServiceProvider, + RouterAbiServiceProvider, + WrapAbiServiceProvider, + TokenServiceProvider, + ContextGetterServiceProvider, + ApiConfigService, + ProxyFarmAbiServiceProvider, + ], + }).compile(); + }); + + it('should be defined', () => { + const service: ProxyFarmTransactionsService = + module.get( + ProxyFarmTransactionsService, + ); + expect(service).toBeDefined(); + }); + + it('should NOT get migrate to total farm position transaction', async () => { + const service: ProxyFarmTransactionsService = + module.get( + ProxyFarmTransactionsService, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsCountForUser').mockResolvedValue(1); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'LKFARM-123456-0a', + collection: 'LKFARM-123456', + attributes: + 'AAAAEEVHTERNRVhGTC1naGlqa2wAAAAAAAAACgAAAAcrKmP2fki2AAAAC0xLTFAtYWJjZGVmAAAAAAAAAAEAAAAHKypj9n5Itg==', + nonce: 10, + type: 'MetaESDT', + name: 'xMEXLPStaked', + creator: + 'erd1qqqqqqqqqqqqqpgqat0auzsgk9x0g9gm8n6tq6qa9xtmwu4h295qaalzvq', + balance: '12150032824158390', + decimals: 18, + ticker: 'LKFARM-123456', + }, + ]); + + const transactions = await service.migrateTotalFarmPosition( + Address.Zero().bech32(), + 'erd1qqqqqqqqqqqqqpgqt6ltx52ukss9d2qag2k67at28a36xc9lkp2sr06394', + ); + + expect(transactions.length).toEqual(0); + }); + + it('should get migrate to total farm position transaction', async () => { + const service: ProxyFarmTransactionsService = + module.get( + ProxyFarmTransactionsService, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsCountForUser').mockResolvedValue(1); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'LKFARM-123456-05', + collection: 'LKFARM-123456', + attributes: + 'AAAAEEVHTERNRVhGTC1naGlqa2wAAAAAAAAAAQAAAAcrKmP2fki2AAAAC0xLTFAtYWJjZGVmAAAAAAAAAAEAAAAHKypj9n5Itg==', + nonce: 5, + type: 'MetaESDT', + name: 'xMEXLPStaked', + creator: + 'erd1qqqqqqqqqqqqqpgqat0auzsgk9x0g9gm8n6tq6qa9xtmwu4h295qaalzvq', + balance: '12150032824158390', + decimals: 18, + ticker: 'LKFARM-123456', + }, + ]); + + const transactions = await service.migrateTotalFarmPosition( + Address.Zero().bech32(), + 'erd1qqqqqqqqqqqqqpgqt6ltx52ukss9d2qag2k67at28a36xc9lkp2sr06394', + ); + + expect(transactions[0].data).toEqual( + encodeTransactionData( + 'ESDTNFTTransfer@LKFARM-123456@05@12150032824158390@erd1qqqqqqqqqqqqqpgqt6ltx52ukss9d2qag2k67at28a36xc9lkp2sr06394@claimRewardsProxy@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqsdtp6mh', + ), + ); + }); +}); From 15cfe94c6b780ca52c436dd702c3fe68d8a8c44b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:25:37 +0300 Subject: [PATCH 011/313] MEX-372: add check for farm address version on userTotalFarmPosition query Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.resolver.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 9f7be3950..3b4c1b383 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -13,6 +13,10 @@ import { UseGuards } from '@nestjs/common'; import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard'; import { UserAuthResult } from 'src/modules/auth/user.auth.result'; import { AuthUser } from 'src/modules/auth/auth.user'; +import { farmVersion } from 'src/utils/farm.utils'; +import { FarmVersion } from '../models/farm.model'; +import { GraphQLError } from 'graphql'; +import { ApolloServerErrorCode } from '@apollo/server/dist/esm/errors'; @Resolver(() => FarmModelV2) export class FarmResolverV2 extends FarmResolver { @@ -144,11 +148,20 @@ export class FarmResolverV2 extends FarmResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => String) + @Query(() => String, { + description: 'Returns the total farm position of the user in the farm', + }) async userTotalFarmPosition( @Args('farmAddress') farmAddress: string, @AuthUser() user: UserAuthResult, ): Promise { + if (farmVersion(farmAddress) !== FarmVersion.V2) { + throw new GraphQLError('Farm version is not supported', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } return this.farmAbi.userTotalFarmPosition(farmAddress, user.address); } } From 40895e6aedbd2b33b4e9d5c59d8f82e743015d17 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 12:32:26 +0300 Subject: [PATCH 012/313] MEX-372: fix imports Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 3b4c1b383..1cfd08b05 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -16,7 +16,7 @@ import { AuthUser } from 'src/modules/auth/auth.user'; import { farmVersion } from 'src/utils/farm.utils'; import { FarmVersion } from '../models/farm.model'; import { GraphQLError } from 'graphql'; -import { ApolloServerErrorCode } from '@apollo/server/dist/esm/errors'; +import { ApolloServerErrorCode } from '@apollo/server/errors'; @Resolver(() => FarmModelV2) export class FarmResolverV2 extends FarmResolver { From 2afe239fb3774a9b41f9b794a8ab0b799d78c719 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 14:14:42 +0300 Subject: [PATCH 013/313] MEX-376: add description for migration query Signed-off-by: Claudiu Lataretu --- .../farm/v2/farm.v2.transaction.resolver.ts | 16 +++++++++++++++- src/modules/proxy/proxy.transaction.resolver.ts | 5 ++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/modules/farm/v2/farm.v2.transaction.resolver.ts b/src/modules/farm/v2/farm.v2.transaction.resolver.ts index 9cb5d1fb5..84f58a55f 100644 --- a/src/modules/farm/v2/farm.v2.transaction.resolver.ts +++ b/src/modules/farm/v2/farm.v2.transaction.resolver.ts @@ -5,17 +5,31 @@ import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard' import { TransactionModel } from 'src/models/transaction.model'; import { AuthUser } from 'src/modules/auth/auth.user'; import { UserAuthResult } from 'src/modules/auth/user.auth.result'; +import { farmVersion } from 'src/utils/farm.utils'; +import { FarmVersion } from '../models/farm.model'; +import { GraphQLError } from 'graphql'; +import { ApolloServerErrorCode } from '@apollo/server/errors'; @Resolver() export class FarmTransactionResolverV2 { constructor(private readonly farmTransaction: FarmTransactionServiceV2) {} @UseGuards(JwtOrNativeAuthGuard) - @Query(() => [TransactionModel]) + @Query(() => [TransactionModel], { + description: + 'Generate transactions to initialize the total farm positions for a user', + }) async migrateTotalFarmPositions( @Args('farmAddress') farmAddress: string, @AuthUser() user: UserAuthResult, ): Promise { + if (farmVersion(farmAddress) !== FarmVersion.V2) { + throw new GraphQLError('Farm version is not supported', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } return this.farmTransaction.migrateTotalFarmPosition( farmAddress, user.address, diff --git a/src/modules/proxy/proxy.transaction.resolver.ts b/src/modules/proxy/proxy.transaction.resolver.ts index f64a67662..6f0fe81af 100644 --- a/src/modules/proxy/proxy.transaction.resolver.ts +++ b/src/modules/proxy/proxy.transaction.resolver.ts @@ -197,7 +197,10 @@ export class ProxyTransactionResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => [TransactionModel]) + @Query(() => [TransactionModel], { + description: + 'Generate transactions to initialize the total farm positions for a user', + }) async migrateTotalFarmPositionsProxy( @Args('proxyAddress') proxyAddress: string, @AuthUser() user: UserAuthResult, From 729cf5a371e1fac191c68df0d7ddb753b214606a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 17:16:57 +0300 Subject: [PATCH 014/313] MEX-376: fix user total farm position query Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/services/farm.v2.abi.service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index 255bffefe..3fd1d9424 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -410,11 +410,14 @@ export class FarmAbiServiceV2 ]); const response = await this.getGenericData(interaction); - if (response.returnCode.equals(ReturnCode.FunctionNotFound)) { + if ( + response.returnCode.equals(ReturnCode.FunctionNotFound) || + response.returnCode.equals(ReturnCode.UserError) + ) { return '0'; } - return response.firstValue.valueOf().toFixed(); + return response.firstValue.valueOf().total_farm_position.toFixed(); } @ErrorLoggerAsync({ From 6cde88d93ffc575fcd8b8d0594c93be524a94fd2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 17:32:54 +0300 Subject: [PATCH 015/313] MEX-376: update analytics unit test Signed-off-by: Claudiu Lataretu --- src/modules/analytics/specs/analytics.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/analytics/specs/analytics.service.spec.ts b/src/modules/analytics/specs/analytics.service.spec.ts index 94274ded5..6490887e2 100644 --- a/src/modules/analytics/specs/analytics.service.spec.ts +++ b/src/modules/analytics/specs/analytics.service.spec.ts @@ -122,6 +122,6 @@ describe('AnalyticsService', () => { const totalLockedValueUSDFarms = await service.computeLockedValueUSDFarms(); - expect(totalLockedValueUSDFarms.toString()).toEqual('90'); + expect(totalLockedValueUSDFarms.toString()).toEqual('110'); }); }); From c17da00569d63d0fe8e1ccd7a90a54e3f309e859 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 4 Oct 2023 17:59:35 +0300 Subject: [PATCH 016/313] MEX-376: fix farm service unit test Signed-off-by: Claudiu Lataretu --- src/modules/farm/specs/farm.service.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/farm/specs/farm.service.spec.ts b/src/modules/farm/specs/farm.service.spec.ts index 86fedd9ec..3cb9cd053 100644 --- a/src/modules/farm/specs/farm.service.spec.ts +++ b/src/modules/farm/specs/farm.service.spec.ts @@ -159,6 +159,12 @@ describe('FarmService', () => { rewardType: 'customRewards', version: 'v1.3', }, + { + address: + 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqsdtp6mh', + rewardType: 'lockedRewards', + version: 'v2', + }, ]); }); From dbcb8341e7a27df0251494416dab31123bc41c0f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 14:56:54 +0300 Subject: [PATCH 017/313] MEX-375: use query method for getAccumulatedRewardsForWeekRaw Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/services/farm.v2.abi.service.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index 3fd1d9424..af70042c5 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -251,15 +251,10 @@ export class FarmAbiServiceV2 scAddress: string, week: number, ): Promise { - const hexValue = await this.gatewayService.getSCStorageKeys(scAddress, [ - 'accumulatedRewardsForWeek', - week, - ]); - return new BigNumber(hexValue, 16).integerValue().toFixed(); // TODO: remove the code above after the contracts are upgraded with the required view const contract = await this.mxProxy.getFarmSmartContract(scAddress); const interaction: Interaction = - contract.methodsExplicit.getAccumulatedFees([ + contract.methodsExplicit.getAccumulatedRewardsForWeek([ new U32Value(new BigNumber(week)), ]); const response = await this.getGenericData(interaction); From bf515547d75af8dd22f94dae7f77b29347f95fb5 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 14:58:50 +0300 Subject: [PATCH 018/313] MEX-375: update compute rewards to use user total farm position Signed-off-by: Claudiu Lataretu --- .../v2/services/farm.v2.compute.service.ts | 50 +++++++------------ .../farm/v2/services/farm.v2.service.ts | 1 - src/modules/farm/v2/services/interfaces.ts | 3 -- .../user/user.info-by-week.resolver.ts | 2 - 4 files changed, 18 insertions(+), 38 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index c6bc9dd24..1a0c3df83 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -140,7 +140,6 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise { return await this.cachingService.getOrSet( `farm.userRewardsDistributionForWeek.${scAddress}.${userAddress}.${week}`, @@ -149,7 +148,6 @@ export class FarmComputeServiceV2 scAddress, userAddress, week, - liquidity, ), CacheTtlInfo.ContractBalance.remoteTtl, CacheTtlInfo.ContractBalance.localTtl, @@ -160,13 +158,11 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise { const userRewardsForWeek = await this.userRewardsForWeek( scAddress, userAddress, week, - liquidity, ); return await this.weeklyRewardsSplittingCompute.computeDistribution( userRewardsForWeek, @@ -180,7 +176,6 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise { return await this.cachingService.getOrSet( `farm.userAccumulatedRewards.${scAddress}.${userAddress}.${week}`, @@ -189,7 +184,6 @@ export class FarmComputeServiceV2 scAddress, userAddress, week, - liquidity, ), CacheTtlInfo.ContractBalance.remoteTtl, CacheTtlInfo.ContractBalance.localTtl, @@ -200,7 +194,6 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise { const [ boostedYieldsFactors, @@ -211,6 +204,7 @@ export class FarmComputeServiceV2 farmTokenSupply, totalEnergy, blocksInWeek, + liquidity, ] = await Promise.all([ this.farmAbi.boostedYieldsFactors(scAddress), this.farmAbi.boostedYieldsRewardsPercenatage(scAddress), @@ -224,6 +218,7 @@ export class FarmComputeServiceV2 this.farmAbi.farmTokenSupply(scAddress), this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), this.computeBlocksInWeek(scAddress, week), + this.farmAbi.userTotalFarmPosition(scAddress, userAddress), ]); const energyAmount = userEnergy.amount; @@ -291,17 +286,10 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise { return await this.cachingService.getOrSet( `farm.userRewardsForWeek.${scAddress}.${userAddress}.${week}`, - () => - this.computeUserRewardsForWeek( - scAddress, - userAddress, - week, - liquidity, - ), + () => this.computeUserRewardsForWeek(scAddress, userAddress, week), CacheTtlInfo.ContractBalance.remoteTtl, CacheTtlInfo.ContractBalance.localTtl, ); @@ -311,25 +299,23 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise { const payments: EsdtTokenPayment[] = []; - const [totalRewardsForWeek, userEnergyForWeek, totalEnergyForWeek] = - await Promise.all([ - this.weeklyRewardsSplittingAbi.totalRewardsForWeek( - scAddress, - week, - ), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( - scAddress, - userAddress, - week, - ), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek( - scAddress, - week, - ), - ]); + const [ + totalRewardsForWeek, + userEnergyForWeek, + totalEnergyForWeek, + liquidity, + ] = await Promise.all([ + this.weeklyRewardsSplittingAbi.totalRewardsForWeek(scAddress, week), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), + this.farmAbi.userTotalFarmPosition(scAddress, userAddress), + ]); const boostedYieldsFactors = await this.farmAbi.boostedYieldsFactors( scAddress, diff --git a/src/modules/farm/v2/services/farm.v2.service.ts b/src/modules/farm/v2/services/farm.v2.service.ts index 11b84fd77..ecc3390c5 100644 --- a/src/modules/farm/v2/services/farm.v2.service.ts +++ b/src/modules/farm/v2/services/farm.v2.service.ts @@ -129,7 +129,6 @@ export class FarmServiceV2 extends FarmServiceBase { positon.farmAddress, positon.user, currentWeek, - positon.liquidity, ); } diff --git a/src/modules/farm/v2/services/interfaces.ts b/src/modules/farm/v2/services/interfaces.ts index bc49dabc2..d7c253e63 100644 --- a/src/modules/farm/v2/services/interfaces.ts +++ b/src/modules/farm/v2/services/interfaces.ts @@ -34,19 +34,16 @@ export interface IFarmComputeServiceV2 extends IFarmComputeService { scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise; userAccumulatedRewards( scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise; userRewardsForWeek( scAddress: string, userAddress: string, week: number, - liquidity: string, ): Promise; optimalEnergyPerLP(scAddress: string, week: number): Promise; } diff --git a/src/modules/user/user.info-by-week.resolver.ts b/src/modules/user/user.info-by-week.resolver.ts index 532683879..85c2fdfb1 100644 --- a/src/modules/user/user.info-by-week.resolver.ts +++ b/src/modules/user/user.info-by-week.resolver.ts @@ -55,7 +55,6 @@ export class UserInfoByWeekResolver { parent.scAddress, parent.userAddress, parent.week, - parent.positionAmount, ); } @@ -74,7 +73,6 @@ export class UserInfoByWeekResolver { parent.scAddress, parent.userAddress, parent.week, - parent.positionAmount, ); } } From 3e00b359630b20b32295c2efd4aec2fef54d20ee Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 14:59:30 +0300 Subject: [PATCH 019/313] MEX-375: add optional week input for farm accumulatedRewards field Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.resolver.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 9f7be3950..5645ce056 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -27,13 +27,16 @@ export class FarmResolverV2 extends FarmResolver { } @ResolveField() - async accumulatedRewards(@Parent() parent: FarmModelV2): Promise { + async accumulatedRewards( + @Parent() parent: FarmModelV2, + @Args('week', { nullable: true }) week: number, + ): Promise { const currentWeek = await this.weekTimekeepingAbi.currentWeek( parent.address, ); return this.farmAbi.accumulatedRewardsForWeek( parent.address, - currentWeek, + week ?? currentWeek, ); } From ee1fe5a80c2e8507bd9dcf37526b3aa33da46b03 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 15:01:45 +0300 Subject: [PATCH 020/313] MEX-375: add unit tests for farm v2 compute accumulated rewards Signed-off-by: Claudiu Lataretu --- .../farm/mocks/farm.v2.abi.service.mock.ts | 30 +++++++--- .../specs/farm.v2.compute.service.spec.ts | 60 +++++++++++++++++++ .../mocks/context.getter.service.mock.ts | 7 +++ 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts index 848590e74..559085e87 100644 --- a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts +++ b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts @@ -2,6 +2,7 @@ import { FarmAbiServiceV2 } from '../v2/services/farm.v2.abi.service'; import { FarmAbiServiceMock } from './farm.abi.service.mock'; import { IFarmAbiServiceV2 } from '../v2/services/interfaces'; import { BoostedYieldsFactors } from '../models/farm.v2.model'; +import BigNumber from 'bignumber.js'; export class FarmAbiServiceMockV2 extends FarmAbiServiceMock @@ -22,27 +23,40 @@ export class FarmAbiServiceMockV2 return '1000'; } - accumulatedRewardsForWeek( + async accumulatedRewardsForWeek( scAddress: string, week: number, ): Promise { - throw new Error('Method not implemented.'); + return new BigNumber('1000000000000000000') + .multipliedBy(10 * 60 * 24 * 7) + .multipliedBy(0.6) + .toFixed(); } - boostedYieldsFactors(farmAddress: string): Promise { - throw new Error('Method not implemented.'); + async boostedYieldsFactors( + farmAddress: string, + ): Promise { + return { + maxRewardsFactor: '2', + minFarmAmount: '1', + minEnergyAmount: '1', + userRewardsFarm: '0', + userRewardsEnergy: '1', + }; } - boostedYieldsRewardsPercenatage(farmAddress: string): Promise { - throw new Error('Method not implemented.'); + async boostedYieldsRewardsPercenatage( + farmAddress: string, + ): Promise { + return 6000; } energyFactoryAddress(farmAddress: string): Promise { throw new Error('Method not implemented.'); } - lockEpochs(farmAddress: string): Promise { - throw new Error('Method not implemented.'); + async lockEpochs(farmAddress: string): Promise { + return 1440; } lockingScAddress(farmAddress: string): Promise { diff --git a/src/modules/farm/specs/farm.v2.compute.service.spec.ts b/src/modules/farm/specs/farm.v2.compute.service.spec.ts index ad12490e9..8510385de 100644 --- a/src/modules/farm/specs/farm.v2.compute.service.spec.ts +++ b/src/modules/farm/specs/farm.v2.compute.service.spec.ts @@ -23,6 +23,10 @@ import { WinstonModule } from 'nest-winston'; import { ApiConfigService } from 'src/helpers/api.config.service'; import winston from 'winston'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; +import { FarmAbiServiceV2 } from '../v2/services/farm.v2.abi.service'; +import { Address } from '@multiversx/sdk-core/out'; +import { ContextGetterService } from 'src/services/context/context.getter.service'; describe('FarmServiceV2', () => { let module: TestingModule; @@ -83,4 +87,60 @@ describe('FarmServiceV2', () => { // // expect(mockFarmAbi.undistributedBoostedRewards).toHaveBeenCalled(); // // expect(mockFarmAbi.lastUndistributedBoostedRewardsCollectWeek).toHaveBeenCalled(); // }, 10000); + + it('should compute blocks in week', async () => { + const service = module.get(FarmComputeServiceV2); + const contextGetter = + module.get(ContextGetterService); + jest.spyOn(contextGetter, 'getCurrentEpoch').mockResolvedValue(256); + + const blocksInWeek = await service.computeBlocksInWeek( + Address.fromBech32( + 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqsdtp6mh', + ).bech32(), + 1, + ); + + expect(blocksInWeek).toEqual(10 * 60 * 24 * 7); + }); + + it('should compute user accumulated rewards', async () => { + const service = module.get(FarmComputeServiceV2); + const farmAbi = module.get(FarmAbiServiceV2); + const weeklyRewardsSplittingAbi = + module.get( + WeeklyRewardsSplittingAbiService, + ); + const contextGetter = + module.get(ContextGetterService); + + jest.spyOn(contextGetter, 'getCurrentEpoch').mockResolvedValue(256); + jest.spyOn( + weeklyRewardsSplittingAbi, + 'totalEnergyForWeek', + ).mockResolvedValue('1440'); + jest.spyOn( + weeklyRewardsSplittingAbi, + 'userEnergyForWeek', + ).mockResolvedValue({ + amount: '1440', + totalLockedTokens: '1', + lastUpdateEpoch: 256, + }); + jest.spyOn(farmAbi, 'farmTokenSupply').mockResolvedValue('2'); + jest.spyOn(farmAbi, 'rewardsPerBlock').mockResolvedValue( + '1000000000000000000', + ); + jest.spyOn(farmAbi, 'userTotalFarmPosition').mockResolvedValue('2'); + + const accumulatedRewards = await service.computeUserAccumulatedRewards( + Address.fromBech32( + 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqsdtp6mh', + ).bech32(), + Address.Zero().bech32(), + 1, + ); + + expect(accumulatedRewards).toEqual('60480000000000000000000'); + }); }); diff --git a/src/services/context/mocks/context.getter.service.mock.ts b/src/services/context/mocks/context.getter.service.mock.ts index 3c054d84b..de63ba913 100644 --- a/src/services/context/mocks/context.getter.service.mock.ts +++ b/src/services/context/mocks/context.getter.service.mock.ts @@ -8,6 +8,13 @@ export class ContextGetterServiceMock { async getShardCurrentBlockNonce(shardID: number): Promise { return 111; } + + async getBlocksCountInEpoch( + epoch: number, + shardId: number, + ): Promise { + return 10 * 60 * 24; + } } export const ContextGetterServiceProvider = { From df33c5e9b6bf9926cd472bbe32d42890c8293bf5 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 15:44:30 +0300 Subject: [PATCH 021/313] MEX-375: use farm shard ID instead of hardcoded value Signed-off-by: Claudiu Lataretu --- .../farm/v2/services/farm.v2.compute.service.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index 1a0c3df83..792230c78 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -510,10 +510,12 @@ export class FarmComputeServiceV2 scAddress: string, week: number, ): Promise { - const [startEpochForCurrentWeek, currentEpoch] = await Promise.all([ - this.weekTimekeepingCompute.startEpochForWeek(scAddress, week), - this.contextGetter.getCurrentEpoch(), - ]); + const [startEpochForCurrentWeek, currentEpoch, shardID] = + await Promise.all([ + this.weekTimekeepingCompute.startEpochForWeek(scAddress, week), + this.contextGetter.getCurrentEpoch(), + this.farmAbi.farmShard(scAddress), + ]); const promises = []; for ( @@ -521,7 +523,9 @@ export class FarmComputeServiceV2 epoch <= currentEpoch; epoch++ ) { - promises.push(this.contextGetter.getBlocksCountInEpoch(epoch, 1)); + promises.push( + this.contextGetter.getBlocksCountInEpoch(epoch, shardID), + ); } const blocksInEpoch = await Promise.all(promises); From 9b013928271c03807092553597b5794cc155d833 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 15:45:14 +0300 Subject: [PATCH 022/313] MEX-375: use GetOrSetCache decorator in farm v2 compute service Signed-off-by: Claudiu Lataretu --- .../v2/services/farm.v2.compute.service.ts | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index 792230c78..54a033e45 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -172,22 +172,17 @@ export class FarmComputeServiceV2 @ErrorLoggerAsync({ logArgs: true, }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, + localTtl: CacheTtlInfo.ContractBalance.localTtl, + }) async userAccumulatedRewards( scAddress: string, userAddress: string, week: number, ): Promise { - return await this.cachingService.getOrSet( - `farm.userAccumulatedRewards.${scAddress}.${userAddress}.${week}`, - () => - this.computeUserAccumulatedRewards( - scAddress, - userAddress, - week, - ), - CacheTtlInfo.ContractBalance.remoteTtl, - CacheTtlInfo.ContractBalance.localTtl, - ); + return this.computeUserAccumulatedRewards(scAddress, userAddress, week); } async computeUserAccumulatedRewards( @@ -282,17 +277,17 @@ export class FarmComputeServiceV2 @ErrorLoggerAsync({ logArgs: true, }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, + localTtl: CacheTtlInfo.ContractBalance.localTtl, + }) async userRewardsForWeek( scAddress: string, userAddress: string, week: number, ): Promise { - return await this.cachingService.getOrSet( - `farm.userRewardsForWeek.${scAddress}.${userAddress}.${week}`, - () => this.computeUserRewardsForWeek(scAddress, userAddress, week), - CacheTtlInfo.ContractBalance.remoteTtl, - CacheTtlInfo.ContractBalance.localTtl, - ); + return this.computeUserRewardsForWeek(scAddress, userAddress, week); } async computeUserRewardsForWeek( From 725c25a387f36a23d77f5acf456fd2576cc68637 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 15:46:16 +0300 Subject: [PATCH 023/313] MEX-375: proper return type for getShardBlockCountInEpoch Signed-off-by: Claudiu Lataretu --- .../mx.api.service.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/services/multiversx-communication/mx.api.service.ts b/src/services/multiversx-communication/mx.api.service.ts index 0512f5f56..2a3e6699f 100644 --- a/src/services/multiversx-communication/mx.api.service.ts +++ b/src/services/multiversx-communication/mx.api.service.ts @@ -11,7 +11,12 @@ import { MetricsCollector } from '../../utils/metrics.collector'; import { Stats } from '../../models/stats.model'; import { ApiConfigService } from 'src/helpers/api.config.service'; import { ApiNetworkProvider } from '@multiversx/sdk-network-providers/out'; -import { isEsdtToken, isEsdtTokenValid, isNftCollection, isNftCollectionValid } from 'src/utils/token.type.compare'; +import { + isEsdtToken, + isEsdtTokenValid, + isNftCollection, + isNftCollectionValid, +} from 'src/utils/token.type.compare'; import { PendingExecutor } from 'src/utils/pending.executor'; import { MXProxyService } from './mx.proxy.service'; @@ -114,8 +119,8 @@ export class MXApiService { async getShardBlockCountInEpoch( epoch: number, shardId: number, - ): Promise { - return await this.doGetGeneric( + ): Promise { + return await this.doGetGeneric( this.getStats.name, `blocks/count?epoch=${epoch}&shard=${shardId}`, ); @@ -327,13 +332,11 @@ export class MXApiService { ); } - async getTransactionsWithOptions( - { - sender, - receiver, - functionName, - }, - ): Promise { + async getTransactionsWithOptions({ + sender, + receiver, + functionName, + }): Promise { return await this.doGetGeneric( this.getTransactions.name, `transactions?sender=${sender}&receiver=${receiver}&function=${functionName}`, From d36a5a473d19e6b936dacb892e7af1d8ed287fc9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 5 Oct 2023 15:47:35 +0300 Subject: [PATCH 024/313] MEX-375: add cache warmer for shard blocks count Signed-off-by: Claudiu Lataretu --- src/services/crons/cache.warmer.service.ts | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/services/crons/cache.warmer.service.ts b/src/services/crons/cache.warmer.service.ts index e10bcae8f..7cc9ec399 100644 --- a/src/services/crons/cache.warmer.service.ts +++ b/src/services/crons/cache.warmer.service.ts @@ -143,7 +143,7 @@ export class CacheWarmerService { @Cron('*/6 * * * * *') async cacheShardCurrentBlockNonce(): Promise { const stats = await this.apiService.getStats(); - const promises: Promise[] = []; + let promises: Promise[] = []; for (let index = 0; index < stats.shards; index++) { promises.push(this.apiService.getCurrentBlockNonce(index)); } @@ -160,6 +160,29 @@ export class CacheWarmerService { shardsNonces[index], Constants.oneMinute(), ); + invalidatedKeys.push(cacheKey); + } + + promises = []; + for (let index = 0; index < stats.shards; index++) { + promises.push( + this.apiService.getShardBlockCountInEpoch(stats.epoch, index), + ); + } + const shardsBlockCount = await Promise.all(promises); + for (let index = 0; index < stats.shards; index++) { + const cacheKey = generateCacheKeyFromParams( + 'context', + 'blocksCountInEpoch', + index, + stats.epoch, + ); + await this.cachingService.set( + cacheKey, + shardsBlockCount[index], + Constants.oneMinute(), + ); + invalidatedKeys.push(cacheKey); } await this.deleteCacheKeys(invalidatedKeys); From 0667e7c12c7a733741536736c2bc469442926065 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 9 Oct 2023 15:53:27 +0300 Subject: [PATCH 025/313] MEX-376: remove logging message Signed-off-by: Claudiu Lataretu --- src/modules/farm/specs/farm.v2.transactions.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/farm/specs/farm.v2.transactions.service.spec.ts b/src/modules/farm/specs/farm.v2.transactions.service.spec.ts index bc6266891..a0429ffc7 100644 --- a/src/modules/farm/specs/farm.v2.transactions.service.spec.ts +++ b/src/modules/farm/specs/farm.v2.transactions.service.spec.ts @@ -115,7 +115,7 @@ describe('FarmTransactionsServiceV2', () => { ).bech32(), Address.Zero().bech32(), ); - console.log(transactions); + expect(transactions[0].data).toEqual( encodeTransactionData( `ESDTNFTTransfer@EGLDMEXFL-ghijkl@01@12120193336145595@${Address.fromHex( From 8e1a75917ee5030e00f28b72113b650e0472850d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 18:14:39 +0300 Subject: [PATCH 026/313] MEX-404: update farm staking abi with boosted rewards Signed-off-by: Claudiu Lataretu --- src/abis/farm-staking.abi.json | 1398 ++++++++++++++++++++++++-------- 1 file changed, 1051 insertions(+), 347 deletions(-) diff --git a/src/abis/farm-staking.abi.json b/src/abis/farm-staking.abi.json index 029354a8c..95aeb73a3 100644 --- a/src/abis/farm-staking.abi.json +++ b/src/abis/farm-staking.abi.json @@ -1,28 +1,25 @@ { "buildInfo": { "rustc": { - "version": "1.60.0-nightly", - "commitHash": "a00e130dae74a213338e2b095ec855156d8f3d8a", - "commitDate": "2022-01-29", + "version": "1.73.0-nightly", + "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", + "commitDate": "2023-07-15", "channel": "Nightly", - "short": "rustc 1.60.0-nightly (a00e130da 2022-01-29)" + "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" }, "contractCrate": { "name": "farm-staking", - "version": "0.0.0" + "version": "0.0.0", + "gitVersion": "v1.6.0-1527-g9534cc41" }, "framework": { - "name": "elrond-wasm", - "version": "0.27.4" + "name": "multiversx-sc", + "version": "0.43.3" } }, - "name": "Farm", + "name": "FarmStaking", "constructor": { "inputs": [ - { - "name": "reward_token_id", - "type": "TokenIdentifier" - }, { "name": "farming_token_id", "type": "TokenIdentifier" @@ -38,136 +35,33 @@ { "name": "min_unbond_epochs", "type": "u64" + }, + { + "name": "owner", + "type": "Address" + }, + { + "name": "admins", + "type": "variadic
", + "multi_arg": true } ], "outputs": [] }, "endpoints": [ { - "name": "stakeFarmThroughProxy", - "mutability": "mutable", - "inputs": [ - { - "name": "staked_token_amount", - "type": "BigUint" - } - ], - "outputs": [ - { - "type": "EsdtTokenPayment" - } - ] - }, - { - "name": "stakeFarm", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], - "inputs": [ - { - "name": "opt_accept_funds_func", - "type": "optional", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "EsdtTokenPayment" - } - ] - }, - { - "name": "unstakeFarm", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], - "inputs": [ - { - "name": "opt_accept_funds_func", - "type": "optional", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "EsdtTokenPayment" - }, - { - "type": "EsdtTokenPayment" - } - ] - }, - { - "name": "unstakeFarmThroughProxy", + "name": "upgrade", "mutability": "mutable", - "payableInTokens": [ - "*" - ], "inputs": [], - "outputs": [ - { - "type": "EsdtTokenPayment" - }, - { - "type": "EsdtTokenPayment" - } - ] - }, - { - "name": "unbondFarm", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], - "inputs": [ - { - "name": "opt_accept_funds_func", - "type": "optional", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "EsdtTokenPayment" - } - ] - }, - { - "name": "claimRewards", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], - "inputs": [ - { - "name": "opt_accept_funds_func", - "type": "optional", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "EsdtTokenPayment" - }, - { - "type": "EsdtTokenPayment" - } - ] + "outputs": [] }, { - "name": "claimRewardsWithNewValue", + "name": "mergeFarmTokens", "mutability": "mutable", "payableInTokens": [ "*" ], - "inputs": [ - { - "name": "new_values", - "type": "List" - } - ], + "inputs": [], "outputs": [ { "type": "EsdtTokenPayment" @@ -177,31 +71,12 @@ } ] }, - { - "name": "compoundRewards", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], - "inputs": [ - { - "name": "opt_accept_funds_func", - "type": "optional", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "EsdtTokenPayment" - } - ] - }, { "name": "calculateRewardsForGivenPosition", "mutability": "readonly", "inputs": [ { - "name": "amount", + "name": "farm_token_amount", "type": "BigUint" }, { @@ -217,7 +92,6 @@ }, { "name": "topUpRewards", - "onlyOwner": true, "mutability": "mutable", "payableInTokens": [ "*" @@ -226,15 +100,13 @@ "outputs": [] }, { - "name": "end_produce_rewards", - "onlyOwner": true, + "name": "endProduceRewards", "mutability": "mutable", "inputs": [], "outputs": [] }, { "name": "setPerBlockRewardAmount", - "onlyOwner": true, "mutability": "mutable", "inputs": [ { @@ -246,7 +118,6 @@ }, { "name": "setMaxApr", - "onlyOwner": true, "mutability": "mutable", "inputs": [ { @@ -258,7 +129,6 @@ }, { "name": "setMinUnbondEpochs", - "onlyOwner": true, "mutability": "mutable", "inputs": [ { @@ -270,21 +140,10 @@ }, { "name": "startProduceRewards", - "onlyOwner": true, "mutability": "mutable", "inputs": [], "outputs": [] }, - { - "name": "getRewardPerShare", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "BigUint" - } - ] - }, { "name": "getAccumulatedRewards", "mutability": "readonly", @@ -326,119 +185,103 @@ ] }, { - "name": "set_penalty_percent", - "onlyOwner": true, - "mutability": "mutable", - "inputs": [ + "name": "getRewardPerShare", + "mutability": "readonly", + "inputs": [], + "outputs": [ { - "name": "percent", - "type": "u64" + "type": "BigUint" } - ], - "outputs": [] + ] }, { - "name": "set_minimum_farming_epochs", - "onlyOwner": true, - "mutability": "mutable", - "inputs": [ + "name": "getRewardReserve", + "mutability": "readonly", + "inputs": [], + "outputs": [ { - "name": "epochs", - "type": "u8" + "type": "BigUint" } - ], - "outputs": [] + ] }, { - "name": "set_transfer_exec_gas_limit", - "onlyOwner": true, + "name": "allowExternalClaimBoostedRewards", "mutability": "mutable", "inputs": [ { - "name": "gas_limit", - "type": "u64" + "name": "allow_external_claim", + "type": "bool" } ], "outputs": [] }, { - "name": "set_burn_gas_limit", - "onlyOwner": true, - "mutability": "mutable", - "inputs": [ + "name": "getFarmingTokenId", + "mutability": "readonly", + "inputs": [], + "outputs": [ { - "name": "gas_limit", - "type": "u64" + "type": "TokenIdentifier" } - ], - "outputs": [] - }, - { - "name": "pause", - "onlyOwner": true, - "mutability": "mutable", - "inputs": [], - "outputs": [] - }, - { - "name": "resume", - "onlyOwner": true, - "mutability": "mutable", - "inputs": [], - "outputs": [] + ] }, { - "name": "getFarmTokenSupply", + "name": "getRewardTokenId", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "BigUint" + "type": "TokenIdentifier" } ] }, { - "name": "getLastErrorMessage", + "name": "getPerBlockRewardAmount", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "bytes" + "type": "BigUint" } ] }, { - "name": "getState", + "name": "getLastRewardBlockNonce", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "State" + "type": "u64" } ] }, { - "name": "getFarmingTokenId", + "name": "getDivisionSafetyConstant", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "TokenIdentifier" + "type": "BigUint" } ] }, { - "name": "getRewardTokenId", + "name": "getUserTotalFarmPosition", "mutability": "readonly", - "inputs": [], + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], "outputs": [ { - "type": "TokenIdentifier" + "type": "UserTotalFarmPosition" } ] }, { - "name": "getPenaltyPercent", + "name": "getFarmPositionMigrationNonce", "mutability": "readonly", "inputs": [], "outputs": [ @@ -448,146 +291,183 @@ ] }, { - "name": "getMinimumFarmingEpoch", - "mutability": "readonly", - "inputs": [], - "outputs": [ + "name": "registerFarmToken", + "mutability": "mutable", + "payableInTokens": [ + "EGLD" + ], + "inputs": [ + { + "name": "token_display_name", + "type": "bytes" + }, + { + "name": "token_ticker", + "type": "bytes" + }, { - "type": "u8" + "name": "num_decimals", + "type": "u32" } - ] + ], + "outputs": [] }, { - "name": "getPerBlockRewardAmount", + "name": "getFarmTokenId", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "BigUint" + "type": "TokenIdentifier" } ] }, { - "name": "getLastRewardBlockNonce", + "name": "getFarmTokenSupply", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "u64" + "type": "BigUint" } ] }, { - "name": "getFarmTokenId", - "mutability": "readonly", - "inputs": [], - "outputs": [ + "name": "addSCAddressToWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ { - "type": "TokenIdentifier" + "name": "address", + "type": "Address" } - ] + ], + "outputs": [] }, { - "name": "getDivisionSafetyConstant", - "mutability": "readonly", - "inputs": [], - "outputs": [ + "name": "removeSCAddressFromWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ { - "type": "BigUint" + "name": "address", + "type": "Address" } - ] + ], + "outputs": [] }, { - "name": "getPairContractManagedAddress", + "name": "isSCAddressWhitelisted", "mutability": "readonly", - "inputs": [], - "outputs": [ + "inputs": [ { + "name": "address", "type": "Address" } + ], + "outputs": [ + { + "type": "bool" + } ] }, { - "name": "getBurnGasLimit", - "mutability": "readonly", - "inputs": [], - "outputs": [ + "name": "addToPauseWhitelist", + "mutability": "mutable", + "inputs": [ { - "type": "u64" + "name": "address_list", + "type": "variadic
", + "multi_arg": true } - ] + ], + "outputs": [] }, { - "name": "getLockedAssetFactoryManagedAddress", - "mutability": "readonly", - "inputs": [], - "outputs": [ + "name": "removeFromPauseWhitelist", + "mutability": "mutable", + "inputs": [ { - "type": "Address" + "name": "address_list", + "type": "variadic
", + "multi_arg": true } - ] + ], + "outputs": [] + }, + { + "name": "pause", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "resume", + "mutability": "mutable", + "inputs": [], + "outputs": [] }, { - "name": "getTransferExecGasLimit", + "name": "getState", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "u64" + "type": "State" } ] }, { - "name": "registerFarmToken", - "onlyOwner": true, + "name": "addAdmin", "mutability": "mutable", - "payableInTokens": [ - "EGLD" - ], "inputs": [ { - "name": "token_display_name", - "type": "bytes" - }, - { - "name": "token_ticker", - "type": "bytes" - }, - { - "name": "num_decimals", - "type": "u32" + "name": "address", + "type": "Address" } ], "outputs": [] }, { - "name": "setLocalRolesFarmToken", - "onlyOwner": true, + "name": "removeAdmin", "mutability": "mutable", - "inputs": [], + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], "outputs": [] }, { - "name": "mergeFarmTokens", + "name": "updateOwnerOrAdmin", + "onlyOwner": true, "mutability": "mutable", - "payableInTokens": [ - "*" + "inputs": [ + { + "name": "previous_owner", + "type": "Address" + } ], + "outputs": [] + }, + { + "name": "getPermissions", + "mutability": "readonly", "inputs": [ { - "name": "opt_accept_funds_func", - "type": "optional", - "multi_arg": true + "name": "address", + "type": "Address" } ], "outputs": [ { - "type": "EsdtTokenPayment" + "type": "u32" } ] }, { - "name": "addAddressToWhitelist", + "name": "setBurnRoleForAddress", "onlyOwner": true, "mutability": "mutable", "inputs": [ @@ -599,112 +479,936 @@ "outputs": [] }, { - "name": "removeAddressFromWhitelist", - "onlyOwner": true, + "name": "stakeFarmThroughProxy", "mutability": "mutable", + "payableInTokens": [ + "*" + ], "inputs": [ { - "name": "address", + "name": "staked_token_amount", + "type": "BigUint" + }, + { + "name": "original_caller", "type": "Address" } ], - "outputs": [] + "outputs": [ + { + "type": "EsdtTokenPayment" + }, + { + "type": "EsdtTokenPayment" + } + ] }, { - "name": "isWhitelisted", - "mutability": "readonly", + "name": "stakeFarm", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], "inputs": [ { - "name": "address", - "type": "Address" + "name": "opt_original_caller", + "type": "optional
", + "multi_arg": true } ], "outputs": [ { - "type": "bool" + "type": "EsdtTokenPayment" + }, + { + "type": "EsdtTokenPayment" } ] - } - ], - "hasCallback": true, - "types": { - "EsdtTokenPayment": { - "type": "struct", - "fields": [ - { - "name": "token_type", - "type": "EsdtTokenType" - }, + }, + { + "name": "claimRewards", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "token_identifier", - "type": "TokenIdentifier" - }, + "name": "opt_original_caller", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [ { - "name": "token_nonce", - "type": "u64" + "type": "EsdtTokenPayment" }, { - "name": "amount", - "type": "BigUint" + "type": "EsdtTokenPayment" } ] }, - "EsdtTokenType": { - "type": "enum", - "variants": [ - { - "name": "Fungible", - "discriminant": 0 - }, + { + "name": "claimRewardsWithNewValue", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "NonFungible", - "discriminant": 1 + "name": "new_farming_amount", + "type": "BigUint" }, { - "name": "SemiFungible", - "discriminant": 2 - }, + "name": "original_caller", + "type": "Address" + } + ], + "outputs": [ { - "name": "Meta", - "discriminant": 3 + "type": "EsdtTokenPayment" }, { - "name": "Invalid", - "discriminant": 4 + "type": "EsdtTokenPayment" } ] }, - "StakingFarmTokenAttributes": { - "type": "struct", - "fields": [ + { + "name": "compoundRewards", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [ { - "name": "reward_per_share", - "type": "BigUint" - }, + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "unstakeFarm", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "last_claim_block", - "type": "u64" - }, + "name": "opt_original_caller", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [ { - "name": "compounded_reward", - "type": "BigUint" + "type": "EsdtTokenPayment" }, { - "name": "current_farm_amount", - "type": "BigUint" + "type": "EsdtTokenPayment" } ] }, - "State": { - "type": "enum", - "variants": [ + { + "name": "unstakeFarmThroughProxy", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "Inactive", - "discriminant": 0 + "name": "original_caller", + "type": "Address" + } + ], + "outputs": [ + { + "type": "EsdtTokenPayment" }, { - "name": "Active", - "discriminant": 1 + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "unbondFarm", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [ + { + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "claimBoostedRewards", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "opt_user", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "setBoostedYieldsRewardsPercentage", + "mutability": "mutable", + "inputs": [ + { + "name": "percentage", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "collectUndistributedBoostedRewards", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "getBoostedYieldsRewardsPercentage", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getAccumulatedRewardsForWeek", + "mutability": "readonly", + "inputs": [ + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getFarmSupplyForWeek", + "mutability": "readonly", + "inputs": [ + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getRemainingBoostedRewardsToDistribute", + "mutability": "readonly", + "inputs": [ + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getUndistributedBoostedRewards", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "setBoostedYieldsFactors", + "mutability": "mutable", + "inputs": [ + { + "name": "max_rewards_factor", + "type": "BigUint" + }, + { + "name": "user_rewards_energy_const", + "type": "BigUint" + }, + { + "name": "user_rewards_farm_const", + "type": "BigUint" + }, + { + "name": "min_energy_amount", + "type": "BigUint" + }, + { + "name": "min_farm_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "getBoostedYieldsFactors", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BoostedYieldsFactors" + } + ] + }, + { + "docs": [ + "Week starts from 1" + ], + "name": "getCurrentWeek", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getFirstWeekStartEpoch", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getLastActiveWeekForUser", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getUserEnergyForWeek", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + }, + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "optional", + "multi_result": true + } + ] + }, + { + "name": "getLastGlobalUpdateWeek", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getTotalRewardsForWeek", + "mutability": "readonly", + "inputs": [ + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "getTotalEnergyForWeek", + "mutability": "readonly", + "inputs": [ + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getTotalLockedTokensForWeek", + "mutability": "readonly", + "inputs": [ + { + "name": "week", + "type": "u32" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "updateEnergyForUser", + "mutability": "mutable", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getCurrentClaimProgress", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "ClaimProgress" + } + ] + }, + { + "name": "setEnergyFactoryAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "sc_address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getEnergyFactoryAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + } + ], + "events": [ + { + "identifier": "enter_farm", + "inputs": [ + { + "name": "caller", + "type": "Address", + "indexed": true + }, + { + "name": "epoch", + "type": "u64", + "indexed": true + }, + { + "name": "block", + "type": "u64", + "indexed": true + }, + { + "name": "timestamp", + "type": "u64", + "indexed": true + }, + { + "name": "farming_token", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "enter_farm_event", + "type": "EnterFarmEvent" + } + ] + }, + { + "identifier": "exit_farm", + "inputs": [ + { + "name": "caller", + "type": "Address", + "indexed": true + }, + { + "name": "epoch", + "type": "u64", + "indexed": true + }, + { + "name": "block", + "type": "u64", + "indexed": true + }, + { + "name": "timestamp", + "type": "u64", + "indexed": true + }, + { + "name": "farm_token", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "exit_farm_event", + "type": "ExitFarmEvent" + } + ] + }, + { + "identifier": "claim_rewards", + "inputs": [ + { + "name": "caller", + "type": "Address", + "indexed": true + }, + { + "name": "epoch", + "type": "u64", + "indexed": true + }, + { + "name": "block", + "type": "u64", + "indexed": true + }, + { + "name": "timestamp", + "type": "u64", + "indexed": true + }, + { + "name": "farm_token", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "claim_rewards_event", + "type": "ClaimRewardsEvent" + } + ] + }, + { + "identifier": "compound_rewards", + "inputs": [ + { + "name": "caller", + "type": "Address", + "indexed": true + }, + { + "name": "epoch", + "type": "u64", + "indexed": true + }, + { + "name": "block", + "type": "u64", + "indexed": true + }, + { + "name": "timestamp", + "type": "u64", + "indexed": true + }, + { + "name": "farm_token", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "compound_rewards_event", + "type": "CompoundRewardsEvent" + } + ] + }, + { + "identifier": "claim_multi_event", + "inputs": [ + { + "name": "user", + "type": "Address", + "indexed": true + }, + { + "name": "current_week", + "type": "u32", + "indexed": true + }, + { + "name": "energy", + "type": "Energy", + "indexed": true + }, + { + "name": "all_payments", + "type": "List" + } + ] + }, + { + "identifier": "update_user_energy_event", + "inputs": [ + { + "name": "user", + "type": "Address", + "indexed": true + }, + { + "name": "current_week", + "type": "u32", + "indexed": true + }, + { + "name": "energy", + "type": "Energy", + "indexed": true + } + ] + }, + { + "identifier": "update_global_amounts_event", + "inputs": [ + { + "name": "current_week", + "type": "u32", + "indexed": true + }, + { + "name": "total_locked_tokens", + "type": "BigUint", + "indexed": true + }, + { + "name": "total_energy", + "type": "BigUint", + "indexed": true + } + ] + } + ], + "hasCallback": true, + "types": { + "BoostedYieldsFactors": { + "type": "struct", + "fields": [ + { + "name": "max_rewards_factor", + "type": "BigUint" + }, + { + "name": "user_rewards_energy_const", + "type": "BigUint" + }, + { + "name": "user_rewards_farm_const", + "type": "BigUint" + }, + { + "name": "min_energy_amount", + "type": "BigUint" + }, + { + "name": "min_farm_amount", + "type": "BigUint" + } + ] + }, + "ClaimProgress": { + "type": "struct", + "fields": [ + { + "name": "energy", + "type": "Energy" + }, + { + "name": "week", + "type": "u32" + } + ] + }, + "ClaimRewardsEvent": { + "type": "struct", + "fields": [ + { + "name": "old_farm_token", + "type": "EsdtTokenPayment" + }, + { + "name": "new_farm_token", + "type": "EsdtTokenPayment" + }, + { + "name": "farm_supply", + "type": "BigUint" + }, + { + "name": "reward_tokens", + "type": "EsdtTokenPayment" + }, + { + "name": "reward_reserve", + "type": "BigUint" + }, + { + "name": "old_farm_attributes", + "type": "bytes" + }, + { + "name": "new_farm_attributes", + "type": "bytes" + }, + { + "name": "created_with_merge", + "type": "bool" + } + ] + }, + "CompoundRewardsEvent": { + "type": "struct", + "fields": [ + { + "name": "old_farm_token", + "type": "EsdtTokenPayment" + }, + { + "name": "new_farm_token", + "type": "EsdtTokenPayment" + }, + { + "name": "farm_supply", + "type": "BigUint" + }, + { + "name": "reward_tokens", + "type": "EsdtTokenPayment" + }, + { + "name": "reward_reserve", + "type": "BigUint" + }, + { + "name": "old_farm_attributes", + "type": "bytes" + }, + { + "name": "new_farm_attributes", + "type": "bytes" + }, + { + "name": "created_with_merge", + "type": "bool" + } + ] + }, + "Energy": { + "type": "struct", + "fields": [ + { + "name": "amount", + "type": "BigInt" + }, + { + "name": "last_update_epoch", + "type": "u64" + }, + { + "name": "total_locked_tokens", + "type": "BigUint" + } + ] + }, + "EnterFarmEvent": { + "type": "struct", + "fields": [ + { + "name": "farming_token_id", + "type": "TokenIdentifier" + }, + { + "name": "farming_token_amount", + "type": "BigUint" + }, + { + "name": "farm_token", + "type": "EsdtTokenPayment" + }, + { + "name": "farm_supply", + "type": "BigUint" + }, + { + "name": "reward_token_id", + "type": "TokenIdentifier" + }, + { + "name": "reward_token_reserve", + "type": "BigUint" + }, + { + "name": "farm_attributes", + "type": "bytes" + }, + { + "name": "created_with_merge", + "type": "bool" + } + ] + }, + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "ExitFarmEvent": { + "type": "struct", + "fields": [ + { + "name": "farming_token_id", + "type": "TokenIdentifier" + }, + { + "name": "farming_token_amount", + "type": "BigUint" + }, + { + "name": "farm_token", + "type": "EsdtTokenPayment" + }, + { + "name": "farm_supply", + "type": "BigUint" + }, + { + "name": "reward_tokens", + "type": "EsdtTokenPayment" + }, + { + "name": "reward_reserve", + "type": "BigUint" + }, + { + "name": "farm_attributes", + "type": "bytes" + } + ] + }, + "StakingFarmTokenAttributes": { + "type": "struct", + "fields": [ + { + "name": "reward_per_share", + "type": "BigUint" + }, + { + "name": "compounded_reward", + "type": "BigUint" + }, + { + "name": "current_farm_amount", + "type": "BigUint" + }, + { + "name": "original_owner", + "type": "Address" + } + ] + }, + "State": { + "type": "enum", + "variants": [ + { + "name": "Inactive", + "discriminant": 0 + }, + { + "name": "Active", + "discriminant": 1 + }, + { + "name": "PartialActive", + "discriminant": 2 + } + ] + }, + "UserTotalFarmPosition": { + "type": "struct", + "fields": [ + { + "name": "total_farm_position", + "type": "BigUint" + }, + { + "name": "allow_external_claim_boosted_rewards", + "type": "bool" } ] } From ef7de9e68d362abf306001172d9e7a9dbf5c3f60 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 18:16:39 +0300 Subject: [PATCH 027/313] MEX-404: Add week timekeeping field to staking model Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 3 +++ src/modules/staking/staking.module.ts | 2 ++ src/modules/staking/staking.resolver.ts | 14 ++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 40a3c6039..93aba4849 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -2,6 +2,7 @@ import { Field, Int, ObjectType } from '@nestjs/graphql'; import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { StakingTokenAttributesModel } from './stakingTokenAttributes.model'; +import { WeekTimekeepingModel } from 'src/submodules/week-timekeeping/models/week-timekeeping.model'; @ObjectType() export class StakingModel { @@ -45,6 +46,8 @@ export class StakingModel { transferExecGasLimit: string; @Field() state: string; + @Field({ description: 'Timekeeping for boosted rewards' }) + time: WeekTimekeepingModel; constructor(init?: Partial) { Object.assign(this, init); diff --git a/src/modules/staking/staking.module.ts b/src/modules/staking/staking.module.ts index 78b5a1a2b..043035935 100644 --- a/src/modules/staking/staking.module.ts +++ b/src/modules/staking/staking.module.ts @@ -10,6 +10,7 @@ import { StakingService } from './services/staking.service'; import { StakingSetterService } from './services/staking.setter.service'; import { StakingTransactionService } from './services/staking.transactions.service'; import { StakingResolver } from './staking.resolver'; +import { WeekTimekeepingModule } from 'src/submodules/week-timekeeping/week-timekeeping.module'; @Module({ imports: [ @@ -18,6 +19,7 @@ import { StakingResolver } from './staking.resolver'; MXCommunicationModule, RemoteConfigModule, TokenModule, + WeekTimekeepingModule, ], providers: [ StakingAbiService, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index ac85d5b41..023aa9ad9 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -32,6 +32,8 @@ import { StakingTransactionService } from './services/staking.transactions.servi import { StakingAbiService } from './services/staking.abi.service'; import { StakingComputeService } from './services/staking.compute.service'; import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; +import { WeekTimekeepingModel } from 'src/submodules/week-timekeeping/models/week-timekeeping.model'; +import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; @Resolver(() => StakingModel) export class StakingResolver { @@ -40,6 +42,7 @@ export class StakingResolver { private readonly stakingAbi: StakingAbiService, private readonly stakingCompute: StakingComputeService, private readonly stakingTransactionService: StakingTransactionService, + private readonly weekTimekeepingAbi: WeekTimekeepingAbiService, ) {} @ResolveField() @@ -137,6 +140,17 @@ export class StakingResolver { return this.stakingAbi.state(parent.address); } + @ResolveField() + async time(@Parent() parent: StakingModel): Promise { + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + parent.address, + ); + return new WeekTimekeepingModel({ + scAddress: parent.address, + currentWeek: currentWeek, + }); + } + @Query(() => String) async getLastErrorMessage( @Args('stakeAddress') stakeAddress: string, From a5086bdb8ead7fef67b584eb63e7f7b2587efe6d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 18:22:44 +0300 Subject: [PATCH 028/313] MEX-404: cleanup farm staking model Signed-off-by: Claudiu Lataretu --- .../staking/mocks/staking.abi.service.mock.ts | 10 --- src/modules/staking/models/staking.model.ts | 6 -- src/modules/staking/services/interfaces.ts | 3 - .../staking/services/staking.abi.service.ts | 67 ------------------- src/modules/staking/staking.resolver.ts | 24 +------ 5 files changed, 1 insertion(+), 109 deletions(-) diff --git a/src/modules/staking/mocks/staking.abi.service.mock.ts b/src/modules/staking/mocks/staking.abi.service.mock.ts index bf42b16d3..12dd0de74 100644 --- a/src/modules/staking/mocks/staking.abi.service.mock.ts +++ b/src/modules/staking/mocks/staking.abi.service.mock.ts @@ -1,12 +1,8 @@ import BigNumber from 'bignumber.js'; import { IStakingAbiService } from '../services/interfaces'; import { StakingAbiService } from '../services/staking.abi.service'; -import { Address } from '@multiversx/sdk-core/out'; export class StakingAbiServiceMock implements IStakingAbiService { - async pairContractAddress(stakeAddress: string): Promise { - return Address.Zero().bech32(); - } async farmTokenID(stakeAddress: string): Promise { return 'STAKETOK-1111'; } @@ -46,12 +42,6 @@ export class StakingAbiServiceMock implements IStakingAbiService { async produceRewardsEnabled(stakeAddress: string): Promise { return true; } - burnGasLimit(stakeAddress: string): Promise { - throw new Error('Method not implemented.'); - } - transferExecGasLimit(stakeAddress: string): Promise { - throw new Error('Method not implemented.'); - } state(stakeAddress: string): Promise { throw new Error('Method not implemented.'); } diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 93aba4849..7b9906d86 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -17,8 +17,6 @@ export class StakingModel { @Field() farmTokenSupply: string; @Field() - pairContractManagedAddress: string; - @Field() rewardPerShare: string; @Field() accumulatedRewards: string; @@ -40,10 +38,6 @@ export class StakingModel { produceRewardsEnabled: boolean; @Field({ nullable: true }) lockedAssetFactoryManagedAddress: string; - @Field({ nullable: true }) - burnGasLimit: string; - @Field({ nullable: true }) - transferExecGasLimit: string; @Field() state: string; @Field({ description: 'Timekeeping for boosted rewards' }) diff --git a/src/modules/staking/services/interfaces.ts b/src/modules/staking/services/interfaces.ts index 046252083..5d7b2002c 100644 --- a/src/modules/staking/services/interfaces.ts +++ b/src/modules/staking/services/interfaces.ts @@ -1,7 +1,6 @@ import BigNumber from 'bignumber.js'; export interface IStakingAbiService { - pairContractAddress(stakeAddress: string): Promise; farmTokenID(stakeAddress: string): Promise; farmingTokenID(stakeAddress: string): Promise; rewardTokenID(stakeAddress: string): Promise; @@ -16,8 +15,6 @@ export interface IStakingAbiService { lastRewardBlockNonce(stakeAddress: string): Promise; divisionSafetyConstant(stakeAddress: string): Promise; produceRewardsEnabled(stakeAddress: string): Promise; - burnGasLimit(stakeAddress: string): Promise; - transferExecGasLimit(stakeAddress: string): Promise; state(stakeAddress: string): Promise; calculateRewardsForGivenPosition( stakeAddress: string, diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index aa03d7f3f..d76916c3d 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -29,31 +29,6 @@ export class StakingAbiService super(mxProxy); } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: Constants.oneHour(), - }) - async pairContractAddress(stakeAddress: string): Promise { - return await this.getPairContractAddressRaw(stakeAddress); - } - - async getPairContractAddressRaw(stakeAddress: string): Promise { - try { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - const interaction: Interaction = - contract.methodsExplicit.getPairContractManagedAddress(); - const response = await this.getGenericData(interaction); - return response.firstValue.valueOf().hex32(); - } catch { - return undefined; - } - } - @ErrorLoggerAsync({ logArgs: true, }) @@ -338,48 +313,6 @@ export class StakingAbiService return response === '01'; } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: Constants.oneHour(), - }) - async burnGasLimit(stakeAddress: string): Promise { - return await this.getBurnGasLimitRaw(stakeAddress); - } - - async getBurnGasLimitRaw(stakeAddress: string): Promise { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - const interaction: Interaction = - contract.methodsExplicit.getBurnGasLimit(); - const response = await this.getGenericData(interaction); - return response.firstValue.valueOf(); - } - - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: Constants.oneHour(), - }) - async transferExecGasLimit(stakeAddress: string): Promise { - return await this.getTransferExecGasLimitRaw(stakeAddress); - } - - async getTransferExecGasLimitRaw(stakeAddress: string): Promise { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - const interaction: Interaction = - contract.methodsExplicit.getTransferExecGasLimit(); - const response = await this.getGenericData(interaction); - return response.firstValue.valueOf(); - } - @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 023aa9ad9..9bd8345ca 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -1,12 +1,5 @@ import { UseGuards } from '@nestjs/common'; -import { - Args, - Int, - Parent, - Query, - ResolveField, - Resolver, -} from '@nestjs/graphql'; +import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'; import { AuthUser } from '../auth/auth.user'; import { UserAuthResult } from '../auth/user.auth.result'; import { TransactionModel } from 'src/models/transaction.model'; @@ -120,21 +113,6 @@ export class StakingResolver { return this.stakingAbi.lockedAssetFactoryAddress(parent.address); } - @ResolveField() - async pairContractManagedAddress(@Parent() parent: StakingModel) { - return this.stakingAbi.pairContractAddress(parent.address); - } - - @ResolveField() - async burnGasLimit(@Parent() parent: StakingModel) { - return this.stakingAbi.burnGasLimit(parent.address); - } - - @ResolveField() - async transferExecGasLimit(@Parent() parent: StakingModel) { - return this.stakingAbi.transferExecGasLimit(parent.address); - } - @ResolveField() async state(@Parent() parent: StakingModel) { return this.stakingAbi.state(parent.address); From 6b4d4bf4e5855792f013fb28cd01cda40172eb3c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 18:27:35 +0300 Subject: [PATCH 029/313] MEX-404: remove unused setter from farm staking Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.setter.service.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/modules/staking/services/staking.setter.service.ts b/src/modules/staking/services/staking.setter.service.ts index ad6686c8c..e26429b00 100644 --- a/src/modules/staking/services/staking.setter.service.ts +++ b/src/modules/staking/services/staking.setter.service.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; -import { Constants } from '@multiversx/sdk-nestjs-common'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { GenericSetterService } from 'src/services/generics/generic.setter.service'; @@ -16,17 +15,6 @@ export class StakingSetterService extends GenericSetterService { this.baseKey = 'stake'; } - async setPairContractManagedAddress( - stakeAddress: string, - value: string, - ): Promise { - return await this.setData( - this.getCacheKey('pairContractAddress', stakeAddress), - value, - Constants.oneHour(), - ); - } - async setFarmTokenID(stakeAddress: string, value: string): Promise { return await this.setData( this.getCacheKey('farmTokenID', stakeAddress), From 3b64ea0924233e02d00db85991ad179df26a070a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 18:50:06 +0300 Subject: [PATCH 030/313] MEX-404: remove unused transactions from farm staking Signed-off-by: Claudiu Lataretu --- .../services/staking.transactions.service.ts | 64 ---------- .../staking.transactions.service.spec.ts | 116 ------------------ src/modules/staking/staking.resolver.ts | 56 --------- 3 files changed, 236 deletions(-) diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index 8cb075b50..f1612695e 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -226,70 +226,6 @@ export class StakingTransactionService { .toPlainObject(); } - async setPenaltyPercent( - stakeAddress: string, - percent: number, - ): Promise { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - return contract.methodsExplicit - .set_penalty_percent([new BigUIntValue(new BigNumber(percent))]) - .withGasLimit(gasConfig.stake.admin.set_penalty_percent) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); - } - - async setMinimumFarmingEpochs( - stakeAddress: string, - epochs: number, - ): Promise { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - return contract.methodsExplicit - .set_minimum_farming_epochs([ - new BigUIntValue(new BigNumber(epochs)), - ]) - .withGasLimit(gasConfig.stake.admin.set_minimum_farming_epochs) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); - } - - async setBurnGasLimit( - stakeAddress: string, - gasLimit: number, - ): Promise { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - return contract.methodsExplicit - .set_burn_gas_limit([new BigUIntValue(new BigNumber(gasLimit))]) - .withGasLimit(gasConfig.stake.admin.set_burn_gas_limit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); - } - - async setTransferExecGasLimit( - stakeAddress: string, - gasLimit: number, - ): Promise { - const contract = await this.mxProxy.getStakingSmartContract( - stakeAddress, - ); - return contract.methodsExplicit - .set_transfer_exec_gas_limit([ - new BigUIntValue(new BigNumber(gasLimit)), - ]) - .withGasLimit(gasConfig.stake.admin.set_transfer_exec_gas_limit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); - } - async setAddressWhitelist( stakeAddress: string, address: string, diff --git a/src/modules/staking/specs/staking.transactions.service.spec.ts b/src/modules/staking/specs/staking.transactions.service.spec.ts index 642fb3904..88fc133fb 100644 --- a/src/modules/staking/specs/staking.transactions.service.spec.ts +++ b/src/modules/staking/specs/staking.transactions.service.spec.ts @@ -281,122 +281,6 @@ describe('StakingTransactionService', () => { }); }); - it('should get set penalty percent transaction', async () => { - const service = module.get( - StakingTransactionService, - ); - - const transaction = await service.setPenaltyPercent( - Address.Zero().bech32(), - 5, - ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.stake.admin.set_penalty_percent, - data: encodeTransactionData('set_penalty_percent@05'), - chainID: 'T', - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }); - }); - - it('should get set minimum farming epochs transaction', async () => { - const service = module.get( - StakingTransactionService, - ); - - const transaction = await service.setMinimumFarmingEpochs( - Address.Zero().bech32(), - 10, - ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.stake.admin.set_minimum_farming_epochs, - data: encodeTransactionData('set_minimum_farming_epochs@10'), - chainID: 'T', - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }); - }); - - it('should get set burn gas limit transaction', async () => { - const service = module.get( - StakingTransactionService, - ); - - const transaction = await service.setBurnGasLimit( - Address.Zero().bech32(), - 1000000, - ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.stake.admin.set_burn_gas_limit, - data: encodeTransactionData('set_burn_gas_limit@01000000'), - chainID: 'T', - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }); - }); - - it('should get set transfer exec gas limit transaction', async () => { - const service = module.get( - StakingTransactionService, - ); - - const transaction = await service.setTransferExecGasLimit( - Address.Zero().bech32(), - 1000000, - ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.stake.admin.set_transfer_exec_gas_limit, - data: encodeTransactionData('set_transfer_exec_gas_limit@01000000'), - chainID: 'T', - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }); - }); - it('should get add address to whitelist transaction', async () => { const service = module.get( StakingTransactionService, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 9bd8345ca..7c0c454a0 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -207,34 +207,6 @@ export class StakingResolver { ); } - @UseGuards(JwtOrNativeAdminGuard) - @Query(() => TransactionModel) - async setPenaltyPercent( - @Args('farmStakeAddress') farmStakeAddress: string, - @Args('percent') percent: number, - @AuthUser() user: UserAuthResult, - ): Promise { - await this.stakingService.requireOwner(farmStakeAddress, user.address); - return this.stakingTransactionService.setPenaltyPercent( - farmStakeAddress, - percent, - ); - } - - @UseGuards(JwtOrNativeAdminGuard) - @Query(() => TransactionModel) - async setMinimumFarmingEpochs( - @Args('farmStakeAddress') farmStakeAddress: string, - @Args('epochs') epochs: number, - @AuthUser() user: UserAuthResult, - ): Promise { - await this.stakingService.requireOwner(farmStakeAddress, user.address); - return this.stakingTransactionService.setMinimumFarmingEpochs( - farmStakeAddress, - epochs, - ); - } - @UseGuards(JwtOrNativeAdminGuard) @Query(() => TransactionModel) async setPerBlockRewardAmount( @@ -303,34 +275,6 @@ export class StakingResolver { ); } - @UseGuards(JwtOrNativeAdminGuard) - @Query(() => TransactionModel) - async setBurnGasLimit( - @Args('farmStakeAddress') farmStakeAddress: string, - @Args('gasLimit') gasLimit: number, - @AuthUser() user: UserAuthResult, - ): Promise { - await this.stakingService.requireOwner(farmStakeAddress, user.address); - return this.stakingTransactionService.setBurnGasLimit( - farmStakeAddress, - gasLimit, - ); - } - - @UseGuards(JwtOrNativeAdminGuard) - @Query(() => TransactionModel) - async setTransferExecGasLimit( - @Args('farmStakeAddress') farmStakeAddress: string, - @Args('gasLimit') gasLimit: number, - @AuthUser() user: UserAuthResult, - ): Promise { - await this.stakingService.requireOwner(farmStakeAddress, user.address); - return this.stakingTransactionService.setTransferExecGasLimit( - farmStakeAddress, - gasLimit, - ); - } - @UseGuards(JwtOrNativeAdminGuard) @Query(() => TransactionModel) async addAddressToWhitelist( From 1e69156ca389aae7817ac5d8a7fefa41a22c7415 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 18:52:56 +0300 Subject: [PATCH 031/313] MEX-404: update farm staking transactions Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.transactions.service.ts | 8 ++++---- .../staking/specs/staking.transactions.service.spec.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index f1612695e..773e2c175 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -237,7 +237,7 @@ export class StakingTransactionService { if (whitelist) return contract.methodsExplicit - .addAddressToWhitelist([ + .addSCAddressToWhitelist([ new AddressValue(Address.fromString(address)), ]) .withGasLimit(gasConfig.stake.admin.whitelist) @@ -246,7 +246,7 @@ export class StakingTransactionService { .toPlainObject(); return contract.methodsExplicit - .removeAddressFromWhitelist([ + .removeSCAddressFromWhitelist([ new AddressValue(Address.fromString(address)), ]) .withGasLimit(gasConfig.stake.admin.whitelist) @@ -286,7 +286,7 @@ export class StakingTransactionService { stakeAddress, ); return contract.methodsExplicit - .setLocalRolesFarmToken() + .setBurnRoleForAddress() .withGasLimit(gasConfig.stake.admin.setLocalRolesFarmToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -379,7 +379,7 @@ export class StakingTransactionService { .toPlainObject(); return contract.methodsExplicit - .end_produce_rewards() + .endProduceRewards() .withGasLimit(gasConfig.stake.admin.setRewardsState) .withChainID(mxConfig.chainID) .buildTransaction() diff --git a/src/modules/staking/specs/staking.transactions.service.spec.ts b/src/modules/staking/specs/staking.transactions.service.spec.ts index 88fc133fb..07eb621f1 100644 --- a/src/modules/staking/specs/staking.transactions.service.spec.ts +++ b/src/modules/staking/specs/staking.transactions.service.spec.ts @@ -302,7 +302,7 @@ describe('StakingTransactionService', () => { gasPrice: 1000000000, gasLimit: gasConfig.stake.admin.whitelist, data: encodeTransactionData( - 'addAddressToWhitelist@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + 'addSCAddressToWhitelist@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', ), chainID: 'T', version: 1, @@ -334,7 +334,7 @@ describe('StakingTransactionService', () => { gasPrice: 1000000000, gasLimit: gasConfig.stake.admin.whitelist, data: encodeTransactionData( - 'removeAddressFromWhitelist@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + 'removeSCAddressFromWhitelist@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', ), chainID: 'T', version: 1, @@ -454,7 +454,7 @@ describe('StakingTransactionService', () => { senderUsername: undefined, gasPrice: 1000000000, gasLimit: gasConfig.stake.admin.setLocalRolesFarmToken, - data: encodeTransactionData('setLocalRolesFarmToken'), + data: encodeTransactionData('setBurnRoleForAddress'), chainID: 'T', version: 1, options: undefined, @@ -599,7 +599,7 @@ describe('StakingTransactionService', () => { senderUsername: undefined, gasPrice: 1000000000, gasLimit: gasConfig.stake.admin.setRewardsState, - data: encodeTransactionData('end_produce_rewards'), + data: encodeTransactionData('endProduceRewards'), chainID: 'T', version: 1, options: undefined, From 854ce03fdf99c12db71f99c3736e8aba5306835a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Oct 2023 23:48:45 +0300 Subject: [PATCH 032/313] MEX-404: update week timekeeping contract handler to return staking sc Signed-off-by: Claudiu Lataretu --- .../services/week-timekeeping.abi.service.ts | 13 +++++++++++-- .../week-timekeeping/week-timekeeping.module.ts | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/submodules/week-timekeeping/services/week-timekeeping.abi.service.ts b/src/submodules/week-timekeeping/services/week-timekeeping.abi.service.ts index adfb6fea3..1a20d4ea4 100644 --- a/src/submodules/week-timekeeping/services/week-timekeeping.abi.service.ts +++ b/src/submodules/week-timekeeping/services/week-timekeeping.abi.service.ts @@ -7,13 +7,17 @@ import { IWeekTimekeepingAbiService } from '../interfaces'; import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; +import { RemoteConfigGetterService } from 'src/modules/remote-config/remote-config.getter.service'; @Injectable() export class WeekTimekeepingAbiService extends GenericAbiService implements IWeekTimekeepingAbiService { - constructor(protected readonly mxProxy: MXProxyService) { + constructor( + protected readonly mxProxy: MXProxyService, + private readonly remoteConfig: RemoteConfigGetterService, + ) { super(mxProxy); } @@ -57,13 +61,18 @@ export class WeekTimekeepingAbiService return response.firstValue.valueOf().toNumber(); } - private getContractHandler( + private async getContractHandler( contractAddress: string, ): Promise { if (scAddress.feesCollector === contractAddress) { return this.mxProxy.getFeesCollectorContract(); } + const stakingAddresses = await this.remoteConfig.getStakingAddresses(); + if (stakingAddresses.includes(contractAddress)) { + return this.mxProxy.getStakingSmartContract(contractAddress); + } + return this.mxProxy.getFarmSmartContract(contractAddress); } } diff --git a/src/submodules/week-timekeeping/week-timekeeping.module.ts b/src/submodules/week-timekeeping/week-timekeeping.module.ts index 1e799b3a4..f6d39b9c7 100644 --- a/src/submodules/week-timekeeping/week-timekeeping.module.ts +++ b/src/submodules/week-timekeeping/week-timekeeping.module.ts @@ -7,6 +7,7 @@ import { WeekTimekeepingResolver } from './week-timekeeping.resolver'; import { FarmModuleV2 } from 'src/modules/farm/v2/farm.v2.module'; import { FeesCollectorModule } from 'src/modules/fees-collector/fees-collector.module'; import { ContextModule } from 'src/services/context/context.module'; +import { RemoteConfigModule } from 'src/modules/remote-config/remote-config.module'; @Module({ imports: [ @@ -14,6 +15,7 @@ import { ContextModule } from 'src/services/context/context.module'; ContextModule, forwardRef(() => FarmModuleV2), forwardRef(() => FeesCollectorModule), + RemoteConfigModule, ], providers: [ ApiConfigService, From 7f676e872a30f78785a1f547f9734cd7f5d5fa01 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 14:21:04 +0300 Subject: [PATCH 033/313] MEX-405: add farm staking into weekly rewards splitting contract handler Signed-off-by: Claudiu Lataretu --- .../services/weekly-rewards-splitting.abi.service.ts | 9 ++++++++- .../weekly-rewards-splitting.module.ts | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts index de9348c54..0c87267ba 100644 --- a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts +++ b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts @@ -20,6 +20,7 @@ import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { WeekTimekeepingComputeService } from 'src/submodules/week-timekeeping/services/week-timekeeping.compute.service'; import { IWeeklyRewardsSplittingAbiService } from '../interfaces'; +import { RemoteConfigGetterService } from 'src/modules/remote-config/remote-config.getter.service'; @Injectable() export class WeeklyRewardsSplittingAbiService @@ -28,6 +29,7 @@ export class WeeklyRewardsSplittingAbiService { constructor( protected readonly mxProxy: MXProxyService, + private readonly remoteConfig: RemoteConfigGetterService, private readonly weekTimekeepCompute: WeekTimekeepingComputeService, ) { super(mxProxy); @@ -291,13 +293,18 @@ export class WeeklyRewardsSplittingAbiService return response.firstValue.valueOf().toFixed(); } - private getContractHandler( + private async getContractHandler( contractAddress: string, ): Promise { if (scAddress.feesCollector === contractAddress) { return this.mxProxy.getFeesCollectorContract(); } + const stakingAddresses = await this.remoteConfig.getStakingAddresses(); + if (stakingAddresses.includes(contractAddress)) { + return this.mxProxy.getStakingSmartContract(contractAddress); + } + return this.mxProxy.getFarmSmartContract(contractAddress); } } diff --git a/src/submodules/weekly-rewards-splitting/weekly-rewards-splitting.module.ts b/src/submodules/weekly-rewards-splitting/weekly-rewards-splitting.module.ts index 70deda38c..723851d21 100644 --- a/src/submodules/weekly-rewards-splitting/weekly-rewards-splitting.module.ts +++ b/src/submodules/weekly-rewards-splitting/weekly-rewards-splitting.module.ts @@ -14,6 +14,7 @@ import { FarmModuleV2 } from '../../modules/farm/v2/farm.v2.module'; import { ContextModule } from '../../services/context/context.module'; import { FeesCollectorModule } from 'src/modules/fees-collector/fees-collector.module'; import { WeeklyRewardsSplittingSetterService } from './services/weekly.rewarrds.splitting.setter.service'; +import { RemoteConfigModule } from 'src/modules/remote-config/remote-config.module'; @Module({ imports: [ @@ -26,6 +27,7 @@ import { WeeklyRewardsSplittingSetterService } from './services/weekly.rewarrds. forwardRef(() => FeesCollectorModule), forwardRef(() => ContextModule), forwardRef(() => WeekTimekeepingModule), + RemoteConfigModule, ], providers: [ ApiConfigService, From f1de9698f160019aeedf0e6c4339db5098b5f026 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 14:29:09 +0300 Subject: [PATCH 034/313] MEX-405: integrate weekly rewards splitting submodule - added new fileds into farm staking model - added resolvers for new fields - added get methods for new fields Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 13 +++ .../staking/services/staking.abi.service.ts | 99 +++++++++++++++++++ .../services/staking.compute.service.ts | 56 +++++++++++ src/modules/staking/staking.module.ts | 2 + src/modules/staking/staking.resolver.ts | 66 +++++++++++++ 5 files changed, 236 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 7b9906d86..c3c946599 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -3,6 +3,7 @@ import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { StakingTokenAttributesModel } from './stakingTokenAttributes.model'; import { WeekTimekeepingModel } from 'src/submodules/week-timekeeping/models/week-timekeeping.model'; +import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; @ObjectType() export class StakingModel { @@ -42,6 +43,18 @@ export class StakingModel { state: string; @Field({ description: 'Timekeeping for boosted rewards' }) time: WeekTimekeepingModel; + @Field(() => [GlobalInfoByWeekModel], { + description: 'Global info for boosted rewards', + }) + boosterRewards: [GlobalInfoByWeekModel]; + @Field() + lastGlobalUpdateWeek: number; + @Field() + energyFactoryAddress: string; + @Field() + undistributedBoostedRewards: string; + @Field() + undistributedBoostedRewardsClaimed: string; constructor(init?: Partial) { Object.assign(this, init); diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index d76916c3d..bb28db3f0 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -5,6 +5,7 @@ import { BytesValue, Interaction, TypedValue, + U32Value, } from '@multiversx/sdk-core'; import { Injectable } from '@nestjs/common'; import { BigNumber } from 'bignumber.js'; @@ -431,4 +432,102 @@ export class StakingAbiService const response = await this.getGenericData(interaction); return response.firstValue.valueOf().toString(); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async energyFactoryAddress(stakeAddress: string): Promise { + return await this.getEnergyFactoryAddressRaw(stakeAddress); + } + + async getEnergyFactoryAddressRaw(stakeAddress: string): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); + + const interaction: Interaction = + contract.methodsExplicit.getEnergyFactoryAddress(); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().bech32(); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async undistributedBoostedRewards(stakeAddress: string): Promise { + return await this.getUndistributedBoostedRewardsRaw(stakeAddress); + } + + async getUndistributedBoostedRewardsRaw( + stakeAddress: string, + ): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); + + const interaction: Interaction = + contract.methodsExplicit.getUndistributedBoostedRewards(); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toFixed(); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async lastUndistributedBoostedRewardsCollectWeek( + stakeAddress: string, + ): Promise { + return this.gatewayService.getSCStorageKey( + stakeAddress, + 'lastUndistributedBoostedRewardsCollectWeek', + ); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async remainingBoostedRewardsToDistribute( + stakeAddress: string, + week: number, + ): Promise { + return await this.getRemainingBoostedRewardsToDistributeRaw( + stakeAddress, + week, + ); + } + + async getRemainingBoostedRewardsToDistributeRaw( + stakeAddress: string, + week: number, + ): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); + const interaction: Interaction = + contract.methodsExplicit.getRemainingBoostedRewardsToDistribute([ + new U32Value(new BigNumber(week)), + ]); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toFixed(); + } } diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 295a4904b..c7035f829 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -296,4 +296,60 @@ export class StakingComputeService { minutes: Math.floor(frequencyMinutes), }); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async undistributedBoostedRewards( + scAddress: string, + currentWeek: number, + ): Promise { + const amount = await this.undistributedBoostedRewardsRaw( + scAddress, + currentWeek, + ); + return amount.integerValue().toFixed(); + } + + async undistributedBoostedRewardsRaw( + scAddress: string, + currentWeek: number, + ): Promise { + const [ + undistributedBoostedRewards, + lastUndistributedBoostedRewardsCollectWeek, + ] = await Promise.all([ + this.stakingAbi.undistributedBoostedRewards(scAddress), + this.stakingAbi.lastUndistributedBoostedRewardsCollectWeek( + scAddress, + ), + ]); + + const firstWeek = lastUndistributedBoostedRewardsCollectWeek + 1; + const lastWeek = currentWeek - constantsConfig.USER_MAX_CLAIM_WEEKS - 1; + if (firstWeek > lastWeek) { + return new BigNumber(undistributedBoostedRewards); + } + const promises = []; + for (let week = firstWeek; week <= lastWeek; week++) { + promises.push( + this.stakingAbi.remainingBoostedRewardsToDistribute( + scAddress, + week, + ), + ); + } + const remainingRewards = await Promise.all(promises); + const totalRemainingRewards = remainingRewards.reduce((acc, curr) => { + return new BigNumber(acc).plus(curr); + }); + return new BigNumber(undistributedBoostedRewards).plus( + totalRemainingRewards, + ); + } } diff --git a/src/modules/staking/staking.module.ts b/src/modules/staking/staking.module.ts index 043035935..1a0d94881 100644 --- a/src/modules/staking/staking.module.ts +++ b/src/modules/staking/staking.module.ts @@ -11,6 +11,7 @@ import { StakingSetterService } from './services/staking.setter.service'; import { StakingTransactionService } from './services/staking.transactions.service'; import { StakingResolver } from './staking.resolver'; import { WeekTimekeepingModule } from 'src/submodules/week-timekeeping/week-timekeeping.module'; +import { WeeklyRewardsSplittingModule } from 'src/submodules/weekly-rewards-splitting/weekly-rewards-splitting.module'; @Module({ imports: [ @@ -20,6 +21,7 @@ import { WeekTimekeepingModule } from 'src/submodules/week-timekeeping/week-time RemoteConfigModule, TokenModule, WeekTimekeepingModule, + WeeklyRewardsSplittingModule, ], providers: [ StakingAbiService, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 7c0c454a0..808106b2f 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -27,6 +27,9 @@ import { StakingComputeService } from './services/staking.compute.service'; import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { WeekTimekeepingModel } from 'src/submodules/week-timekeeping/models/week-timekeeping.model'; import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; +import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; +import { constantsConfig } from 'src/config'; +import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; @Resolver(() => StakingModel) export class StakingResolver { @@ -36,6 +39,7 @@ export class StakingResolver { private readonly stakingCompute: StakingComputeService, private readonly stakingTransactionService: StakingTransactionService, private readonly weekTimekeepingAbi: WeekTimekeepingAbiService, + private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, ) {} @ResolveField() @@ -129,6 +133,68 @@ export class StakingResolver { }); } + @ResolveField() + async boosterRewards( + @Parent() parent: StakingModel, + ): Promise { + const modelsList = []; + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + parent.address, + ); + for ( + let week = currentWeek - constantsConfig.USER_MAX_CLAIM_WEEKS; + week <= currentWeek; + week++ + ) { + if (week < 1) { + continue; + } + modelsList.push( + new GlobalInfoByWeekModel({ + scAddress: parent.address, + week: week, + }), + ); + } + return modelsList; + } + + @ResolveField() + async lastGlobalUpdateWeek( + @Parent() parent: StakingModel, + ): Promise { + return this.weeklyRewardsSplittingAbi.lastGlobalUpdateWeek( + parent.address, + ); + } + + @ResolveField() + async energyFactoryAddress( + @Parent() parent: StakingModel, + ): Promise { + return this.stakingAbi.energyFactoryAddress(parent.address); + } + + @ResolveField() + async undistributedBoostedRewards( + @Parent() parent: StakingModel, + ): Promise { + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + parent.address, + ); + return this.stakingCompute.undistributedBoostedRewards( + parent.address, + currentWeek, + ); + } + + @ResolveField() + async undistributedBoostedRewardsClaimed( + @Parent() parent: StakingModel, + ): Promise { + return this.stakingAbi.undistributedBoostedRewards(parent.address); + } + @Query(() => String) async getLastErrorMessage( @Args('stakeAddress') stakeAddress: string, From aefd6290867daacccfedfadd56267aae697587ab Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 15:09:54 +0300 Subject: [PATCH 035/313] MEX-406: add query for user total stake position Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.abi.service.ts | 39 +++++++++++++++++++ .../services/staking.setter.service.ts | 17 ++++++++ src/modules/staking/staking.resolver.ts | 16 ++++++++ .../staking/validators/stake.address.ts | 24 ++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 src/modules/staking/validators/stake.address.ts diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index bb28db3f0..7cf819466 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -4,6 +4,7 @@ import { BigUIntValue, BytesValue, Interaction, + ReturnCode, TypedValue, U32Value, } from '@multiversx/sdk-core'; @@ -530,4 +531,42 @@ export class StakingAbiService const response = await this.getGenericData(interaction); return response.firstValue.valueOf().toFixed(); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async userTotalStakePosition( + stakeAddress: string, + userAddress: string, + ): Promise { + return await this.getUserTotalStakePositionRaw( + stakeAddress, + userAddress, + ); + } + + async getUserTotalStakePositionRaw( + stakeAddress: string, + userAddress: string, + ): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); + const interaction: Interaction = + contract.methodsExplicit.getUserTotalFarmPosition([ + new AddressValue(Address.fromString(userAddress)), + ]); + const response = await this.getGenericData(interaction); + + if (response.returnCode.equals(ReturnCode.FunctionNotFound)) { + return '0'; + } + + return response.firstValue.valueOf().total_farm_position.toFixed(); + } } diff --git a/src/modules/staking/services/staking.setter.service.ts b/src/modules/staking/services/staking.setter.service.ts index e26429b00..71c23709e 100644 --- a/src/modules/staking/services/staking.setter.service.ts +++ b/src/modules/staking/services/staking.setter.service.ts @@ -176,4 +176,21 @@ export class StakingSetterService extends GenericSetterService { CacheTtlInfo.ContractState.localTtl, ); } + + async setUserTotalStakePosition( + stakeAddress: string, + userAddress: string, + value: string, + ): Promise { + return this.setData( + this.getCacheKey( + 'userTotalFarmPosition', + stakeAddress, + userAddress, + ), + value, + CacheTtlInfo.ContractInfo.remoteTtl, + CacheTtlInfo.ContractInfo.localTtl, + ); + } } diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 808106b2f..1066ee263 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -30,6 +30,7 @@ import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/servi import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { constantsConfig } from 'src/config'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; +import { StakeAddressValidationPipe } from './validators/stake.address'; @Resolver(() => StakingModel) export class StakingResolver { @@ -242,6 +243,21 @@ export class StakingResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => String, { + description: + 'Returns the total staked position of the user in the staking contract', + }) + async userTotalStakePosition( + @Args('stakeAddress', StakeAddressValidationPipe) stakeAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.stakingAbi.userTotalStakePosition( + stakeAddress, + user.address, + ); + } + @Query(() => [StakingModel]) async stakingFarms(): Promise { return this.stakingService.getFarmsStaking(); diff --git a/src/modules/staking/validators/stake.address.ts b/src/modules/staking/validators/stake.address.ts new file mode 100644 index 000000000..857c1c856 --- /dev/null +++ b/src/modules/staking/validators/stake.address.ts @@ -0,0 +1,24 @@ +import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; +import { UserInputError } from '@nestjs/apollo'; +import { RemoteConfigGetterService } from 'src/modules/remote-config/remote-config.getter.service'; +import { Address } from '@multiversx/sdk-core/out'; + +@Injectable() +export class StakeAddressValidationPipe implements PipeTransform { + constructor(private readonly remoteConfig: RemoteConfigGetterService) {} + + async transform(value: string, metadata: ArgumentMetadata) { + let address: Address; + try { + address = Address.fromBech32(value); + } catch (error) { + throw new UserInputError('Invalid address'); + } + const stakingAddresses = await this.remoteConfig.getStakingAddresses(); + if (!stakingAddresses.includes(address.bech32())) { + throw new UserInputError('Invalid staking address'); + } + + return value; + } +} From 71022167e42689201dc9c1a9ecb2dd3ea0d5db5e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 15:18:34 +0300 Subject: [PATCH 036/313] MEX-376: add validator for proxy address Signed-off-by: Claudiu Lataretu --- .../proxy/proxy.transaction.resolver.ts | 3 ++- .../validators/proxy.address.validator.ts | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/modules/proxy/validators/proxy.address.validator.ts diff --git a/src/modules/proxy/proxy.transaction.resolver.ts b/src/modules/proxy/proxy.transaction.resolver.ts index 6f0fe81af..3e0d5fc73 100644 --- a/src/modules/proxy/proxy.transaction.resolver.ts +++ b/src/modules/proxy/proxy.transaction.resolver.ts @@ -24,6 +24,7 @@ import { InputTokenModel } from 'src/models/inputToken.model'; import { LiquidityTokensValidationPipe } from './validators/add.liquidity.input.validator'; import { ProxyService } from './services/proxy.service'; import { scAddress } from 'src/config'; +import { ProxyAddressValidationPipe } from './validators/proxy.address.validator'; @Resolver() export class ProxyTransactionResolver { @@ -202,7 +203,7 @@ export class ProxyTransactionResolver { 'Generate transactions to initialize the total farm positions for a user', }) async migrateTotalFarmPositionsProxy( - @Args('proxyAddress') proxyAddress: string, + @Args('proxyAddress', ProxyAddressValidationPipe) proxyAddress: string, @AuthUser() user: UserAuthResult, ): Promise { return this.transactionsProxyFarmService.migrateTotalFarmPosition( diff --git a/src/modules/proxy/validators/proxy.address.validator.ts b/src/modules/proxy/validators/proxy.address.validator.ts new file mode 100644 index 000000000..8392864fe --- /dev/null +++ b/src/modules/proxy/validators/proxy.address.validator.ts @@ -0,0 +1,25 @@ +import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; +import { UserInputError } from '@nestjs/apollo'; +import { Address } from '@multiversx/sdk-core/out'; +import { scAddress } from 'src/config'; + +@Injectable() +export class ProxyAddressValidationPipe implements PipeTransform { + async transform(value: string, metadata: ArgumentMetadata) { + let address: Address; + try { + address = Address.fromBech32(value); + } catch (error) { + throw new UserInputError('Invalid address format'); + } + + const proxAddresses: string[] = Object.values( + scAddress.proxyDexAddress, + ); + if (!proxAddresses.includes(address.bech32())) { + throw new UserInputError('Invalid proxy address'); + } + + return value; + } +} From 602d5320d6f79947b6578611ec10de17ec8d3e88 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 15:20:37 +0300 Subject: [PATCH 037/313] MEX-406: rename stake address validator file Signed-off-by: Claudiu Lataretu --- src/modules/staking/staking.resolver.ts | 2 +- .../validators/{stake.address.ts => stake.address.validator.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/modules/staking/validators/{stake.address.ts => stake.address.validator.ts} (100%) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 1066ee263..69875ce97 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -30,7 +30,7 @@ import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/servi import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { constantsConfig } from 'src/config'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; -import { StakeAddressValidationPipe } from './validators/stake.address'; +import { StakeAddressValidationPipe } from './validators/stake.address.validator'; @Resolver(() => StakingModel) export class StakingResolver { diff --git a/src/modules/staking/validators/stake.address.ts b/src/modules/staking/validators/stake.address.validator.ts similarity index 100% rename from src/modules/staking/validators/stake.address.ts rename to src/modules/staking/validators/stake.address.validator.ts From 1c657e72631be9324aa699d08dfd200bfc844e41 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 23:00:09 +0300 Subject: [PATCH 038/313] MEX-406: add staking events handler service Signed-off-by: Claudiu Lataretu --- .../handlers/staking.handler.service.ts | 89 +++++++++++++++++++ src/modules/rabbitmq/rabbitmq.consumer.ts | 45 +++++++++- src/modules/rabbitmq/rabbitmq.module.ts | 4 + 3 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 src/modules/rabbitmq/handlers/staking.handler.service.ts diff --git a/src/modules/rabbitmq/handlers/staking.handler.service.ts b/src/modules/rabbitmq/handlers/staking.handler.service.ts new file mode 100644 index 000000000..9ef9ef42a --- /dev/null +++ b/src/modules/rabbitmq/handlers/staking.handler.service.ts @@ -0,0 +1,89 @@ +import { + ClaimRewardsEventV2, + EnterFarmEventV2, + ExitFarmEventV2, + RawEvent, +} from '@multiversx/sdk-exchange'; +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { RedisPubSub } from 'graphql-redis-subscriptions'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; +import { StakingSetterService } from 'src/modules/staking/services/staking.setter.service'; +import { PUB_SUB } from 'src/services/redis.pubSub.module'; + +@Injectable() +export class StakingHandlerService { + constructor( + private readonly stakingAbi: StakingAbiService, + private readonly stakingSetter: StakingSetterService, + @Inject(PUB_SUB) private pubSub: RedisPubSub, + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + ) {} + + async handleStakeEvent(rawEvent: RawEvent): Promise { + const event = new EnterFarmEventV2(rawEvent); + const userTotalFarmPosition = + await this.stakingAbi.getUserTotalStakePositionRaw( + event.address, + event.decodedTopics.caller.bech32(), + ); + const cacheKeys = await Promise.all([ + this.stakingSetter.setFarmTokenSupply( + event.address, + event.farmSupply.toFixed(), + ), + this.stakingSetter.setUserTotalStakePosition( + event.address, + event.decodedTopics.caller.bech32(), + userTotalFarmPosition, + ), + ]); + await this.deleteCacheKeys(cacheKeys); + } + + async handleUnstakeEvent(rawEvent: RawEvent): Promise { + const event = new ExitFarmEventV2(rawEvent); + const userTotalFarmPosition = + await this.stakingAbi.getUserTotalStakePositionRaw( + event.address, + event.decodedTopics.caller.bech32(), + ); + const cacheKeys = await Promise.all([ + this.stakingSetter.setFarmTokenSupply( + event.address, + event.farmSupply.toFixed(), + ), + this.stakingSetter.setUserTotalStakePosition( + event.address, + event.decodedTopics.caller.bech32(), + userTotalFarmPosition, + ), + ]); + await this.deleteCacheKeys(cacheKeys); + } + + async handleClaimRewardsEvent(rawEvent: RawEvent): Promise { + const event = new ClaimRewardsEventV2(rawEvent); + const userTotalFarmPosition = + await this.stakingAbi.getUserTotalStakePositionRaw( + event.address, + event.decodedTopics.caller.bech32(), + ); + const cacheKeys = await Promise.all([ + this.stakingSetter.setFarmTokenSupply( + event.address, + event.farmSupply.toFixed(), + ), + this.stakingSetter.setUserTotalStakePosition( + event.address, + event.decodedTopics.caller.bech32(), + userTotalFarmPosition, + ), + ]); + await this.deleteCacheKeys(cacheKeys); + } + + private async deleteCacheKeys(invalidatedKeys: string[]) { + await this.pubSub.publish('deleteCacheKeys', invalidatedKeys); + } +} diff --git a/src/modules/rabbitmq/rabbitmq.consumer.ts b/src/modules/rabbitmq/rabbitmq.consumer.ts index 49976d624..7bec19faf 100644 --- a/src/modules/rabbitmq/rabbitmq.consumer.ts +++ b/src/modules/rabbitmq/rabbitmq.consumer.ts @@ -64,6 +64,8 @@ import { RouterAbiService } from '../router/services/router.abi.service'; import { EscrowHandlerService } from './handlers/escrow.handler.service'; import { governanceContractsAddresses } from '../../utils/governance'; import { GovernanceHandlerService } from './handlers/governance.handler.service'; +import { RemoteConfigGetterService } from '../remote-config/remote-config.getter.service'; +import { StakingHandlerService } from './handlers/staking.handler.service'; @Injectable() export class RabbitMqConsumer { @@ -75,6 +77,7 @@ export class RabbitMqConsumer { private readonly liquidityHandler: LiquidityHandler, private readonly swapHandler: SwapEventHandler, private readonly wsFarmHandler: FarmHandlerService, + private readonly stakingHandler: StakingHandlerService, private readonly wsProxyHandler: RabbitMQProxyHandlerService, private readonly routerHandler: RouterHandlerService, private readonly wsEsdtTokenHandler: RabbitMQEsdtTokenHandlerService, @@ -87,6 +90,7 @@ export class RabbitMqConsumer { private readonly escrowHandler: EscrowHandlerService, private readonly analyticsWrite: AnalyticsWriteService, private readonly governanceHandler: GovernanceHandlerService, + private readonly remoteConfig: RemoteConfigGetterService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} @@ -165,14 +169,36 @@ export class RabbitMqConsumer { this.updateIngestData(eventData); break; case FARM_EVENTS.ENTER_FARM: - await this.wsFarmHandler.handleEnterFarmEvent(rawEvent); + let isStakingAddress = await this.isStakingAddress( + rawEvent.address, + ); + if (isStakingAddress) { + await this.stakingHandler.handleStakeEvent(rawEvent); + } else { + await this.wsFarmHandler.handleEnterFarmEvent(rawEvent); + } break; case FARM_EVENTS.EXIT_FARM: - await this.wsFarmHandler.handleExitFarmEvent(rawEvent); - + isStakingAddress = await this.isStakingAddress( + rawEvent.address, + ); + if (isStakingAddress) { + await this.stakingHandler.handleUnstakeEvent(rawEvent); + } else { + await this.wsFarmHandler.handleExitFarmEvent(rawEvent); + } break; case FARM_EVENTS.CLAIM_REWARDS: - await this.wsFarmHandler.handleRewardsEvent(rawEvent); + isStakingAddress = await this.isStakingAddress( + rawEvent.address, + ); + if (isStakingAddress) { + await this.stakingHandler.handleClaimRewardsEvent( + rawEvent, + ); + } else { + await this.wsFarmHandler.handleRewardsEvent(rawEvent); + } break; case FARM_EVENTS.COMPOUND_REWARDS: await this.wsFarmHandler.handleRewardsEvent(rawEvent); @@ -335,6 +361,9 @@ export class RabbitMqConsumer { this.filterAddresses.push(scAddress.tokenUnstake); this.filterAddresses.push(scAddress.escrow); this.filterAddresses.push(...governanceContractsAddresses()); + + const stakeAddresses = await this.remoteConfig.getStakingAddresses(); + this.filterAddresses.push(...stakeAddresses); } private isFilteredAddress(address: string): boolean { @@ -345,6 +374,14 @@ export class RabbitMqConsumer { ); } + private async isStakingAddress(address: string): Promise { + const stakeAddresses = await this.remoteConfig.getStakingAddresses(); + return ( + stakeAddresses.find((stakeAddress) => address === stakeAddress) !== + undefined + ); + } + private async updateIngestData(eventData: any[]): Promise { for (const series of Object.keys(eventData)) { if (this.data[series] === undefined) { diff --git a/src/modules/rabbitmq/rabbitmq.module.ts b/src/modules/rabbitmq/rabbitmq.module.ts index 7151a4b1b..557610628 100644 --- a/src/modules/rabbitmq/rabbitmq.module.ts +++ b/src/modules/rabbitmq/rabbitmq.module.ts @@ -37,6 +37,8 @@ import { EscrowModule } from '../escrow/escrow.module'; import { GovernanceHandlerService } from './handlers/governance.handler.service'; import { GovernanceModule } from '../governance/governance.module'; import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; +import { RemoteConfigModule } from '../remote-config/remote-config.module'; +import { StakingHandlerService } from './handlers/staking.handler.service'; @Module({ imports: [ @@ -61,6 +63,7 @@ import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; WeeklyRewardsSplittingModule, GovernanceModule, EscrowModule, + RemoteConfigModule, ], providers: [ RabbitMqConsumer, @@ -79,6 +82,7 @@ import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; WeeklyRewardsSplittingHandlerService, GovernanceHandlerService, EscrowHandlerService, + StakingHandlerService, ], }) export class RabbitMqModule { From f18c0c5c474e2f93577f6f0e3a1021dba195ed33 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 23:00:51 +0300 Subject: [PATCH 039/313] MEX-406: fix get user total stake position Signed-off-by: Claudiu Lataretu --- src/modules/staking/services/staking.abi.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 7cf819466..76138f61a 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -563,7 +563,10 @@ export class StakingAbiService ]); const response = await this.getGenericData(interaction); - if (response.returnCode.equals(ReturnCode.FunctionNotFound)) { + if ( + response.returnCode.equals(ReturnCode.FunctionNotFound) || + response.returnCode.equals(ReturnCode.UserError) + ) { return '0'; } From 518aa7832968f34197f2ca17f4937b3cd49481b9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 23:36:49 +0300 Subject: [PATCH 040/313] MEX-407: add boosted yields configs fields to staking model Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 7 +++ .../staking/services/staking.abi.service.ts | 61 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 15 +++++ 3 files changed, 83 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index c3c946599..fe86736e1 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -4,6 +4,7 @@ import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { StakingTokenAttributesModel } from './stakingTokenAttributes.model'; import { WeekTimekeepingModel } from 'src/submodules/week-timekeeping/models/week-timekeeping.model'; import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; +import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; @ObjectType() export class StakingModel { @@ -41,6 +42,12 @@ export class StakingModel { lockedAssetFactoryManagedAddress: string; @Field() state: string; + @Field(() => Int, { description: 'The percentage of boosted rewards' }) + boostedYieldsRewardsPercenatage: number; + @Field(() => BoostedYieldsFactors, { + description: 'Factors used to compute boosted rewards', + }) + boostedYieldsFactors: BoostedYieldsFactors; @Field({ description: 'Timekeeping for boosted rewards' }) time: WeekTimekeepingModel; @Field(() => [GlobalInfoByWeekModel], { diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 76138f61a..56cb7f707 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -18,6 +18,7 @@ import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { Constants } from '@multiversx/sdk-nestjs-common'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { IStakingAbiService } from './interfaces'; +import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; @Injectable() export class StakingAbiService @@ -457,6 +458,66 @@ export class StakingAbiService return response.firstValue.valueOf().bech32(); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async boostedYieldsRewardsPercenatage( + stakeAddress: string, + ): Promise { + return await this.getBoostedYieldsRewardsPercenatageRaw(stakeAddress); + } + + async getBoostedYieldsRewardsPercenatageRaw( + stakeAddress: string, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(stakeAddress); + + const interaction: Interaction = + contract.methodsExplicit.getBoostedYieldsRewardsPercentage(); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toNumber(); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async boostedYieldsFactors( + stakeAddress: string, + ): Promise { + return await this.getBoostedYieldsFactorsRaw(stakeAddress); + } + + async getBoostedYieldsFactorsRaw( + stakeAddress: string, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(stakeAddress); + const interaction: Interaction = + contract.methodsExplicit.getBoostedYieldsFactors(); + const response = await this.getGenericData(interaction); + const rawBoostedYieldsFactors = response.firstValue.valueOf(); + return new BoostedYieldsFactors({ + maxRewardsFactor: + rawBoostedYieldsFactors.max_rewards_factor.toFixed(), + userRewardsEnergy: + rawBoostedYieldsFactors.user_rewards_energy_const.toFixed(), + userRewardsFarm: + rawBoostedYieldsFactors.user_rewards_farm_const.toFixed(), + minEnergyAmount: + rawBoostedYieldsFactors.min_energy_amount.toFixed(), + minFarmAmount: rawBoostedYieldsFactors.min_farm_amount.toFixed(), + }); + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 69875ce97..03dcc5210 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -31,6 +31,7 @@ import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/m import { constantsConfig } from 'src/config'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { StakeAddressValidationPipe } from './validators/stake.address.validator'; +import { BoostedYieldsFactors } from '../farm/models/farm.v2.model'; @Resolver(() => StakingModel) export class StakingResolver { @@ -176,6 +177,20 @@ export class StakingResolver { return this.stakingAbi.energyFactoryAddress(parent.address); } + @ResolveField() + async boostedYieldsRewardsPercenatage( + @Parent() parent: StakingModel, + ): Promise { + return this.stakingAbi.boostedYieldsRewardsPercenatage(parent.address); + } + + @ResolveField() + async boostedYieldsFactors( + @Parent() parent: StakingModel, + ): Promise { + return this.stakingAbi.boostedYieldsFactors(parent.address); + } + @ResolveField() async undistributedBoostedRewards( @Parent() parent: StakingModel, From f064e713a394cc1646a7bb31a977c0f5f4d5308e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 23:44:39 +0300 Subject: [PATCH 041/313] MEX-407: add accumulated rewards for week field into staking model Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 2 ++ .../staking/services/staking.abi.service.ts | 28 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 14 ++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index fe86736e1..41a2b48f2 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -58,6 +58,8 @@ export class StakingModel { lastGlobalUpdateWeek: number; @Field() energyFactoryAddress: string; + @Field({ description: 'Accumulated boosted rewards for specific week' }) + accumulatedRewardsForWeek: string; @Field() undistributedBoostedRewards: string; @Field() diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 56cb7f707..4091b2d83 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -518,6 +518,34 @@ export class StakingAbiService }); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async accumulatedRewardsForWeek( + stakeAddress: string, + week: number, + ): Promise { + return await this.getAccumulatedRewardsForWeekRaw(stakeAddress, week); + } + + async getAccumulatedRewardsForWeekRaw( + stakeAddress: string, + week: number, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(stakeAddress); + const interaction: Interaction = + contract.methodsExplicit.getAccumulatedRewardsForWeek([ + new U32Value(new BigNumber(week)), + ]); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().integerValue().toFixed(); + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 03dcc5210..78a5510c1 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -191,6 +191,20 @@ export class StakingResolver { return this.stakingAbi.boostedYieldsFactors(parent.address); } + @ResolveField() + async accumulatedRewardsForWeek( + @Parent() parent: StakingModel, + @Args('week', { nullable: true }) week: number, + ): Promise { + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + parent.address, + ); + return this.stakingAbi.accumulatedRewardsForWeek( + parent.address, + week ?? currentWeek, + ); + } + @ResolveField() async undistributedBoostedRewards( @Parent() parent: StakingModel, From 25746905d4de3947cce63c8d867ed802509f2fc2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Oct 2023 23:47:44 +0300 Subject: [PATCH 042/313] MEX-407: add staking shard get method Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.abi.service.ts | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 4091b2d83..c31f2b7ba 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -19,6 +19,7 @@ import { Constants } from '@multiversx/sdk-nestjs-common'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { IStakingAbiService } from './interfaces'; import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; @Injectable() export class StakingAbiService @@ -28,6 +29,7 @@ export class StakingAbiService constructor( protected readonly mxProxy: MXProxyService, private readonly gatewayService: MXGatewayService, + private readonly apiService: MXApiService, ) { super(mxProxy); } @@ -475,7 +477,9 @@ export class StakingAbiService async getBoostedYieldsRewardsPercenatageRaw( stakeAddress: string, ): Promise { - const contract = await this.mxProxy.getFarmSmartContract(stakeAddress); + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); const interaction: Interaction = contract.methodsExplicit.getBoostedYieldsRewardsPercentage(); @@ -500,7 +504,9 @@ export class StakingAbiService async getBoostedYieldsFactorsRaw( stakeAddress: string, ): Promise { - const contract = await this.mxProxy.getFarmSmartContract(stakeAddress); + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); const interaction: Interaction = contract.methodsExplicit.getBoostedYieldsFactors(); const response = await this.getGenericData(interaction); @@ -537,7 +543,9 @@ export class StakingAbiService stakeAddress: string, week: number, ): Promise { - const contract = await this.mxProxy.getFarmSmartContract(stakeAddress); + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); const interaction: Interaction = contract.methodsExplicit.getAccumulatedRewardsForWeek([ new U32Value(new BigNumber(week)), @@ -661,4 +669,20 @@ export class StakingAbiService return response.firstValue.valueOf().total_farm_position.toFixed(); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async stakingShard(stakeAddress: string): Promise { + return await this.getStakingShardRaw(stakeAddress); + } + + async getStakingShardRaw(stakeAddress: string): Promise { + return (await this.apiService.getAccountStats(stakeAddress)).shard; + } } From 943164b8c9d2f5a032f9200374c3397a40f42e7d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 12 Oct 2023 14:55:53 +0300 Subject: [PATCH 043/313] MEX-407: fix rabbitmq consumer for staking events Signed-off-by: Claudiu Lataretu --- src/modules/rabbitmq/rabbitmq.consumer.ts | 15 +++------------ src/modules/rabbitmq/rabbitmq.module.ts | 2 ++ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/modules/rabbitmq/rabbitmq.consumer.ts b/src/modules/rabbitmq/rabbitmq.consumer.ts index 7bec19faf..b84c44e7d 100644 --- a/src/modules/rabbitmq/rabbitmq.consumer.ts +++ b/src/modules/rabbitmq/rabbitmq.consumer.ts @@ -169,30 +169,21 @@ export class RabbitMqConsumer { this.updateIngestData(eventData); break; case FARM_EVENTS.ENTER_FARM: - let isStakingAddress = await this.isStakingAddress( - rawEvent.address, - ); - if (isStakingAddress) { + if (await this.isStakingAddress(rawEvent.address)) { await this.stakingHandler.handleStakeEvent(rawEvent); } else { await this.wsFarmHandler.handleEnterFarmEvent(rawEvent); } break; case FARM_EVENTS.EXIT_FARM: - isStakingAddress = await this.isStakingAddress( - rawEvent.address, - ); - if (isStakingAddress) { + if (await this.isStakingAddress(rawEvent.address)) { await this.stakingHandler.handleUnstakeEvent(rawEvent); } else { await this.wsFarmHandler.handleExitFarmEvent(rawEvent); } break; case FARM_EVENTS.CLAIM_REWARDS: - isStakingAddress = await this.isStakingAddress( - rawEvent.address, - ); - if (isStakingAddress) { + if (await this.isStakingAddress(rawEvent.address)) { await this.stakingHandler.handleClaimRewardsEvent( rawEvent, ); diff --git a/src/modules/rabbitmq/rabbitmq.module.ts b/src/modules/rabbitmq/rabbitmq.module.ts index 557610628..d55056177 100644 --- a/src/modules/rabbitmq/rabbitmq.module.ts +++ b/src/modules/rabbitmq/rabbitmq.module.ts @@ -39,6 +39,7 @@ import { GovernanceModule } from '../governance/governance.module'; import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; import { RemoteConfigModule } from '../remote-config/remote-config.module'; import { StakingHandlerService } from './handlers/staking.handler.service'; +import { StakingModule } from '../staking/staking.module'; @Module({ imports: [ @@ -51,6 +52,7 @@ import { StakingHandlerService } from './handlers/staking.handler.service'; FarmModuleV1_2, FarmModuleV1_3, FarmModuleV2, + StakingModule, RouterModule, MetabondingModule, PriceDiscoveryModule, From e2f8c3c111185a930195db61853e139b34cb777a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 12 Oct 2023 14:56:43 +0300 Subject: [PATCH 044/313] MEX-407: use staking event decoders for staking events handlers Signed-off-by: Claudiu Lataretu --- package-lock.json | 22 +++++++++---------- package.json | 2 +- .../handlers/staking.handler.service.ts | 12 +++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0f2ecc98..cedf54cdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@golevelup/nestjs-rabbitmq": "^4.0.0", "@multiversx/sdk-core": "^12.8.0", "@multiversx/sdk-data-api-client": "^0.5.8", - "@multiversx/sdk-exchange": "^0.2.19", + "@multiversx/sdk-exchange": "^0.2.20", "@multiversx/sdk-native-auth-server": "1.0.7", "@multiversx/sdk-nestjs-cache": "^2.0.0-beta.2", "@multiversx/sdk-nestjs-common": "^2.0.0-beta.2", @@ -2856,9 +2856,9 @@ } }, "node_modules/@multiversx/sdk-exchange": { - "version": "0.2.19", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-exchange/-/sdk-exchange-0.2.19.tgz", - "integrity": "sha512-xxWVJ8IMCyiE+yWZ5dKPX7bln7d6WsRv8aalCB373+oxZl69SG4bZc/ZfDy8zUPdDdwPEkXkaAWstvNNKJFmcg==", + "version": "0.2.20", + "resolved": "http://localhost:4873/@multiversx/sdk-exchange/-/sdk-exchange-0.2.20.tgz", + "integrity": "sha512-PK5XKlsg//Iu4cvX2ffaY6mJrm4tHtruJNxpCzAIrHd3eBaonmcqR/EqEHjZVAaUgtbxF8SvWoBKoHy6MRQWfw==", "dependencies": { "@multiversx/sdk-core": "^11.2.0", "bignumber.js": "9.0.1" @@ -2866,7 +2866,7 @@ }, "node_modules/@multiversx/sdk-exchange/node_modules/@multiversx/sdk-core": { "version": "11.6.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", + "resolved": "http://localhost:4873/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", "integrity": "sha512-bP1fmp84iEAJEdaprvw8M9FeqJVOWTmv8vhhiaYW6YTV/ztlB+niv1CFsYk0dWFWIfKzn+eT4CvQjl8OTwBC/A==", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -2881,7 +2881,7 @@ }, "node_modules/@multiversx/sdk-exchange/node_modules/protobufjs": { "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "resolved": "http://localhost:4873/protobufjs/-/protobufjs-6.11.3.tgz", "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "hasInstallScript": true, "dependencies": { @@ -19006,9 +19006,9 @@ } }, "@multiversx/sdk-exchange": { - "version": "0.2.19", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-exchange/-/sdk-exchange-0.2.19.tgz", - "integrity": "sha512-xxWVJ8IMCyiE+yWZ5dKPX7bln7d6WsRv8aalCB373+oxZl69SG4bZc/ZfDy8zUPdDdwPEkXkaAWstvNNKJFmcg==", + "version": "0.2.20", + "resolved": "http://localhost:4873/@multiversx/sdk-exchange/-/sdk-exchange-0.2.20.tgz", + "integrity": "sha512-PK5XKlsg//Iu4cvX2ffaY6mJrm4tHtruJNxpCzAIrHd3eBaonmcqR/EqEHjZVAaUgtbxF8SvWoBKoHy6MRQWfw==", "requires": { "@multiversx/sdk-core": "^11.2.0", "bignumber.js": "9.0.1" @@ -19016,7 +19016,7 @@ "dependencies": { "@multiversx/sdk-core": { "version": "11.6.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", + "resolved": "http://localhost:4873/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", "integrity": "sha512-bP1fmp84iEAJEdaprvw8M9FeqJVOWTmv8vhhiaYW6YTV/ztlB+niv1CFsYk0dWFWIfKzn+eT4CvQjl8OTwBC/A==", "requires": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -19031,7 +19031,7 @@ }, "protobufjs": { "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "resolved": "http://localhost:4873/protobufjs/-/protobufjs-6.11.3.tgz", "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", diff --git a/package.json b/package.json index d91d226a3..c22b7e7d4 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@golevelup/nestjs-rabbitmq": "^4.0.0", "@multiversx/sdk-core": "^12.8.0", "@multiversx/sdk-data-api-client": "^0.5.8", - "@multiversx/sdk-exchange": "^0.2.19", + "@multiversx/sdk-exchange": "^0.2.20", "@multiversx/sdk-native-auth-server": "1.0.7", "@multiversx/sdk-nestjs-cache": "^2.0.0-beta.2", "@multiversx/sdk-nestjs-common": "^2.0.0-beta.2", diff --git a/src/modules/rabbitmq/handlers/staking.handler.service.ts b/src/modules/rabbitmq/handlers/staking.handler.service.ts index 9ef9ef42a..dea2f118a 100644 --- a/src/modules/rabbitmq/handlers/staking.handler.service.ts +++ b/src/modules/rabbitmq/handlers/staking.handler.service.ts @@ -1,8 +1,8 @@ import { - ClaimRewardsEventV2, - EnterFarmEventV2, - ExitFarmEventV2, RawEvent, + StakeClaimRewardsEvent, + StakeEvent, + UnstakeEvent, } from '@multiversx/sdk-exchange'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { RedisPubSub } from 'graphql-redis-subscriptions'; @@ -21,7 +21,7 @@ export class StakingHandlerService { ) {} async handleStakeEvent(rawEvent: RawEvent): Promise { - const event = new EnterFarmEventV2(rawEvent); + const event = new StakeEvent(rawEvent); const userTotalFarmPosition = await this.stakingAbi.getUserTotalStakePositionRaw( event.address, @@ -42,7 +42,7 @@ export class StakingHandlerService { } async handleUnstakeEvent(rawEvent: RawEvent): Promise { - const event = new ExitFarmEventV2(rawEvent); + const event = new UnstakeEvent(rawEvent); const userTotalFarmPosition = await this.stakingAbi.getUserTotalStakePositionRaw( event.address, @@ -63,7 +63,7 @@ export class StakingHandlerService { } async handleClaimRewardsEvent(rawEvent: RawEvent): Promise { - const event = new ClaimRewardsEventV2(rawEvent); + const event = new StakeClaimRewardsEvent(rawEvent); const userTotalFarmPosition = await this.stakingAbi.getUserTotalStakePositionRaw( event.address, From dba3794e7962173878359d17db261a46d7240d34 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 12 Oct 2023 16:29:04 +0300 Subject: [PATCH 045/313] MEX-407: compute staking boosted rewards - add methods to compute boosted rewards - add new fileds for staking rewards model Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 14 +- .../services/staking.compute.service.ts | 294 ++++++++++++++++++ .../staking/services/staking.service.ts | 64 +++- src/modules/staking/staking.resolver.ts | 2 + .../user/user.info-by-week.resolver.ts | 23 ++ 5 files changed, 395 insertions(+), 2 deletions(-) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 41a2b48f2..ad91f68dd 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -3,7 +3,11 @@ import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { StakingTokenAttributesModel } from './stakingTokenAttributes.model'; import { WeekTimekeepingModel } from 'src/submodules/week-timekeeping/models/week-timekeeping.model'; -import { GlobalInfoByWeekModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; +import { + ClaimProgress, + GlobalInfoByWeekModel, + UserInfoByWeekModel, +} from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; @ObjectType() @@ -76,6 +80,14 @@ export class StakingRewardsModel { decodedAttributes: StakingTokenAttributesModel; @Field() rewards: string; + @Field(() => Int, { nullable: true }) + remainingFarmingEpochs?: number; + @Field(() => [UserInfoByWeekModel], { nullable: true }) + boostedRewardsWeeklyInfo: UserInfoByWeekModel[]; + @Field(() => ClaimProgress, { nullable: true }) + claimProgress: ClaimProgress; + @Field({ nullable: true }) + accumulatedRewards: string; constructor(init?: Partial) { Object.assign(this, init); diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index c7035f829..b29d4fcf7 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -12,6 +12,11 @@ import { denominateAmount } from 'src/utils/token.converters'; import { OptimalCompoundModel } from '../models/staking.model'; import { TokenService } from 'src/modules/tokens/services/token.service'; import { TokenComputeService } from 'src/modules/tokens/services/token.compute.service'; +import { TokenDistributionModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; +import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; +import { WeekTimekeepingComputeService } from 'src/submodules/week-timekeeping/services/week-timekeeping.compute.service'; +import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; +import { EsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; @Injectable() export class StakingComputeService { @@ -22,6 +27,9 @@ export class StakingComputeService { private readonly contextGetter: ContextGetterService, private readonly tokenService: TokenService, private readonly tokenCompute: TokenComputeService, + private readonly weekTimekeepingCompute: WeekTimekeepingComputeService, + private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, + private readonly weeklyRewardsSplittingCompute: WeeklyRewardsSplittingComputeService, ) {} async computeStakeRewardsForPosition( @@ -352,4 +360,290 @@ export class StakingComputeService { totalRemainingRewards, ); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, + localTtl: CacheTtlInfo.ContractBalance.localTtl, + }) + async userRewardsDistributionForWeek( + scAddress: string, + userAddress: string, + week: number, + ): Promise { + return await this.computeUserRewardsDistributionForWeek( + scAddress, + userAddress, + week, + ); + } + + async computeUserRewardsDistributionForWeek( + scAddress: string, + userAddress: string, + week: number, + ): Promise { + const userRewardsForWeek = await this.userRewardsForWeek( + scAddress, + userAddress, + week, + ); + return await this.weeklyRewardsSplittingCompute.computeDistribution( + userRewardsForWeek, + ); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + // @GetOrSetCache({ + // baseKey: 'stake', + // remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, + // localTtl: CacheTtlInfo.ContractBalance.localTtl, + // }) + async userAccumulatedRewards( + scAddress: string, + userAddress: string, + week: number, + ): Promise { + return this.computeUserAccumulatedRewards(scAddress, userAddress, week); + } + + async computeUserAccumulatedRewards( + scAddress: string, + userAddress: string, + week: number, + ): Promise { + const [ + boostedYieldsFactors, + boostedYieldsRewardsPercenatage, + userEnergy, + totalRewards, + rewardsPerBlock, + farmTokenSupply, + totalEnergy, + blocksInWeek, + liquidity, + ] = await Promise.all([ + this.stakingAbi.boostedYieldsFactors(scAddress), + this.stakingAbi.boostedYieldsRewardsPercenatage(scAddress), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + this.stakingAbi.accumulatedRewardsForWeek(scAddress, week), + this.stakingAbi.perBlockRewardsAmount(scAddress), + this.stakingAbi.farmTokenSupply(scAddress), + this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), + this.computeBlocksInWeek(scAddress, week), + this.stakingAbi.userTotalStakePosition(scAddress, userAddress), + ]); + + const energyAmount = userEnergy.amount; + + const userHasMinEnergy = new BigNumber(energyAmount).isGreaterThan( + boostedYieldsFactors.minEnergyAmount, + ); + if (!userHasMinEnergy) { + return '0'; + } + + const userMinFarmAmount = new BigNumber(liquidity).isGreaterThan( + boostedYieldsFactors.minFarmAmount, + ); + if (!userMinFarmAmount) { + return '0'; + } + + if (totalRewards.length === 0) { + return '0'; + } + + const userMaxBoostedRewardsPerBlock = new BigNumber(rewardsPerBlock) + .multipliedBy(boostedYieldsRewardsPercenatage) + .dividedBy(constantsConfig.MAX_PERCENT) + .multipliedBy(liquidity) + .dividedBy(farmTokenSupply); + + const userRewardsForWeek = new BigNumber( + boostedYieldsFactors.maxRewardsFactor, + ) + .multipliedBy(userMaxBoostedRewardsPerBlock) + .multipliedBy(blocksInWeek); + + const boostedRewardsByEnergy = new BigNumber(totalRewards) + .multipliedBy(boostedYieldsFactors.userRewardsEnergy) + .multipliedBy(userEnergy.amount) + .dividedBy(totalEnergy); + + const boostedRewardsByTokens = new BigNumber(totalRewards) + .multipliedBy(boostedYieldsFactors.userRewardsFarm) + .multipliedBy(liquidity) + .dividedBy(farmTokenSupply); + + const constantsBase = new BigNumber( + boostedYieldsFactors.userRewardsEnergy, + ).plus(boostedYieldsFactors.userRewardsFarm); + + const boostedRewardAmount = boostedRewardsByEnergy + .plus(boostedRewardsByTokens) + .dividedBy(constantsBase); + + const paymentAmount = + boostedRewardAmount.comparedTo(userRewardsForWeek) < 1 + ? boostedRewardAmount + : userRewardsForWeek; + + return paymentAmount.integerValue().toFixed(); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, + localTtl: CacheTtlInfo.ContractBalance.localTtl, + }) + async userRewardsForWeek( + scAddress: string, + userAddress: string, + week: number, + ): Promise { + return this.computeUserRewardsForWeek(scAddress, userAddress, week); + } + + async computeUserRewardsForWeek( + scAddress: string, + userAddress: string, + week: number, + ): Promise { + const payments: EsdtTokenPayment[] = []; + const [ + totalRewardsForWeek, + userEnergyForWeek, + totalEnergyForWeek, + liquidity, + ] = await Promise.all([ + this.weeklyRewardsSplittingAbi.totalRewardsForWeek(scAddress, week), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), + this.stakingAbi.userTotalStakePosition(scAddress, userAddress), + ]); + + const boostedYieldsFactors = await this.stakingAbi.boostedYieldsFactors( + scAddress, + ); + + const userHasMinEnergy = new BigNumber( + userEnergyForWeek.amount, + ).isGreaterThan(boostedYieldsFactors.minEnergyAmount); + if (!userHasMinEnergy) { + return payments; + } + + const userMinFarmAmount = new BigNumber(liquidity).isGreaterThan( + boostedYieldsFactors.minFarmAmount, + ); + if (!userMinFarmAmount) { + return payments; + } + + if (totalRewardsForWeek.length === 0) { + return payments; + } + + const [ + rewardsPerBlock, + farmTokenSupply, + boostedYieldsRewardsPercenatage, + ] = await Promise.all([ + this.stakingAbi.perBlockRewardsAmount(scAddress), + this.stakingAbi.farmTokenSupply(scAddress), + this.stakingAbi.boostedYieldsRewardsPercenatage(scAddress), + ]); + + const userMaxBoostedRewardsPerBlock = new BigNumber(rewardsPerBlock) + .multipliedBy(boostedYieldsRewardsPercenatage) + .dividedBy(constantsConfig.MAX_PERCENT) + .multipliedBy(liquidity) + .dividedBy(farmTokenSupply); + + const userRewardsForWeek = new BigNumber( + boostedYieldsFactors.maxRewardsFactor, + ) + .multipliedBy(userMaxBoostedRewardsPerBlock) + .multipliedBy(constantsConfig.BLOCKS_PER_WEEK); + + for (const weeklyRewards of totalRewardsForWeek) { + const boostedRewardsByEnergy = new BigNumber(weeklyRewards.amount) + .multipliedBy(boostedYieldsFactors.userRewardsEnergy) + .multipliedBy(userEnergyForWeek.amount) + .dividedBy(totalEnergyForWeek); + + const boostedRewardsByTokens = new BigNumber(weeklyRewards.amount) + .multipliedBy(boostedYieldsFactors.userRewardsFarm) + .multipliedBy(liquidity) + .dividedBy(farmTokenSupply); + + const constantsBase = new BigNumber( + boostedYieldsFactors.userRewardsEnergy, + ).plus(boostedYieldsFactors.userRewardsFarm); + + const boostedRewardAmount = boostedRewardsByEnergy + .plus(boostedRewardsByTokens) + .dividedBy(constantsBase); + + const paymentAmount = + boostedRewardAmount.comparedTo(userRewardsForWeek) < 1 + ? boostedRewardAmount + : userRewardsForWeek; + if (paymentAmount.isPositive()) { + const payment = new EsdtTokenPayment(); + payment.amount = paymentAmount.integerValue().toFixed(); + payment.nonce = 0; + payment.tokenID = weeklyRewards.tokenID; + payment.tokenType = weeklyRewards.tokenType; + payments.push(payment); + } + } + + return payments; + } + + async computeBlocksInWeek( + scAddress: string, + week: number, + ): Promise { + const [startEpochForCurrentWeek, currentEpoch, shardID] = + await Promise.all([ + this.weekTimekeepingCompute.startEpochForWeek(scAddress, week), + this.contextGetter.getCurrentEpoch(), + this.stakingAbi.stakingShard(scAddress), + ]); + + const promises = []; + for ( + let epoch = startEpochForCurrentWeek; + epoch <= currentEpoch; + epoch++ + ) { + promises.push( + this.contextGetter.getBlocksCountInEpoch(epoch, shardID), + ); + } + + const blocksInEpoch = await Promise.all(promises); + return blocksInEpoch.reduce((total, current) => { + return total + current; + }); + } } diff --git a/src/modules/staking/services/staking.service.ts b/src/modules/staking/services/staking.service.ts index 84f3f5875..b1932b802 100644 --- a/src/modules/staking/services/staking.service.ts +++ b/src/modules/staking/services/staking.service.ts @@ -19,6 +19,13 @@ import { StakingComputeService } from './staking.compute.service'; import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { TokenService } from 'src/modules/tokens/services/token.service'; +import { + ClaimProgress, + UserInfoByWeekModel, +} from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; +import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; +import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; +import { constantsConfig } from 'src/config'; @Injectable() export class StakingService { @@ -30,6 +37,8 @@ export class StakingService { private readonly tokenService: TokenService, private readonly apiService: MXApiService, private readonly remoteConfigGetter: RemoteConfigGetterService, + private readonly weekTimekeepingAbi: WeekTimekeepingAbiService, + private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, ) {} async getFarmsStaking(): Promise { @@ -102,15 +111,17 @@ export class StakingService { async getBatchRewardsForPosition( positions: CalculateRewardsArgs[], + computeBoosted = false, ): Promise { const promises = positions.map(async (position) => { - return await this.getRewardsForPosition(position); + return await this.getRewardsForPosition(position, computeBoosted); }); return await Promise.all(promises); } async getRewardsForPosition( positon: CalculateRewardsArgs, + computeBoosted = false, ): Promise { const stakeTokenAttributes = this.decodeStakingTokenAttributes({ batchAttributes: [ @@ -135,9 +146,60 @@ export class StakingService { ); } + let modelsList: UserInfoByWeekModel[] = undefined; + let currentClaimProgress: ClaimProgress = undefined; + let userAccumulatedRewards: string = undefined; + if (computeBoosted) { + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + positon.farmAddress, + ); + modelsList = []; + let lastActiveWeekUser = + await this.weeklyRewardsSplittingAbi.lastActiveWeekForUser( + positon.farmAddress, + positon.user, + ); + if (lastActiveWeekUser === 0) { + lastActiveWeekUser = currentWeek; + } + const startWeek = Math.max( + currentWeek - constantsConfig.USER_MAX_CLAIM_WEEKS, + lastActiveWeekUser, + ); + + for (let week = startWeek; week <= currentWeek - 1; week++) { + if (week < 1) { + continue; + } + const model = new UserInfoByWeekModel({ + scAddress: positon.farmAddress, + userAddress: positon.user, + week: week, + }); + model.positionAmount = positon.liquidity; + modelsList.push(model); + } + + currentClaimProgress = + await this.weeklyRewardsSplittingAbi.currentClaimProgress( + positon.farmAddress, + positon.user, + ); + + userAccumulatedRewards = + await this.stakingCompute.userAccumulatedRewards( + positon.farmAddress, + positon.user, + currentWeek, + ); + } + return new StakingRewardsModel({ decodedAttributes: stakeTokenAttributes[0], rewards: rewards.integerValue().toFixed(), + boostedRewardsWeeklyInfo: modelsList, + claimProgress: currentClaimProgress, + accumulatedRewards: userAccumulatedRewards, }); } diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 78a5510c1..71d6c0974 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -252,9 +252,11 @@ export class StakingResolver { @Query(() => [StakingRewardsModel]) async getStakingRewardsForPosition( @Args('stakingPositions') args: BatchFarmRewardsComputeArgs, + @Args('computeBoosted', { nullable: true }) computeBoosted: boolean, ): Promise { return this.stakingService.getBatchRewardsForPosition( args.farmsPositions, + computeBoosted, ); } diff --git a/src/modules/user/user.info-by-week.resolver.ts b/src/modules/user/user.info-by-week.resolver.ts index 85c2fdfb1..d219a92c9 100644 --- a/src/modules/user/user.info-by-week.resolver.ts +++ b/src/modules/user/user.info-by-week.resolver.ts @@ -10,14 +10,18 @@ import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards- import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; import { FeesCollectorComputeService } from '../fees-collector/services/fees-collector.compute.service'; import { FarmComputeServiceV2 } from '../farm/v2/services/farm.v2.compute.service'; +import { StakingComputeService } from '../staking/services/staking.compute.service'; +import { RemoteConfigGetterService } from '../remote-config/remote-config.getter.service'; @Resolver(() => UserInfoByWeekModel) export class UserInfoByWeekResolver { constructor( private readonly farmComputeV2: FarmComputeServiceV2, private readonly feesCollectorCompute: FeesCollectorComputeService, + private readonly stakingCompute: StakingComputeService, private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, private readonly weeklyRewardsSplittingCompute: WeeklyRewardsSplittingComputeService, + private readonly remoteConfig: RemoteConfigGetterService, ) {} @ResolveField(() => EnergyModel) @@ -51,6 +55,16 @@ export class UserInfoByWeekResolver { parent.week, ); } + + const stakingAddresses = await this.remoteConfig.getStakingAddresses(); + if (stakingAddresses.includes(parent.scAddress)) { + return this.stakingCompute.userRewardsForWeek( + parent.scAddress, + parent.userAddress, + parent.week, + ); + } + return this.farmComputeV2.userRewardsForWeek( parent.scAddress, parent.userAddress, @@ -69,6 +83,15 @@ export class UserInfoByWeekResolver { parent.week, ); } + const stakingAddresses = await this.remoteConfig.getStakingAddresses(); + if (stakingAddresses.includes(parent.scAddress)) { + return this.stakingCompute.userRewardsDistributionForWeek( + parent.scAddress, + parent.userAddress, + parent.week, + ); + } + return this.farmComputeV2.userRewardsDistributionForWeek( parent.scAddress, parent.userAddress, From 3c7a6aabdac00b8ec24ab8d54b5b97866e5d48e3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 12 Oct 2023 17:12:07 +0300 Subject: [PATCH 046/313] MEX-407: compute user outdated energy contract for staking farms Signed-off-by: Claudiu Lataretu --- src/modules/user/models/user.model.ts | 14 ++- .../userEnergy/user.energy.compute.service.ts | 105 ++++++++++++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/src/modules/user/models/user.model.ts b/src/modules/user/models/user.model.ts index 8eba0cba5..b3904ddce 100644 --- a/src/modules/user/models/user.model.ts +++ b/src/modules/user/models/user.model.ts @@ -2,8 +2,14 @@ import { Field, ObjectType } from '@nestjs/graphql'; import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { NftToken } from 'src/modules/tokens/models/nftToken.model'; import { FarmToken } from 'src/modules/tokens/models/farmToken.model'; -import { LockedLpToken, LockedLpTokenV2 } from 'src/modules/tokens/models/lockedLpToken.model'; -import { LockedFarmToken, LockedFarmTokenV2 } from 'src/modules/tokens/models/lockedFarmToken.model'; +import { + LockedLpToken, + LockedLpTokenV2, +} from 'src/modules/tokens/models/lockedLpToken.model'; +import { + LockedFarmToken, + LockedFarmTokenV2, +} from 'src/modules/tokens/models/lockedFarmToken.model'; import { LockedAssetToken } from 'src/modules/tokens/models/lockedAssetToken.model'; import { StakeFarmToken } from 'src/modules/tokens/models/stakeFarmToken.model'; import { UnbondFarmToken } from 'src/modules/tokens/models/unbondFarmToken.model'; @@ -14,10 +20,10 @@ import { LockedSimpleLpToken } from 'src/modules/tokens/models/lockedSimpleLpTok import { PaginationArgs } from 'src/modules/dex.model'; import { WrappedLockedTokenAttributesModel } from 'src/modules/simple-lock/models/simple.lock.model'; - export enum ContractType { Farm = 'Farm', - FeesCollector = 'FeesCollector' + FeesCollector = 'FeesCollector', + StakingFarm = 'StakingFarm', } @ObjectType() diff --git a/src/modules/user/services/userEnergy/user.energy.compute.service.ts b/src/modules/user/services/userEnergy/user.energy.compute.service.ts index abcdc73b1..b05a3dae0 100644 --- a/src/modules/user/services/userEnergy/user.energy.compute.service.ts +++ b/src/modules/user/services/userEnergy/user.energy.compute.service.ts @@ -24,6 +24,8 @@ import { FarmServiceV2 } from 'src/modules/farm/v2/services/farm.v2.service'; import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { Constants } from '@multiversx/sdk-nestjs-common'; import { EnergyAbiService } from 'src/modules/energy/services/energy.abi.service'; +import { RemoteConfigGetterService } from 'src/modules/remote-config/remote-config.getter.service'; +import { StakingService } from 'src/modules/staking/services/staking.service'; @Injectable() export class UserEnergyComputeService { @@ -35,8 +37,10 @@ export class UserEnergyComputeService { private readonly userMetaEsdtService: UserMetaEsdtService, private readonly stakeProxyService: StakingProxyService, private readonly stakeProxyAbi: StakingProxyAbiService, + private readonly stakingService: StakingService, private readonly energyAbi: EnergyAbiService, private readonly proxyService: ProxyService, + private readonly remoteConfig: RemoteConfigGetterService, ) {} async getUserOutdatedContracts( @@ -48,6 +52,14 @@ export class UserEnergyComputeService { const promises = activeFarms.map((farm) => this.outdatedContract(userAddress, farm), ); + + const activeStakings = await this.userActiveStakings(userAddress); + promises.push( + ...activeStakings.map((stake) => + this.outdatedContract(userAddress, stake), + ), + ); + if (!skipFeesCollector) { promises.push( this.outdatedContract(userAddress, scAddress.feesCollector), @@ -78,6 +90,14 @@ export class UserEnergyComputeService { userAddress: string, contractAddress: string, ): Promise { + const stakeAddresses = await this.remoteConfig.getStakingAddresses(); + if (stakeAddresses.includes(contractAddress)) { + return await this.computeStakingOutdatedContract( + userAddress, + contractAddress, + ); + } + const isFarmAddress = contractAddress !== scAddress.feesCollector; if (isFarmAddress) { @@ -128,6 +148,41 @@ export class UserEnergyComputeService { return new OutdatedContract(); } + async computeStakingOutdatedContract( + userAddress: string, + contractAddress: string, + ): Promise { + const [currentClaimProgress, currentWeek, farmToken, userEnergy] = + await Promise.all([ + this.weeklyRewardsSplittingAbi.currentClaimProgress( + contractAddress, + userAddress, + ), + this.weekTimekeepingAbi.currentWeek(contractAddress), + this.stakingService.getFarmToken(contractAddress), + this.energyAbi.energyEntryForUser(userAddress), + ]); + + if (currentClaimProgress.week === 0) { + return new OutdatedContract(); + } + + const outdatedClaimProgress = currentClaimProgress.week !== currentWeek; + + if ( + this.isEnergyOutdated(userEnergy, currentClaimProgress) || + outdatedClaimProgress + ) { + return new OutdatedContract({ + address: contractAddress, + type: ContractType.StakingFarm, + claimProgressOutdated: outdatedClaimProgress, + farmToken: farmToken.collection, + }); + } + return new OutdatedContract(); + } + async computeFeesCollectorOutdatedContract( userAddress: string, ): Promise { @@ -211,6 +266,44 @@ export class UserEnergyComputeService { ); } + @GetOrSetCache({ + baseKey: 'userEnergy', + remoteTtl: Constants.oneMinute(), + }) + async userActiveStakings(userAddress: string): Promise { + return await this.computeActiveStakingsForUser(userAddress); + } + + async computeActiveStakingsForUser(userAddress: string): Promise { + const maxPagination = new PaginationArgs({ + limit: 100, + offset: 0, + }); + const [stakeTokens, dualYieldTokens] = await Promise.all([ + this.userMetaEsdtService.getUserStakeFarmTokens( + userAddress, + maxPagination, + ), + this.userMetaEsdtService.getUserDualYieldTokens( + userAddress, + maxPagination, + false, + ), + ]); + + let userActiveStakeAddresses = stakeTokens.map( + (token) => token.creator, + ); + const promisesDualYieldTokens = dualYieldTokens.map((token) => { + return this.getFarmAddressForDualYieldToken(token); + }); + + userActiveStakeAddresses = userActiveStakeAddresses.concat( + await Promise.all([...promisesDualYieldTokens]), + ); + return [...new Set(userActiveStakeAddresses)]; + } + decodeAndGetFarmAddressFarmLockedTokens(token: UserLockedFarmTokenV2) { const decodedWFMTAttributes = this.proxyService.getWrappedFarmTokenAttributesV2({ @@ -239,6 +332,18 @@ export class UserEnergyComputeService { return this.stakeProxyAbi.lpFarmAddress(stakingProxyAddress); } + async getStakeAddressForDualYieldToken(token: UserDualYiledToken) { + if (!token || token === undefined) { + return undefined; + } + + const stakingProxyAddress = + await this.stakeProxyService.getStakingProxyAddressByDualYieldTokenID( + token.collection, + ); + return this.stakeProxyAbi.stakingFarmAddress(stakingProxyAddress); + } + isEnergyOutdated( currentUserEnergy: EnergyType, currentClaimProgress: ClaimProgress, From a47ae4731d63469221c194a40933b828c85b82d9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 12 Oct 2023 17:13:15 +0300 Subject: [PATCH 047/313] MEX-407: update gas limits for staking transactions Signed-off-by: Claudiu Lataretu --- src/config/default.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 91dbd8220..9746db1af 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -117,7 +117,7 @@ "removeLiquidity": 8500000, "removeLiquidityAndBuyBackAndBurnToken": 200000000, "swapTokensFixedInput": { - "default": 12000000, + "default": 13000000, "withFeeSwap": 25500000 }, "swapTokensFixedOutput": { @@ -379,14 +379,14 @@ }, "stake": { "stakeFarm": { - "default": 7500000, - "withTokenMerge": 10000000 + "default": 17500000, + "withTokenMerge": 20000000 }, - "unstakeFarm": 9500000, + "unstakeFarm": 20000000, "unbondFarm": 7500000, - "claimRewards": 8500000, - "claimRewardsWithNewValue": 7500000, - "compoundRewards": 8500000, + "claimRewards": 17000000, + "claimRewardsWithNewValue": 17000000, + "compoundRewards": 17000000, "mergeTokens": 20000000, "admin": { "setState": 20000000, From d15fe9655a0023232b3c1d0858b06a02aca3a34b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 26 Oct 2023 22:18:34 +0300 Subject: [PATCH 048/313] MEX-409: add staking position migration field to staking model Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 5 ++++ .../staking/services/staking.abi.service.ts | 25 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 7 ++++++ 3 files changed, 37 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index ad91f68dd..9261732d0 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -68,6 +68,11 @@ export class StakingModel { undistributedBoostedRewards: string; @Field() undistributedBoostedRewardsClaimed: string; + @Field({ + description: + 'The nonce of the first staking farm token with total farm position', + }) + stakingPositionMigrationNonce: number; constructor(init?: Partial) { Object.assign(this, init); diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index c31f2b7ba..820d2a44b 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -670,6 +670,31 @@ export class StakingAbiService return response.firstValue.valueOf().total_farm_position.toFixed(); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async farmPositionMigrationNonce(stakeAddress: string): Promise { + return await this.getFarmPositionMigrationNonceRaw(stakeAddress); + } + + async getFarmPositionMigrationNonceRaw( + stakeAddress: string, + ): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); + + const interaction: Interaction = + contract.methodsExplicit.getFarmPositionMigrationNonce(); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toNumber(); + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 71d6c0974..94daefb65 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -225,6 +225,13 @@ export class StakingResolver { return this.stakingAbi.undistributedBoostedRewards(parent.address); } + @ResolveField() + async stakingPositionMigrationNonce( + @Parent() parent: StakingModel, + ): Promise { + return this.stakingAbi.farmPositionMigrationNonce(parent.address); + } + @Query(() => String) async getLastErrorMessage( @Args('stakeAddress') stakeAddress: string, From 5be554aaa4c36ee46460f5dc557035f70e1c6b98 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 26 Oct 2023 23:02:24 +0300 Subject: [PATCH 049/313] MEX-409: add missing definitions for staking abi interface Signed-off-by: Claudiu Lataretu --- src/modules/staking/services/interfaces.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/modules/staking/services/interfaces.ts b/src/modules/staking/services/interfaces.ts index 5d7b2002c..754232859 100644 --- a/src/modules/staking/services/interfaces.ts +++ b/src/modules/staking/services/interfaces.ts @@ -1,4 +1,5 @@ import BigNumber from 'bignumber.js'; +import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; export interface IStakingAbiService { farmTokenID(stakeAddress: string): Promise; @@ -24,4 +25,25 @@ export interface IStakingAbiService { lockedAssetFactoryAddress(stakeAddress: string): Promise; isWhitelisted(stakeAddress: string, scAddress: string): Promise; lastErrorMessage(stakeAddress: string): Promise; + energyFactoryAddress(stakeAddress: string): Promise; + boostedYieldsRewardsPercenatage(stakeAddress: string): Promise; + boostedYieldsFactors(stakeAddress: string): Promise; + accumulatedRewardsForWeek( + stakeAddress: string, + week: number, + ): Promise; + undistributedBoostedRewards(stakeAddress: string): Promise; + lastUndistributedBoostedRewardsCollectWeek( + stakeAddress: string, + ): Promise; + remainingBoostedRewardsToDistribute( + stakeAddress: string, + week: number, + ): Promise; + userTotalStakePosition( + stakeAddress: string, + userAddress: string, + ): Promise; + farmPositionMigrationNonce(stakeAddress: string): Promise; + stakingShard(stakeAddress: string): Promise; } From e37d52791ee78161ec1a480efc251cc5ce11dd62 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 26 Oct 2023 23:05:49 +0300 Subject: [PATCH 050/313] MEX-409: add staking total positions migrate Signed-off-by: Claudiu Lataretu --- .../services/staking.transactions.service.ts | 42 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 12 ++++++ 2 files changed, 54 insertions(+) diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index 773e2c175..3bac4fdb8 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -15,12 +15,14 @@ import { TransactionModel } from 'src/models/transaction.model'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; import { StakingAbiService } from './staking.abi.service'; import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; @Injectable() export class StakingTransactionService { constructor( private readonly stakingAbi: StakingAbiService, private readonly mxProxy: MXProxyService, + private readonly mxApi: MXApiService, ) {} @ErrorLoggerAsync() @@ -180,6 +182,46 @@ export class StakingTransactionService { .toPlainObject(); } + async migrateTotalStakingPosition( + stakingAddress: string, + userAddress: string, + ): Promise { + const [stakeTokenID, migrationNonce, userNftsCount] = await Promise.all( + [ + this.stakingAbi.farmTokenID(stakingAddress), + this.stakingAbi.farmPositionMigrationNonce(stakingAddress), + this.mxApi.getNftsCountForUser(userAddress), + ], + ); + + const userNfts = await this.mxApi.getNftsForUser( + userAddress, + 0, + userNftsCount, + 'MetaESDT', + [stakeTokenID], + ); + + if (userNfts.length === 0) { + return []; + } + + const promises: Promise[] = []; + userNfts.forEach((nft) => { + if (nft.nonce < migrationNonce) { + promises.push( + this.claimRewards(userAddress, stakingAddress, { + tokenID: nft.collection, + nonce: nft.nonce, + amount: nft.balance, + }), + ); + } + }); + + return Promise.all(promises); + } + async topUpRewards( stakeAddress: string, payment: InputTokenModel, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 94daefb65..c72c77554 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -327,6 +327,18 @@ export class StakingResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => [TransactionModel]) + async migrateTotalStakingPosition( + @Args('stakeAddress') stakeAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.stakingTransactionService.migrateTotalStakingPosition( + stakeAddress, + user.address, + ); + } + @UseGuards(JwtOrNativeAdminGuard) @Query(() => TransactionModel) async setPerBlockRewardAmount( From 96511a6a50cf2bd78b5b556fcc8dce737a32a380 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 26 Oct 2023 23:06:36 +0300 Subject: [PATCH 051/313] MEX-409: add unit test for total staking migrate transactions Signed-off-by: Claudiu Lataretu --- .../staking/mocks/staking.abi.service.mock.ts | 42 +++++++++++++++ .../staking.transactions.service.spec.ts | 53 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/modules/staking/mocks/staking.abi.service.mock.ts b/src/modules/staking/mocks/staking.abi.service.mock.ts index 12dd0de74..be9114c77 100644 --- a/src/modules/staking/mocks/staking.abi.service.mock.ts +++ b/src/modules/staking/mocks/staking.abi.service.mock.ts @@ -1,6 +1,7 @@ import BigNumber from 'bignumber.js'; import { IStakingAbiService } from '../services/interfaces'; import { StakingAbiService } from '../services/staking.abi.service'; +import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; export class StakingAbiServiceMock implements IStakingAbiService { async farmTokenID(stakeAddress: string): Promise { @@ -61,6 +62,47 @@ export class StakingAbiServiceMock implements IStakingAbiService { lastErrorMessage(stakeAddress: string): Promise { throw new Error('Method not implemented.'); } + energyFactoryAddress(stakeAddress: string): Promise { + throw new Error('Method not implemented.'); + } + boostedYieldsRewardsPercenatage(stakeAddress: string): Promise { + throw new Error('Method not implemented.'); + } + boostedYieldsFactors(stakeAddress: string): Promise { + throw new Error('Method not implemented.'); + } + accumulatedRewardsForWeek( + stakeAddress: string, + week: number, + ): Promise { + throw new Error('Method not implemented.'); + } + undistributedBoostedRewards(stakeAddress: string): Promise { + throw new Error('Method not implemented.'); + } + lastUndistributedBoostedRewardsCollectWeek( + stakeAddress: string, + ): Promise { + throw new Error('Method not implemented.'); + } + remainingBoostedRewardsToDistribute( + stakeAddress: string, + week: number, + ): Promise { + throw new Error('Method not implemented.'); + } + userTotalStakePosition( + stakeAddress: string, + userAddress: string, + ): Promise { + throw new Error('Method not implemented.'); + } + async farmPositionMigrationNonce(stakeAddress: string): Promise { + return 2; + } + async stakingShard(stakeAddress: string): Promise { + return 1; + } } export const StakingAbiServiceProvider = { diff --git a/src/modules/staking/specs/staking.transactions.service.spec.ts b/src/modules/staking/specs/staking.transactions.service.spec.ts index 07eb621f1..321dcbd89 100644 --- a/src/modules/staking/specs/staking.transactions.service.spec.ts +++ b/src/modules/staking/specs/staking.transactions.service.spec.ts @@ -14,6 +14,8 @@ import { ConfigModule } from '@nestjs/config'; import { WinstonModule } from 'nest-winston'; import winston from 'winston'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; describe('StakingTransactionService', () => { let module: TestingModule; @@ -33,6 +35,7 @@ describe('StakingTransactionService', () => { ContextGetterServiceProvider, MXProxyServiceProvider, MXGatewayService, + MXApiServiceProvider, ApiConfigService, ], }).compile(); @@ -207,6 +210,56 @@ describe('StakingTransactionService', () => { }); }); + it('should get total staking migrate transaction', async () => { + const service = module.get( + StakingTransactionService, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'STAKETOK-1111-01', + collection: 'STAKETOK-1111', + attributes: + 'AAAABHA3g/MAAAAAAAAACA3gtrOnZAAAYLtgEaeB64tT1h15BHRQrapGl34gI5MSlRJiwQV9EJA=', + nonce: 1, + type: 'MetaESDT', + name: 'STAKETOK', + creator: Address.Zero().bech32(), + balance: '1000000000000000000', + decimals: 18, + ticker: 'STAKETOK', + }, + ]); + + const transactions = await service.migrateTotalStakingPosition( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); + + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: + 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 17000000, + data: encodeTransactionData( + 'ESDTNFTTransfer@STAKETOK-1111@01@1000000000000000000@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu@claimRewards', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should get top up rewards transaction', async () => { const service = module.get( StakingTransactionService, From 68b9b1e64f8ec40ae0dcb85e71b16ce1042c480a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 26 Oct 2023 23:28:25 +0300 Subject: [PATCH 052/313] MEX-408: update staking proxy abi - remove liquidity argument from unstakeFarmTokens transaction Signed-off-by: Claudiu Lataretu --- src/abis/farm-staking-proxy.abi.json | 239 +++++++++++++----- .../staking.proxy.transactions.service.ts | 6 - 2 files changed, 173 insertions(+), 72 deletions(-) diff --git a/src/abis/farm-staking-proxy.abi.json b/src/abis/farm-staking-proxy.abi.json index 7cd567876..3b94efef0 100644 --- a/src/abis/farm-staking-proxy.abi.json +++ b/src/abis/farm-staking-proxy.abi.json @@ -1,19 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.60.0-nightly", - "commitHash": "a00e130dae74a213338e2b095ec855156d8f3d8a", - "commitDate": "2022-01-29", + "version": "1.73.0-nightly", + "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", + "commitDate": "2023-07-15", "channel": "Nightly", - "short": "rustc 1.60.0-nightly (a00e130da 2022-01-29)" + "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" }, "contractCrate": { "name": "farm-staking-proxy", - "version": "0.0.0" + "version": "0.0.0", + "gitVersion": "v1.6.0-1527-g9534cc41" }, "framework": { - "name": "elrond-wasm", - "version": "0.27.4" + "name": "multiversx-sc", + "version": "0.43.3" } }, "name": "FarmStakingProxy", @@ -42,49 +43,51 @@ { "name": "staking_farm_token_id", "type": "TokenIdentifier" + }, + { + "name": "lp_token_id", + "type": "TokenIdentifier" } ], "outputs": [] }, "endpoints": [ { - "name": "stakeFarmTokens", + "name": "upgrade", "mutability": "mutable", - "payableInTokens": [ - "*" - ], "inputs": [], - "outputs": [ - { - "type": "EsdtTokenPayment" - } - ] + "outputs": [] }, { - "name": "claimDualYield", + "name": "registerDualYieldToken", + "onlyOwner": true, "mutability": "mutable", "payableInTokens": [ - "*" + "EGLD" ], - "inputs": [], - "outputs": [ + "inputs": [ + { + "name": "token_display_name", + "type": "bytes" + }, { - "type": "variadic", - "multi_result": true + "name": "token_ticker", + "type": "bytes" + }, + { + "name": "num_decimals", + "type": "u32" } - ] + ], + "outputs": [] }, { - "name": "unstakeFarmTokens", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], + "name": "getDualYieldTokenId", + "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "variadic", - "multi_result": true + "type": "TokenIdentifier" } ] }, @@ -139,58 +142,153 @@ ] }, { - "name": "issueDualYieldToken", + "name": "getLpTokenId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getLpFarmTokenId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "addSCAddressToWhitelist", "onlyOwner": true, "mutability": "mutable", - "payableInTokens": [ - "EGLD" - ], "inputs": [ { - "name": "token_display_name", - "type": "bytes" - }, - { - "name": "token_ticker", - "type": "bytes" - }, + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "removeSCAddressFromWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ { - "name": "num_decimals", - "type": "u32" + "name": "address", + "type": "Address" } ], "outputs": [] }, { - "name": "getDualYieldTokenId", + "name": "isSCAddressWhitelisted", "mutability": "readonly", - "inputs": [], + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], "outputs": [ { - "type": "TokenIdentifier" + "type": "bool" } ] }, { - "name": "getLpFarmTokenId", - "mutability": "readonly", - "inputs": [], + "name": "stakeFarmTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "opt_orig_caller", + "type": "optional
", + "multi_arg": true + } + ], "outputs": [ { - "type": "TokenIdentifier" + "type": "StakeProxyResult" + } + ] + }, + { + "name": "claimDualYield", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "opt_orig_caller", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "ClaimDualYieldResult" + } + ] + }, + { + "name": "unstakeFarmTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "pair_first_token_min_amount", + "type": "BigUint" + }, + { + "name": "pair_second_token_min_amount", + "type": "BigUint" + }, + { + "name": "opt_orig_caller", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "UnstakeResult" } ] } ], + "events": [], "hasCallback": true, "types": { - "EsdtTokenPayment": { + "ClaimDualYieldResult": { "type": "struct", "fields": [ { - "name": "token_type", - "type": "EsdtTokenType" + "name": "lp_farm_rewards", + "type": "EsdtTokenPayment" + }, + { + "name": "staking_farm_rewards", + "type": "EsdtTokenPayment" }, + { + "name": "new_dual_yield_tokens", + "type": "EsdtTokenPayment" + } + ] + }, + "EsdtTokenPayment": { + "type": "struct", + "fields": [ { "name": "token_identifier", "type": "TokenIdentifier" @@ -205,28 +303,37 @@ } ] }, - "EsdtTokenType": { - "type": "enum", - "variants": [ + "StakeProxyResult": { + "type": "struct", + "fields": [ { - "name": "Fungible", - "discriminant": 0 + "name": "dual_yield_tokens", + "type": "EsdtTokenPayment" }, { - "name": "NonFungible", - "discriminant": 1 + "name": "boosted_rewards", + "type": "EsdtTokenPayment" + } + ] + }, + "UnstakeResult": { + "type": "struct", + "fields": [ + { + "name": "other_token_payment", + "type": "EsdtTokenPayment" }, { - "name": "SemiFungible", - "discriminant": 2 + "name": "lp_farm_rewards", + "type": "EsdtTokenPayment" }, { - "name": "Meta", - "discriminant": 3 + "name": "staking_rewards", + "type": "EsdtTokenPayment" }, { - "name": "Invalid", - "discriminant": 4 + "name": "unbond_staking_farm_token", + "type": "EsdtTokenPayment" } ] } diff --git a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts index 8916843e0..8e47277c4 100644 --- a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts +++ b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts @@ -7,11 +7,9 @@ import { ruleOfThree } from 'src/helpers/helpers'; import { InputTokenModel } from 'src/models/inputToken.model'; import { TransactionModel } from 'src/models/transaction.model'; import { FarmFactoryService } from 'src/modules/farm/farm.factory'; -import { FarmVersion } from 'src/modules/farm/models/farm.model'; import { PairService } from 'src/modules/pair/services/pair.service'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; -import { farmVersion } from 'src/utils/farm.utils'; import { generateLogMessage } from 'src/utils/generate-log-message'; import { tokenIdentifier } from 'src/utils/token.converters'; import { Logger } from 'winston'; @@ -181,10 +179,6 @@ export class StakingProxyTransactionService { new BigUIntValue(amount1Min), ]; - if (farmVersion(farmAddress) === FarmVersion.V2) { - endpointArgs.push(new BigUIntValue(liquidityPositionAmount)); - } - return contract.methodsExplicit .unstakeFarmTokens(endpointArgs) .withSingleESDTNFTTransfer( From ea7769ac72b522cf3267702e7184c5eb30054ea7 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 30 Oct 2023 16:03:40 +0200 Subject: [PATCH 053/313] MEX-406: fix user total staked position setter Signed-off-by: Claudiu Lataretu --- src/modules/staking/services/staking.setter.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/staking/services/staking.setter.service.ts b/src/modules/staking/services/staking.setter.service.ts index 71c23709e..c2ffbedc5 100644 --- a/src/modules/staking/services/staking.setter.service.ts +++ b/src/modules/staking/services/staking.setter.service.ts @@ -184,7 +184,7 @@ export class StakingSetterService extends GenericSetterService { ): Promise { return this.setData( this.getCacheKey( - 'userTotalFarmPosition', + 'userTotalStakePosition', stakeAddress, userAddress, ), From 664678961d906e2c46a9aacc56d3e965605b1b60 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 30 Oct 2023 17:34:01 +0200 Subject: [PATCH 054/313] MEX-408: Add dual yield token migration - update total farm position for underlying staking or farm tokens based on farm migration nonce from contracts Signed-off-by: Claudiu Lataretu --- .../staking.proxy.transactions.service.ts | 70 +++++++++++++++++++ .../staking-proxy/staking.proxy.module.ts | 2 + .../staking-proxy/staking.proxy.resolver.ts | 18 +++++ .../staking.proxy.address.validator.ts | 25 +++++++ 4 files changed, 115 insertions(+) create mode 100644 src/modules/staking-proxy/validators/staking.proxy.address.validator.ts diff --git a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts index 8e47277c4..9028721bd 100644 --- a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts +++ b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts @@ -20,6 +20,8 @@ import { } from '../models/staking.proxy.args.model'; import { StakingProxyService } from './staking.proxy.service'; import { StakingProxyAbiService } from './staking.proxy.abi.service'; +import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; +import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; @Injectable() export class StakingProxyTransactionService { @@ -28,6 +30,8 @@ export class StakingProxyTransactionService { private readonly stakeProxyAbi: StakingProxyAbiService, private readonly pairService: PairService, private readonly farmFactory: FarmFactoryService, + private readonly farmAbiV2: FarmAbiServiceV2, + private readonly stakingAbi: StakingAbiService, private readonly mxProxy: MXProxyService, private readonly apiService: MXApiService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, @@ -195,6 +199,72 @@ export class StakingProxyTransactionService { .toPlainObject(); } + async migrateDualYieldTokens( + proxyStakeAddress: string, + userAddress: string, + ): Promise { + const [dualYieldTokenID, farmAddress, stakingAddress, userNftsCount] = + await Promise.all([ + this.stakeProxyAbi.dualYieldTokenID(proxyStakeAddress), + this.stakeProxyAbi.lpFarmAddress(proxyStakeAddress), + this.stakeProxyAbi.stakingFarmAddress(proxyStakeAddress), + this.apiService.getNftsCountForUser(userAddress), + ]); + + const userNfts = await this.apiService.getNftsForUser( + userAddress, + 0, + userNftsCount, + 'MetaESDT', + [dualYieldTokenID], + ); + + if (userNfts.length === 0) { + return undefined; + } + + const [farmMigrationNonce, stakingMigrationNonce] = await Promise.all([ + this.farmAbiV2.farmPositionMigrationNonce(farmAddress), + this.stakingAbi.farmPositionMigrationNonce(stakingAddress), + ]); + + const userDualYiledTokensAttrs = + this.stakeProxyService.decodeDualYieldTokenAttributes({ + batchAttributes: userNfts.map((nft) => { + return { + identifier: nft.identifier, + attributes: nft.attributes, + }; + }), + }); + + const payments: InputTokenModel[] = []; + userDualYiledTokensAttrs.forEach( + (dualYieldTokenAttrs, index: number) => { + if ( + dualYieldTokenAttrs.lpFarmTokenNonce < farmMigrationNonce || + dualYieldTokenAttrs.stakingFarmTokenNonce < + stakingMigrationNonce + ) { + payments.push({ + tokenID: userNfts[index].identifier, + nonce: userNfts[index].nonce, + amount: userNfts[index].balance, + }); + } + }, + ); + + if (payments.length > 0) { + return this.claimDualYield(userAddress, { + proxyStakingAddress: proxyStakeAddress, + payments: payments, + }); + } + + return undefined; + } + private async validateInputTokens( proxyStakeAddress: string, tokens: InputTokenModel[], diff --git a/src/modules/staking-proxy/staking.proxy.module.ts b/src/modules/staking-proxy/staking.proxy.module.ts index eee57dd5a..2714df5a7 100644 --- a/src/modules/staking-proxy/staking.proxy.module.ts +++ b/src/modules/staking-proxy/staking.proxy.module.ts @@ -12,6 +12,7 @@ import { StakingProxyService } from './services/staking.proxy.service'; import { StakingProxySetterService } from './services/staking.proxy.setter.service'; import { StakingProxyTransactionService } from './services/staking.proxy.transactions.service'; import { StakingProxyResolver } from './staking.proxy.resolver'; +import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; @Module({ imports: [ @@ -20,6 +21,7 @@ import { StakingProxyResolver } from './staking.proxy.resolver'; MXCommunicationModule, PairModule, FarmModule, + FarmModuleV2, StakingModule, TokenModule, RemoteConfigModule, diff --git a/src/modules/staking-proxy/staking.proxy.resolver.ts b/src/modules/staking-proxy/staking.proxy.resolver.ts index fb2204894..558592369 100644 --- a/src/modules/staking-proxy/staking.proxy.resolver.ts +++ b/src/modules/staking-proxy/staking.proxy.resolver.ts @@ -25,6 +25,7 @@ import { import { StakingProxyService } from './services/staking.proxy.service'; import { StakingProxyTransactionService } from './services/staking.proxy.transactions.service'; import { StakingProxyAbiService } from './services/staking.proxy.abi.service'; +import { StakingProxyAddressValidationPipe } from './validators/staking.proxy.address.validator'; @Resolver(() => StakingProxyModel) export class StakingProxyResolver { @@ -138,4 +139,21 @@ export class StakingProxyResolver { ): Promise { return this.stakingProxyService.getUnstakeTokensReceived(position); } + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => TransactionModel, { + nullable: true, + description: + 'Update staking / farm positions for total farm position from dual yield token', + }) + async migrateDualYieldTokens( + @Args('proxyStakingAddress', StakingProxyAddressValidationPipe) + proxyStakingAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.stakingProxyTransaction.migrateDualYieldTokens( + proxyStakingAddress, + user.address, + ); + } } diff --git a/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts b/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts new file mode 100644 index 000000000..d3d0392a8 --- /dev/null +++ b/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts @@ -0,0 +1,25 @@ +import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; +import { UserInputError } from '@nestjs/apollo'; +import { RemoteConfigGetterService } from 'src/modules/remote-config/remote-config.getter.service'; +import { Address } from '@multiversx/sdk-core/out'; + +@Injectable() +export class StakingProxyAddressValidationPipe implements PipeTransform { + constructor(private readonly remoteConfig: RemoteConfigGetterService) {} + + async transform(value: string, metadata: ArgumentMetadata) { + let address: Address; + try { + address = Address.fromBech32(value); + } catch (error) { + throw new UserInputError('Invalid address'); + } + const stakingProxyAddresses = + await this.remoteConfig.getStakingProxyAddresses(); + if (!stakingProxyAddresses.includes(address.bech32())) { + throw new UserInputError('Invalid staking proxy address'); + } + + return value; + } +} From 9e7a423df63793ae5c76ed7a50ddca83a1bb3ecb Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 30 Oct 2023 18:23:12 +0200 Subject: [PATCH 055/313] MEX-408: add unit tests for dual yield token migration query Signed-off-by: Claudiu Lataretu --- .../mocks/staking.proxy.abi.service.mock.ts | 2 +- .../staking.proxy.getter.service.mock.ts | 2 +- .../staking.proxy.transactions.service.ts | 2 +- .../staking.proxy.transaction.service.spec.ts | 125 ++++++++++++++++-- 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/modules/staking-proxy/mocks/staking.proxy.abi.service.mock.ts b/src/modules/staking-proxy/mocks/staking.proxy.abi.service.mock.ts index 818bcce1a..741037e4e 100644 --- a/src/modules/staking-proxy/mocks/staking.proxy.abi.service.mock.ts +++ b/src/modules/staking-proxy/mocks/staking.proxy.abi.service.mock.ts @@ -19,7 +19,7 @@ export class StakingProxyAbiServiceMock implements IStakingProxyAbiService { return 'STAKETOK-1111'; } async dualYieldTokenID(stakingProxyAddress: string): Promise { - return 'METASTAKE-1234'; + return 'METASTAKE-123456'; } async lpFarmTokenID(stakingProxyAddress: string): Promise { return 'EGLDTOK4FL-abcdef'; diff --git a/src/modules/staking-proxy/mocks/staking.proxy.getter.service.mock.ts b/src/modules/staking-proxy/mocks/staking.proxy.getter.service.mock.ts index 45ae9a382..e14aae406 100644 --- a/src/modules/staking-proxy/mocks/staking.proxy.getter.service.mock.ts +++ b/src/modules/staking-proxy/mocks/staking.proxy.getter.service.mock.ts @@ -22,7 +22,7 @@ export class StakingProxyGetterServiceMock { } async getDualYieldTokenID(stakingProxyAddress: string): Promise { - return 'METASTAKE-1234'; + return 'METASTAKE-123456'; } async getLpFarmTokenID(stakingProxyAddress: string): Promise { diff --git a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts index 9028721bd..a31414f8f 100644 --- a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts +++ b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts @@ -247,7 +247,7 @@ export class StakingProxyTransactionService { stakingMigrationNonce ) { payments.push({ - tokenID: userNfts[index].identifier, + tokenID: userNfts[index].collection, nonce: userNfts[index].nonce, amount: userNfts[index].balance, }); diff --git a/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts b/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts index 1df66b9be..8d5a72b20 100644 --- a/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts +++ b/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts @@ -12,8 +12,6 @@ import { FarmServiceV1_3 } from 'src/modules/farm/v1.3/services/farm.v1.3.servic import { FarmServiceV2 } from 'src/modules/farm/v2/services/farm.v2.service'; import { FarmComputeServiceV1_2 } from 'src/modules/farm/v1.2/services/farm.v1.2.compute.service'; import { FarmComputeServiceV1_3 } from 'src/modules/farm/v1.3/services/farm.v1.3.compute.service'; -import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; -import { FarmAbiServiceMock } from 'src/modules/farm/mocks/farm.abi.service.mock'; import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; import { ContextGetterServiceProvider } from 'src/services/context/mocks/context.getter.service.mock'; import { WeekTimekeepingAbiServiceProvider } from 'src/submodules/week-timekeeping/mocks/week.timekeeping.abi.service.mock'; @@ -41,6 +39,9 @@ import { WinstonModule } from 'nest-winston'; import { ApiConfigService } from 'src/helpers/api.config.service'; import winston from 'winston'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { FarmAbiServiceProviderV2 } from 'src/modules/farm/mocks/farm.v2.abi.service.mock'; +import { StakingAbiServiceProvider } from 'src/modules/staking/mocks/staking.abi.service.mock'; describe('StakingProxyTransactionService', () => { let module: TestingModule; @@ -76,10 +77,8 @@ describe('StakingProxyTransactionService', () => { FarmComputeServiceV2, FarmAbiServiceProviderV1_2, FarmAbiServiceProviderV1_3, - { - provide: FarmAbiServiceV2, - useClass: FarmAbiServiceMock, - }, + FarmAbiServiceProviderV2, + StakingAbiServiceProvider, ContextGetterServiceProvider, WeekTimekeepingAbiServiceProvider, WeekTimekeepingComputeService, @@ -158,7 +157,7 @@ describe('StakingProxyTransactionService', () => { amount: '1', }, { - tokenID: 'METASTAKE-1234', + tokenID: 'METASTAKE-123456', nonce: 1, amount: '1', }, @@ -176,7 +175,7 @@ describe('StakingProxyTransactionService', () => { sender: Address.Zero().bech32(), receiver: Address.Zero().bech32(), data: encodeTransactionData( - `MultiESDTNFTTransfer@${Address.Zero().bech32()}@02@EGLDTOK4FL-abcdef@01@1@METASTAKE-1234@01@1@stakeFarmTokens`, + `MultiESDTNFTTransfer@${Address.Zero().bech32()}@02@EGLDTOK4FL-abcdef@01@1@METASTAKE-123456@01@1@stakeFarmTokens`, ), options: undefined, signature: undefined, @@ -236,4 +235,114 @@ describe('StakingProxyTransactionService', () => { } }, ); + + describe('dual yield token migration', () => { + const expectedTransactionStub = { + nonce: 0, + value: '0', + receiver: + 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@0000000000000000000000000000000000000000000000000000000000000000@01@METASTAKE-123456@01@1000000000000000000@claimDualYield`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }; + + it('migrate on staking token', async () => { + const service = module.get( + StakingProxyTransactionService, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'METASTAKE-123456-01', + collection: 'METASTAKE-123456', + attributes: + 'AAAAAAAAAAsAAAAIAvm+bx2CRdUAAAAAAAAAAQAAAAkDigAw1aDQ9RM=', + nonce: 1, + type: 'MetaESDT', + name: 'MetaStaked', + creator: Address.Zero().bech32(), + balance: '1000000000000000000', + decimals: 18, + ticker: 'METASTAKE-123456', + }, + ]); + + const transaction = await service.migrateDualYieldTokens( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); + + expect(transaction).toEqual(expectedTransactionStub); + }); + + it('migrate on lp farm token', async () => { + const service = module.get( + StakingProxyTransactionService, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'METASTAKE-123456-01', + collection: 'METASTAKE-123456', + attributes: + 'AAAAAAAAAAkAAAAIAvm+bx2CRdUAAAAAAAAAAwAAAAkDigAw1aDQ9RM=', + nonce: 1, + type: 'MetaESDT', + name: 'MetaStaked', + creator: Address.Zero().bech32(), + balance: '1000000000000000000', + decimals: 18, + ticker: 'METASTAKE-123456', + }, + ]); + + const transaction = await service.migrateDualYieldTokens( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); + + expect(transaction).toEqual(expectedTransactionStub); + }); + + it('migrate on both tokens', async () => { + const service = module.get( + StakingProxyTransactionService, + ); + const mxApi = module.get(MXApiService); + jest.spyOn(mxApi, 'getNftsForUser').mockResolvedValue([ + { + identifier: 'METASTAKE-123456-01', + collection: 'METASTAKE-123456', + attributes: + 'AAAAAAAAAAkAAAAIAvm+bx2CRdUAAAAAAAAAAQAAAAkDigAw1aDQ9RM=', + nonce: 1, + type: 'MetaESDT', + name: 'MetaStaked', + creator: Address.Zero().bech32(), + balance: '1000000000000000000', + decimals: 18, + ticker: 'METASTAKE-123456', + }, + ]); + + const transaction = await service.migrateDualYieldTokens( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); + + expect(transaction).toEqual(expectedTransactionStub); + }); + }); }); From 63b24e4475641a385358c6cacd8f715135ee78b9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 30 Oct 2023 23:21:58 +0200 Subject: [PATCH 056/313] MEX-397: update proxy-dex-v2 abi to latest version Signed-off-by: Claudiu Lataretu --- src/abis/proxy-dex-v2.abi.json | 194 ++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 28 deletions(-) diff --git a/src/abis/proxy-dex-v2.abi.json b/src/abis/proxy-dex-v2.abi.json index 611901b87..50354ce89 100644 --- a/src/abis/proxy-dex-v2.abi.json +++ b/src/abis/proxy-dex-v2.abi.json @@ -1,45 +1,47 @@ { "buildInfo": { "rustc": { - "version": "1.66.0-nightly", - "commitHash": "b8c35ca26b191bb9a9ac669a4b3f4d3d52d97fb1", - "commitDate": "2022-10-15", + "version": "1.73.0-nightly", + "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", + "commitDate": "2023-07-15", "channel": "Nightly", - "short": "rustc 1.66.0-nightly (b8c35ca26 2022-10-15)" + "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" }, "contractCrate": { "name": "proxy_dex", "version": "0.0.0", - "git_version": "v1.6.0-659-gbc15fd5b-modified" + "gitVersion": "v1.6.0-1530-g720dae7e" }, "framework": { - "name": "elrond-wasm", - "version": "0.36.0" + "name": "multiversx-sc", + "version": "0.43.3" } }, "name": "ProxyDexImpl", "constructor": { - "docs": [ - "asset_token_id: underlying asset token ID, which is used to interact with", - " pair/farm contracts", - "", - "locked_token_factory_address_pairs: pairs of (token ID, address)", - " token_id: the LOCKED token ID that is generated by the given factory address" - ], "inputs": [ { - "name": "asset_token_id", + "name": "old_locked_token_id", "type": "TokenIdentifier" }, { - "name": "locked_token_factory_address_pairs", - "type": "variadic>", - "multi_arg": true + "name": "old_factory_address", + "type": "Address" + }, + { + "name": "energy_factory_address", + "type": "Address" } ], "outputs": [] }, "endpoints": [ + { + "name": "upgrade", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, { "name": "registerProxyPair", "onlyOwner": true, @@ -63,6 +65,19 @@ ], "outputs": [] }, + { + "name": "setTransferRoleWrappedLpToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, { "name": "registerProxyFarm", "onlyOwner": true, @@ -86,6 +101,19 @@ ], "outputs": [] }, + { + "name": "setTransferRoleWrappedFarmToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, { "name": "getAssetTokenId", "mutability": "readonly", @@ -107,6 +135,26 @@ } ] }, + { + "name": "getOldLockedTokenId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getOldFactoryAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, { "name": "getWrappedLpTokenId", "mutability": "readonly", @@ -251,6 +299,24 @@ } ] }, + { + "name": "increaseProxyPairTokenEnergy", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "lock_epochs", + "type": "u64" + } + ], + "outputs": [ + { + "type": "EsdtTokenPayment" + } + ] + }, { "name": "enterFarmProxy", "mutability": "mutable", @@ -261,9 +327,17 @@ { "name": "farm_address", "type": "Address" + }, + { + "name": "opt_original_caller", + "type": "optional
", + "multi_arg": true } ], "outputs": [ + { + "type": "EsdtTokenPayment" + }, { "type": "EsdtTokenPayment" } @@ -276,22 +350,20 @@ "*" ], "inputs": [ - { - "name": "exit_amount", - "type": "BigUint" - }, { "name": "farm_address", "type": "Address" + }, + { + "name": "opt_original_caller", + "type": "optional
", + "multi_arg": true } ], "outputs": [ { "type": "EsdtTokenPayment" }, - { - "type": "EsdtTokenPayment" - }, { "type": "EsdtTokenPayment" } @@ -307,6 +379,11 @@ { "name": "farm_address", "type": "Address" + }, + { + "name": "opt_original_caller", + "type": "optional
", + "multi_arg": true } ], "outputs": [ @@ -319,15 +396,15 @@ ] }, { - "name": "compoundRewardsProxy", + "name": "increaseProxyFarmTokenEnergy", "mutability": "mutable", "payableInTokens": [ "*" ], "inputs": [ { - "name": "farm_address", - "type": "Address" + "name": "lock_epochs", + "type": "u64" } ], "outputs": [ @@ -366,6 +443,67 @@ "type": "EsdtTokenPayment" } ] + }, + { + "name": "setEnergyFactoryAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "sc_address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getEnergyFactoryAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "addSCAddressToWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "removeSCAddressFromWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "isSCAddressWhitelisted", + "mutability": "readonly", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "bool" + } + ] } ], "events": [ From f9d849ba554ff0ad2d801f25fae32829c4fd851a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 30 Oct 2023 23:58:32 +0200 Subject: [PATCH 057/313] MEX-397: add query for increase proxy pair token energy transaction Signed-off-by: Claudiu Lataretu --- src/config/default.json | 3 +- src/modules/proxy/proxy.module.ts | 2 + .../proxy/proxy.transaction.resolver.ts | 44 +++++++++++++++++++ .../proxy.pair.transactions.service.ts | 28 ++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 91dbd8220..dce40981b 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -261,7 +261,8 @@ "withTokenMerge": 40000000 }, "removeLiquidity": 30000000, - "defaultMergeWLPT": 20000000 + "defaultMergeWLPT": 20000000, + "increaseEnergy": 20000000 }, "farms": { "v1.2": { diff --git a/src/modules/proxy/proxy.module.ts b/src/modules/proxy/proxy.module.ts index 36d87314e..4eb0cb090 100644 --- a/src/modules/proxy/proxy.module.ts +++ b/src/modules/proxy/proxy.module.ts @@ -16,6 +16,7 @@ import { ProxyQueryResolver } from './proxy.query.resolver'; import { ProxyResolver } from './proxy.resolver'; import { WrappedLpTokenAttributesResolverV2 } from './wrappedLp.token.v2.resolver'; import { WrappedFarmTokenResolverV2 } from './wrappedFarm.token.v2.resolver'; +import { EnergyModule } from '../energy/energy.module'; @Module({ imports: [ @@ -27,6 +28,7 @@ import { WrappedFarmTokenResolverV2 } from './wrappedFarm.token.v2.resolver'; forwardRef(() => ProxyFarmModule), forwardRef(() => ProxyModuleV2), FarmModule, + EnergyModule, ], providers: [ ProxyAbiService, diff --git a/src/modules/proxy/proxy.transaction.resolver.ts b/src/modules/proxy/proxy.transaction.resolver.ts index 7bca6af60..886637397 100644 --- a/src/modules/proxy/proxy.transaction.resolver.ts +++ b/src/modules/proxy/proxy.transaction.resolver.ts @@ -24,11 +24,15 @@ import { InputTokenModel } from 'src/models/inputToken.model'; import { LiquidityTokensValidationPipe } from './validators/add.liquidity.input.validator'; import { ProxyService } from './services/proxy.service'; import { scAddress } from 'src/config'; +import { GraphQLError } from 'graphql'; +import { ApolloServerErrorCode } from '@apollo/server/errors'; +import { EnergyAbiService } from '../energy/services/energy.abi.service'; @Resolver() export class ProxyTransactionResolver { constructor( private readonly proxyService: ProxyService, + private readonly energyAbi: EnergyAbiService, private readonly transactionsProxyPairService: ProxyPairTransactionsService, private readonly transactionsProxyFarmService: ProxyFarmTransactionsService, ) {} @@ -195,4 +199,44 @@ export class ProxyTransactionResolver { args, ); } + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => TransactionModel) + async increaseProxyPairTokenEnergy( + @Args('payment') payment: InputTokenModel, + @Args('lockEpochs') lockEpochs: number, + @AuthUser() user: UserAuthResult, + ): Promise { + let proxyAddress: string; + try { + proxyAddress = await this.proxyService.getProxyAddressByToken( + payment.tokenID, + ); + + if (proxyAddress !== scAddress.proxyDexAddress.v2) { + throw new Error('Wrapped lp token is not supported'); + } + + const lockOptions = await this.energyAbi.lockOptions(); + if ( + lockOptions.find( + (option) => option.lockEpochs === lockEpochs, + ) === undefined + ) { + throw new Error('Invalid lock epochs!'); + } + } catch (error) { + throw new GraphQLError(error.message, { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } + return this.transactionsProxyPairService.increaseProxyPairTokenEnergy( + user.address, + proxyAddress, + payment, + lockEpochs, + ); + } } diff --git a/src/modules/proxy/services/proxy-pair/proxy.pair.transactions.service.ts b/src/modules/proxy/services/proxy-pair/proxy.pair.transactions.service.ts index b8dbf2c48..1026b2ea6 100644 --- a/src/modules/proxy/services/proxy-pair/proxy.pair.transactions.service.ts +++ b/src/modules/proxy/services/proxy-pair/proxy.pair.transactions.service.ts @@ -4,6 +4,7 @@ import { BigUIntValue, BytesValue, TypedValue, + U64Value, } from '@multiversx/sdk-core/out/smartcontracts/typesystem'; import { Address, TokenTransfer } from '@multiversx/sdk-core'; import { TransactionModel } from 'src/models/transaction.model'; @@ -236,6 +237,33 @@ export class ProxyPairTransactionsService { .toPlainObject(); } + async increaseProxyPairTokenEnergy( + sender: string, + proxyAddress: string, + payment: InputTokenModel, + lockEpochs: number, + ): Promise { + const contract = await this.mxProxy.getProxyDexSmartContract( + proxyAddress, + ); + return contract.methodsExplicit + .increaseProxyPairTokenEnergy([ + new U64Value(new BigNumber(lockEpochs)), + ]) + .withSingleESDTNFTTransfer( + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenID, + payment.nonce, + new BigNumber(payment.amount), + ), + ) + .withSender(Address.fromString(sender)) + .withGasLimit(gasConfig.proxy.pairs.increaseEnergy) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + private async convertInputTokenstoESDTTokens( tokens: InputTokenModel[], ): Promise { From 5f6ebd709ecb0da4165cc02edb68c23ee9a7cd67 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 31 Oct 2023 21:39:10 +0200 Subject: [PATCH 058/313] MEX-407: use sdk-exchange from npm repository Signed-off-by: Claudiu Lataretu --- package-lock.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index cedf54cdb..0665be058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2857,8 +2857,8 @@ }, "node_modules/@multiversx/sdk-exchange": { "version": "0.2.20", - "resolved": "http://localhost:4873/@multiversx/sdk-exchange/-/sdk-exchange-0.2.20.tgz", - "integrity": "sha512-PK5XKlsg//Iu4cvX2ffaY6mJrm4tHtruJNxpCzAIrHd3eBaonmcqR/EqEHjZVAaUgtbxF8SvWoBKoHy6MRQWfw==", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-exchange/-/sdk-exchange-0.2.20.tgz", + "integrity": "sha512-qUmdoYsTga/mi3wyjJcmPhqRxP34TWN9bpRWsVj3j/3pkNAnpu6wnv/9Jc7QPG/+mppkox3KBSKgQtyrK845Mg==", "dependencies": { "@multiversx/sdk-core": "^11.2.0", "bignumber.js": "9.0.1" @@ -2866,7 +2866,7 @@ }, "node_modules/@multiversx/sdk-exchange/node_modules/@multiversx/sdk-core": { "version": "11.6.0", - "resolved": "http://localhost:4873/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", "integrity": "sha512-bP1fmp84iEAJEdaprvw8M9FeqJVOWTmv8vhhiaYW6YTV/ztlB+niv1CFsYk0dWFWIfKzn+eT4CvQjl8OTwBC/A==", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -2881,7 +2881,7 @@ }, "node_modules/@multiversx/sdk-exchange/node_modules/protobufjs": { "version": "6.11.3", - "resolved": "http://localhost:4873/protobufjs/-/protobufjs-6.11.3.tgz", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "hasInstallScript": true, "dependencies": { @@ -19007,8 +19007,8 @@ }, "@multiversx/sdk-exchange": { "version": "0.2.20", - "resolved": "http://localhost:4873/@multiversx/sdk-exchange/-/sdk-exchange-0.2.20.tgz", - "integrity": "sha512-PK5XKlsg//Iu4cvX2ffaY6mJrm4tHtruJNxpCzAIrHd3eBaonmcqR/EqEHjZVAaUgtbxF8SvWoBKoHy6MRQWfw==", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-exchange/-/sdk-exchange-0.2.20.tgz", + "integrity": "sha512-qUmdoYsTga/mi3wyjJcmPhqRxP34TWN9bpRWsVj3j/3pkNAnpu6wnv/9Jc7QPG/+mppkox3KBSKgQtyrK845Mg==", "requires": { "@multiversx/sdk-core": "^11.2.0", "bignumber.js": "9.0.1" @@ -19016,7 +19016,7 @@ "dependencies": { "@multiversx/sdk-core": { "version": "11.6.0", - "resolved": "http://localhost:4873/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-11.6.0.tgz", "integrity": "sha512-bP1fmp84iEAJEdaprvw8M9FeqJVOWTmv8vhhiaYW6YTV/ztlB+niv1CFsYk0dWFWIfKzn+eT4CvQjl8OTwBC/A==", "requires": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -19031,7 +19031,7 @@ }, "protobufjs": { "version": "6.11.3", - "resolved": "http://localhost:4873/protobufjs/-/protobufjs-6.11.3.tgz", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", From ce45288983245633999b60d3fed83bef77afb964 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 1 Nov 2023 12:17:55 +0200 Subject: [PATCH 059/313] MEX-407: fix staking unit tests imports Signed-off-by: Claudiu Lataretu --- .../staking/specs/staking.compute.service.spec.ts | 10 ++++++++++ src/modules/staking/specs/staking.service.spec.ts | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/modules/staking/specs/staking.compute.service.spec.ts b/src/modules/staking/specs/staking.compute.service.spec.ts index 96424b1f4..d86c137c4 100644 --- a/src/modules/staking/specs/staking.compute.service.spec.ts +++ b/src/modules/staking/specs/staking.compute.service.spec.ts @@ -18,6 +18,11 @@ import { ApiConfigService } from 'src/helpers/api.config.service'; import winston from 'winston'; import { TokenComputeServiceProvider } from 'src/modules/tokens/mocks/token.compute.service.mock'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { WeekTimekeepingAbiServiceProvider } from 'src/submodules/week-timekeeping/mocks/week.timekeeping.abi.service.mock'; +import { WeeklyRewardsSplittingAbiServiceProvider } from 'src/submodules/weekly-rewards-splitting/mocks/weekly.rewards.splitting.abi.mock'; +import { WeekTimekeepingComputeService } from 'src/submodules/week-timekeeping/services/week-timekeeping.compute.service'; +import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; +import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; describe('StakingComputeService', () => { let module: TestingModule; @@ -35,8 +40,13 @@ describe('StakingComputeService', () => { StakingComputeService, StakingService, StakingAbiServiceProvider, + EnergyAbiServiceProvider, TokenServiceProvider, TokenComputeServiceProvider, + WeekTimekeepingAbiServiceProvider, + WeekTimekeepingComputeService, + WeeklyRewardsSplittingAbiServiceProvider, + WeeklyRewardsSplittingComputeService, ContextGetterServiceProvider, MXApiServiceProvider, RemoteConfigGetterServiceProvider, diff --git a/src/modules/staking/specs/staking.service.spec.ts b/src/modules/staking/specs/staking.service.spec.ts index 329ee5ec0..0fd5a79a2 100644 --- a/src/modules/staking/specs/staking.service.spec.ts +++ b/src/modules/staking/specs/staking.service.spec.ts @@ -15,6 +15,11 @@ import { WinstonModule } from 'nest-winston'; import winston from 'winston'; import { TokenComputeServiceProvider } from 'src/modules/tokens/mocks/token.compute.service.mock'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { WeekTimekeepingAbiServiceProvider } from 'src/submodules/week-timekeeping/mocks/week.timekeeping.abi.service.mock'; +import { WeeklyRewardsSplittingAbiServiceProvider } from 'src/submodules/weekly-rewards-splitting/mocks/weekly.rewards.splitting.abi.mock'; +import { WeekTimekeepingComputeService } from 'src/submodules/week-timekeeping/services/week-timekeeping.compute.service'; +import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; +import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; describe('StakingService', () => { let module: TestingModule; @@ -32,12 +37,16 @@ describe('StakingService', () => { StakingService, StakingAbiServiceProvider, StakingComputeService, + EnergyAbiServiceProvider, ContextGetterServiceProvider, + WeekTimekeepingAbiServiceProvider, + WeekTimekeepingComputeService, + WeeklyRewardsSplittingAbiServiceProvider, + WeeklyRewardsSplittingComputeService, RemoteConfigGetterServiceProvider, MXProxyServiceProvider, MXApiServiceProvider, MXGatewayService, - ApiConfigService, TokenServiceProvider, TokenComputeServiceProvider, ApiConfigService, From 1e6098880eac54d54e151933adfb5c770babcf6f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 2 Nov 2023 15:43:26 +0200 Subject: [PATCH 060/313] MEX-407: uncomment caching decorator Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.compute.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index b29d4fcf7..634f8759f 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -399,11 +399,11 @@ export class StakingComputeService { @ErrorLoggerAsync({ logArgs: true, }) - // @GetOrSetCache({ - // baseKey: 'stake', - // remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - // localTtl: CacheTtlInfo.ContractBalance.localTtl, - // }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, + localTtl: CacheTtlInfo.ContractBalance.localTtl, + }) async userAccumulatedRewards( scAddress: string, userAddress: string, From 27341693ea1b8c41a5214b04bfb854acf7642dc0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 13 Nov 2023 13:42:17 +0200 Subject: [PATCH 061/313] MEX-410: position creator initial module Signed-off-by: Claudiu Lataretu --- src/abis/auto-pos-creator.abi.json | 253 ++++++++++++++++++ src/config/devnet2.json | 3 +- .../models/position.creator.model.ts | 28 ++ .../position.creator.module.ts | 18 ++ src/public.app.module.ts | 2 + .../mx.proxy.service.ts | 8 + 6 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 src/abis/auto-pos-creator.abi.json create mode 100644 src/modules/position-creator/models/position.creator.model.ts create mode 100644 src/modules/position-creator/position.creator.module.ts diff --git a/src/abis/auto-pos-creator.abi.json b/src/abis/auto-pos-creator.abi.json new file mode 100644 index 000000000..57682d9f5 --- /dev/null +++ b/src/abis/auto-pos-creator.abi.json @@ -0,0 +1,253 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.73.0-nightly", + "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", + "commitDate": "2023-07-15", + "channel": "Nightly", + "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" + }, + "contractCrate": { + "name": "auto-pos-creator", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.43.3" + } + }, + "name": "AutoPosCreator", + "constructor": { + "inputs": [ + { + "name": "egld_wrapper_address", + "type": "Address" + }, + { + "name": "wegld_token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + "endpoints": [ + { + "name": "upgrade", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "addPairsToWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "pair_addresses", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "removePairsFromWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "pair_addresses", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "createPosFromSingleToken", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "dest_pair_address", + "type": "Address" + }, + { + "name": "steps", + "type": "StepsToPerform" + }, + { + "name": "add_liq_first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount_out", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "docs": [ + "Create pos from two payments, entering the pair for the two tokens", + "It will try doing this with the optimal amounts,", + "performing swaps before adding liqudity if necessary" + ], + "name": "createPosFromTwoTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "steps", + "type": "StepsToPerform" + }, + { + "name": "add_liq_first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount_out", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "createPosFromLp", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "steps", + "type": "StepsToPerform" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "fullExitPos", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "second_token_min_amont_out", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" + } + ] + } + ], + "events": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "FarmConfig": { + "type": "struct", + "fields": [ + { + "name": "state", + "type": "State" + }, + { + "name": "farm_token_id", + "type": "TokenIdentifier" + }, + { + "name": "farming_token_id", + "type": "TokenIdentifier" + } + ] + }, + "MetastakingConfig": { + "type": "struct", + "fields": [ + { + "name": "dual_yield_token_id", + "type": "TokenIdentifier" + }, + { + "name": "lp_farm_token_id", + "type": "TokenIdentifier" + } + ] + }, + "State": { + "type": "enum", + "variants": [ + { + "name": "Inactive", + "discriminant": 0 + }, + { + "name": "Active", + "discriminant": 1 + }, + { + "name": "PartialActive", + "discriminant": 2 + } + ] + }, + "StepsToPerform": { + "type": "enum", + "variants": [ + { + "name": "AddLiquidity", + "discriminant": 0 + }, + { + "name": "EnterFarm", + "discriminant": 1 + }, + { + "name": "EnterMetastaking", + "discriminant": 2 + } + ] + } + } +} diff --git a/src/config/devnet2.json b/src/config/devnet2.json index c9fc12f6c..2b6c925c0 100644 --- a/src/config/devnet2.json +++ b/src/config/devnet2.json @@ -28,7 +28,8 @@ "energyUpdate": "erd1qqqqqqqqqqqqqpgqz2ctz77j9we0r99mnhehv24he3pxmsmq0n4sntf4n7", "tokenUnstake": "erd1qqqqqqqqqqqqqpgqu6s4e5mndgf37psxw9mdlmjavp2er3f00n4snwyk3q", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqasr2asq07e6ur274eecx3vd0pnej2vxs0n4sqqyp52", - "escrow": "erd1qqqqqqqqqqqqqpgqnx4t9kwy5n9atuumvnrluv5yrwmpxzx60n4sj497ms" + "escrow": "erd1qqqqqqqqqqqqqpgqnx4t9kwy5n9atuumvnrluv5yrwmpxzx60n4sj497ms", + "positionCreator": "erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6" }, "governance": { "oldEnergy": { diff --git a/src/modules/position-creator/models/position.creator.model.ts b/src/modules/position-creator/models/position.creator.model.ts new file mode 100644 index 000000000..e605aeeb8 --- /dev/null +++ b/src/modules/position-creator/models/position.creator.model.ts @@ -0,0 +1,28 @@ +import { EnumType, EnumVariantDefinition } from '@multiversx/sdk-core/out'; +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; + +export enum StepsToPerform { + ADD_LIQUIDITY, + ENTER_FARM, + ENTER_METASTAKING, +} + +registerEnumType(StepsToPerform, { + name: 'StepsToPerform', +}); + +export const StepsToPerformEnumType = new EnumType('StepsToPerform', [ + new EnumVariantDefinition('AddLiquidity', 0), + new EnumVariantDefinition('EnterFarm', 1), + new EnumVariantDefinition('EnterMetastaking', 2), +]); + +@ObjectType() +export class PositionCreatorModel { + @Field() + address: string; + + constructor(init: Partial) { + Object.assign(this, init); + } +} diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts new file mode 100644 index 000000000..91a26523f --- /dev/null +++ b/src/modules/position-creator/position.creator.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { PositionCreatorResolver } from './position.creator.resolver'; +import { PairModule } from '../pair/pair.module'; +import { RouterModule } from '../router/router.module'; +import { PositionCreatorComputeService } from './services/position.creator.compute'; +import { PositionCreatorTransactionService } from './services/position.creator.transaction'; +import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; + +@Module({ + imports: [PairModule, RouterModule, MXCommunicationModule], + providers: [ + PositionCreatorComputeService, + PositionCreatorTransactionService, + PositionCreatorResolver, + ], + exports: [], +}) +export class PositionCreatorModule {} diff --git a/src/public.app.module.ts b/src/public.app.module.ts index 45af2ce73..b1a056181 100644 --- a/src/public.app.module.ts +++ b/src/public.app.module.ts @@ -36,6 +36,7 @@ import { EscrowModule } from './modules/escrow/escrow.module'; import { GovernanceModule } from './modules/governance/governance.module'; import { DynamicModuleUtils } from './utils/dynamic.module.utils'; import '@multiversx/sdk-nestjs-common/lib/utils/extensions/array.extensions'; +import { PositionCreatorModule } from './modules/position-creator/position.creator.module'; @Module({ imports: [ @@ -95,6 +96,7 @@ import '@multiversx/sdk-nestjs-common/lib/utils/extensions/array.extensions'; LockedTokenWrapperModule, EscrowModule, GovernanceModule, + PositionCreatorModule, DynamicModuleUtils.getCacheModule(), ], }) diff --git a/src/services/multiversx-communication/mx.proxy.service.ts b/src/services/multiversx-communication/mx.proxy.service.ts index 16487fb11..106627498 100644 --- a/src/services/multiversx-communication/mx.proxy.service.ts +++ b/src/services/multiversx-communication/mx.proxy.service.ts @@ -223,6 +223,14 @@ export class MXProxyService { ); } + async getPostitionCreatorContract(): Promise { + return this.getSmartContract( + scAddress.positionCreator, + abiConfig.positionCreator, + 'AutoPosCreator', + ); + } + async getSmartContract( contractAddress: string, contractAbiPath: string, From 004275c89d81fcfc1b193790ce8015891dc77db2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 20 Nov 2023 16:59:18 +0200 Subject: [PATCH 062/313] MEX-410: create lp position from single token Signed-off-by: Claudiu Lataretu --- src/abis/auto-pos-creator.abi.json | 252 ++++++++++++------ src/config/default.json | 6 +- src/modules/auto-router/auto-router.module.ts | 2 +- .../models/position.creator.model.ts | 19 +- .../position.creator.module.ts | 8 +- .../position.creator.resolver.ts | 38 +++ .../services/position.creator.compute.ts | 42 +++ .../services/position.creator.transaction.ts | 114 ++++++++ 8 files changed, 377 insertions(+), 104 deletions(-) create mode 100644 src/modules/position-creator/position.creator.resolver.ts create mode 100644 src/modules/position-creator/services/position.creator.compute.ts create mode 100644 src/modules/position-creator/services/position.creator.transaction.ts diff --git a/src/abis/auto-pos-creator.abi.json b/src/abis/auto-pos-creator.abi.json index 57682d9f5..916ad1919 100644 --- a/src/abis/auto-pos-creator.abi.json +++ b/src/abis/auto-pos-creator.abi.json @@ -13,7 +13,7 @@ }, "framework": { "name": "multiversx-sc", - "version": "0.43.3" + "version": "0.44.0" } }, "name": "AutoPosCreator", @@ -24,8 +24,8 @@ "type": "Address" }, { - "name": "wegld_token_id", - "type": "TokenIdentifier" + "name": "router_address", + "type": "Address" } ], "outputs": [] @@ -38,46 +38,73 @@ "outputs": [] }, { - "name": "addPairsToWhitelist", - "onlyOwner": true, + "name": "createLpPosFromSingleToken", "mutability": "mutable", + "payableInTokens": [ + "*" + ], "inputs": [ { - "name": "pair_addresses", - "type": "variadic
", + "name": "pair_address", + "type": "Address" + }, + { + "name": "add_liq_first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "swap_operations", + "type": "variadic>", "multi_arg": true } ], - "outputs": [] + "outputs": [ + { + "type": "List" + } + ] }, { - "name": "removePairsFromWhitelist", - "onlyOwner": true, + "name": "createLpPosFromTwoTokens", "mutability": "mutable", + "payableInTokens": [ + "*" + ], "inputs": [ { - "name": "pair_addresses", - "type": "variadic
", - "multi_arg": true + "name": "pair_address", + "type": "Address" + }, + { + "name": "add_liq_first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount_out", + "type": "BigUint" } ], - "outputs": [] + "outputs": [ + { + "type": "List" + } + ] }, { - "name": "createPosFromSingleToken", + "name": "createFarmPosFromSingleToken", "mutability": "mutable", "payableInTokens": [ "*" ], "inputs": [ { - "name": "dest_pair_address", + "name": "farm_address", "type": "Address" }, - { - "name": "steps", - "type": "StepsToPerform" - }, { "name": "add_liq_first_token_min_amount_out", "type": "BigUint" @@ -85,6 +112,11 @@ { "name": "add_liq_second_token_min_amount_out", "type": "BigUint" + }, + { + "name": "swap_operations", + "type": "variadic>", + "multi_arg": true } ], "outputs": [ @@ -94,20 +126,15 @@ ] }, { - "docs": [ - "Create pos from two payments, entering the pair for the two tokens", - "It will try doing this with the optimal amounts,", - "performing swaps before adding liqudity if necessary" - ], - "name": "createPosFromTwoTokens", + "name": "createFarmPosFromTwoTokens", "mutability": "mutable", "payableInTokens": [ "*" ], "inputs": [ { - "name": "steps", - "type": "StepsToPerform" + "name": "farm_address", + "type": "Address" }, { "name": "add_liq_first_token_min_amount_out", @@ -125,15 +152,28 @@ ] }, { - "name": "createPosFromLp", + "name": "createMetastakingPosFromSingleToken", "mutability": "mutable", "payableInTokens": [ "*" ], "inputs": [ { - "name": "steps", - "type": "StepsToPerform" + "name": "metastaking_address", + "type": "Address" + }, + { + "name": "add_liq_first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "swap_operations", + "type": "variadic>", + "multi_arg": true } ], "outputs": [ @@ -143,18 +183,22 @@ ] }, { - "name": "fullExitPos", + "name": "createMetastakingPosFromTwoTokens", "mutability": "mutable", "payableInTokens": [ "*" ], "inputs": [ { - "name": "first_token_min_amount_out", + "name": "metastaking_address", + "type": "Address" + }, + { + "name": "add_liq_first_token_min_amount_out", "type": "BigUint" }, { - "name": "second_token_min_amont_out", + "name": "add_liq_second_token_min_amount_out", "type": "BigUint" } ], @@ -163,89 +207,131 @@ "type": "List" } ] - } - ], - "events": [], - "hasCallback": false, - "types": { - "EsdtTokenPayment": { - "type": "struct", - "fields": [ + }, + { + "name": "createFarmStakingPosFromSingleToken", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "token_identifier", - "type": "TokenIdentifier" + "name": "farm_staking_address", + "type": "Address" }, { - "name": "token_nonce", - "type": "u64" + "name": "min_amount_out", + "type": "BigUint" }, { - "name": "amount", - "type": "BigUint" + "name": "swap_operations", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "List" } ] }, - "FarmConfig": { - "type": "struct", - "fields": [ + { + "name": "exitMetastakingPos", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "state", - "type": "State" + "name": "metastaking_address", + "type": "Address" }, { - "name": "farm_token_id", - "type": "TokenIdentifier" + "name": "first_token_min_amount_out", + "type": "BigUint" }, { - "name": "farming_token_id", - "type": "TokenIdentifier" + "name": "second_token_min_amont_out", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" } ] }, - "MetastakingConfig": { - "type": "struct", - "fields": [ + { + "name": "exitFarmPos", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "dual_yield_token_id", - "type": "TokenIdentifier" + "name": "farm_address", + "type": "Address" }, { - "name": "lp_farm_token_id", - "type": "TokenIdentifier" + "name": "first_token_min_amount_out", + "type": "BigUint" + }, + { + "name": "second_token_min_amont_out", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" } ] }, - "State": { - "type": "enum", - "variants": [ + { + "name": "exitLpPos", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ { - "name": "Inactive", - "discriminant": 0 + "name": "pair_address", + "type": "Address" }, { - "name": "Active", - "discriminant": 1 + "name": "first_token_min_amount_out", + "type": "BigUint" }, { - "name": "PartialActive", - "discriminant": 2 + "name": "second_token_min_amont_out", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" } ] - }, - "StepsToPerform": { - "type": "enum", - "variants": [ + } + ], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ { - "name": "AddLiquidity", - "discriminant": 0 + "name": "token_identifier", + "type": "TokenIdentifier" }, { - "name": "EnterFarm", - "discriminant": 1 + "name": "token_nonce", + "type": "u64" }, { - "name": "EnterMetastaking", - "discriminant": 2 + "name": "amount", + "type": "BigUint" } ] } diff --git a/src/config/default.json b/src/config/default.json index 91dbd8220..2bef8e077 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -492,6 +492,9 @@ "governance": { "vote": 50000000 }, + "positionCreator": { + "singleToken": 50000000 + }, "lockedAssetCreate": 5000000, "wrapeGLD": 4200000, "claimLockedAssets": 45500000 @@ -532,7 +535,8 @@ "governance": { "energy": "./src/abis/governance-v2.abi.json", "tokenSnapshot": "./src/abis/governance-v2-merkle-proof.abi.json" - } + }, + "positionCreator": "./src/abis/auto-pos-creator.abi.json" }, "cron": { "transactionCollectorMaxHyperblocks": 10, diff --git a/src/modules/auto-router/auto-router.module.ts b/src/modules/auto-router/auto-router.module.ts index 7075dc840..8918f96e1 100644 --- a/src/modules/auto-router/auto-router.module.ts +++ b/src/modules/auto-router/auto-router.module.ts @@ -33,6 +33,6 @@ import { TokenModule } from '../tokens/token.module'; AutoRouterTransactionService, PairTransactionService, ], - exports: [], + exports: [AutoRouterService], }) export class AutoRouterModule {} diff --git a/src/modules/position-creator/models/position.creator.model.ts b/src/modules/position-creator/models/position.creator.model.ts index e605aeeb8..bb01757e1 100644 --- a/src/modules/position-creator/models/position.creator.model.ts +++ b/src/modules/position-creator/models/position.creator.model.ts @@ -1,21 +1,4 @@ -import { EnumType, EnumVariantDefinition } from '@multiversx/sdk-core/out'; -import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; - -export enum StepsToPerform { - ADD_LIQUIDITY, - ENTER_FARM, - ENTER_METASTAKING, -} - -registerEnumType(StepsToPerform, { - name: 'StepsToPerform', -}); - -export const StepsToPerformEnumType = new EnumType('StepsToPerform', [ - new EnumVariantDefinition('AddLiquidity', 0), - new EnumVariantDefinition('EnterFarm', 1), - new EnumVariantDefinition('EnterMetastaking', 2), -]); +import { Field, ObjectType } from '@nestjs/graphql'; @ObjectType() export class PositionCreatorModel { diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index 91a26523f..658e92c56 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -5,9 +5,15 @@ import { RouterModule } from '../router/router.module'; import { PositionCreatorComputeService } from './services/position.creator.compute'; import { PositionCreatorTransactionService } from './services/position.creator.transaction'; import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; +import { AutoRouterModule } from '../auto-router/auto-router.module'; @Module({ - imports: [PairModule, RouterModule, MXCommunicationModule], + imports: [ + PairModule, + RouterModule, + AutoRouterModule, + MXCommunicationModule, + ], providers: [ PositionCreatorComputeService, PositionCreatorTransactionService, diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts new file mode 100644 index 000000000..6f8aa6f76 --- /dev/null +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -0,0 +1,38 @@ +import { Args, Query, Resolver } from '@nestjs/graphql'; +import { PositionCreatorModel } from './models/position.creator.model'; +import { scAddress } from 'src/config'; +import { TransactionModel } from 'src/models/transaction.model'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { PositionCreatorTransactionService } from './services/position.creator.transaction'; +import { InputTokenModel } from 'src/models/inputToken.model'; + +@Resolver(PositionCreatorModel) +export class PositionCreatorResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + ) {} + + @Query(() => PositionCreatorModel) + async getPositionCreator(): Promise { + return new PositionCreatorModel({ + address: scAddress.positionCreator, + }); + } + + @Query(() => TransactionModel) + async createPositionSingleToken( + @Args('pairAddress') pairAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createLiquidityPositionSingleToken( + pairAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + } +} diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts new file mode 100644 index 000000000..90e3a8c7d --- /dev/null +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -0,0 +1,42 @@ +import { Injectable } from '@nestjs/common'; +import BigNumber from 'bignumber.js'; +import { PairService } from 'src/modules/pair/services/pair.service'; +import { RouterService } from 'src/modules/router/services/router.service'; + +@Injectable() +export class PositionCreatorComputeService { + constructor( + private readonly pairService: PairService, + private readonly routerService: RouterService, + ) {} + + async computeSwap( + fromTokenID: string, + toTokenID: string, + amount: string, + ): Promise { + if (fromTokenID === toTokenID) { + return new BigNumber(amount); + } + + const pairs = await this.routerService.getAllPairs(0, 1, { + address: null, + issuedLpToken: true, + firstTokenID: fromTokenID, + secondTokenID: toTokenID, + state: 'Active', + }); + + if (pairs.length === 0) { + throw new Error('Pair not found'); + } + + const amountOut = await this.pairService.getAmountOut( + pairs[0].address, + fromTokenID, + amount, + ); + + return new BigNumber(amountOut); + } +} diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts new file mode 100644 index 000000000..3ca0aef08 --- /dev/null +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -0,0 +1,114 @@ +import { + Address, + AddressValue, + BigUIntValue, + BytesValue, + TokenIdentifierValue, + TokenTransfer, +} from '@multiversx/sdk-core/out'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { Injectable } from '@nestjs/common'; +import BigNumber from 'bignumber.js'; +import { gasConfig, mxConfig } from 'src/config'; +import { TransactionModel } from 'src/models/transaction.model'; +import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; +import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; +import { PositionCreatorComputeService } from './position.creator.compute'; +import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; +import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; + +@Injectable() +export class PositionCreatorTransactionService { + constructor( + private readonly autoRouterService: AutoRouterService, + private readonly routerAbi: RouterAbiService, + private readonly posCreatorCompute: PositionCreatorComputeService, + private readonly pairAbi: PairAbiService, + private readonly mxProxy: MXProxyService, + ) {} + + async createLiquidityPositionSingleToken( + pairAddress: string, + payment: EsdtTokenPayment, + tolerance: number, + ): Promise { + const acceptedPairedTokensIDs = + await this.routerAbi.commonTokensForUserPairs(); + + const [firstTokenID, secondTokenID] = await Promise.all([ + this.pairAbi.firstTokenID(pairAddress), + this.pairAbi.secondTokenID(pairAddress), + ]); + + const swapToTokenID = acceptedPairedTokensIDs.includes(firstTokenID) + ? firstTokenID + : secondTokenID; + + const swapRoute = await this.autoRouterService.swap({ + tokenInID: payment.tokenIdentifier, + amountIn: payment.amount, + tokenOutID: swapToTokenID, + tolerance, + }); + + const halfPayment = new BigNumber(swapRoute.amountOut) + .dividedBy(2) + .integerValue() + .toFixed(); + + const remainingPayment = new BigNumber(swapRoute.amountOut) + .minus(halfPayment) + .toFixed(); + + const [amount0, amount1] = await Promise.all([ + await this.posCreatorCompute.computeSwap( + swapRoute.tokenOutID, + firstTokenID, + halfPayment, + ), + await this.posCreatorCompute.computeSwap( + swapRoute.tokenOutID, + secondTokenID, + remainingPayment, + ), + ]); + + const amount0Min = new BigNumber(amount0) + .multipliedBy(1 - tolerance) + .integerValue(); + const amount1Min = new BigNumber(amount1) + .multipliedBy(1 - tolerance) + .integerValue(); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .createLpPosFromSingleToken([ + new AddressValue(Address.fromBech32(pairAddress)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + new AddressValue( + Address.fromBech32(swapRoute.pairs[0].address), + ), + new BytesValue(Buffer.from('swapTokensFixedInput')), + new TokenIdentifierValue(swapRoute.tokenOutID), + new BigUIntValue( + new BigNumber( + swapRoute.intermediaryAmounts[ + swapRoute.intermediaryAmounts.length - 1 + ], + ), + ), + ]) + .withSingleESDTTransfer( + TokenTransfer.fungibleFromBigInteger( + payment.tokenIdentifier, + new BigNumber(payment.amount), + ), + ) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } +} From 9e7277d864db3608293f4ac41edfc15233cfacf4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:23:56 +0200 Subject: [PATCH 063/313] MEX-410: refactor liquidity position creator transaction Signed-off-by: Claudiu Lataretu --- src/modules/auto-router/auto-router.module.ts | 2 +- .../auto-router.transactions.service.ts | 17 ++-- .../position.creator.module.ts | 8 ++ .../services/position.creator.compute.ts | 88 +++++++++++++++++++ .../services/position.creator.transaction.ts | 55 ++++++++++-- 5 files changed, 151 insertions(+), 19 deletions(-) diff --git a/src/modules/auto-router/auto-router.module.ts b/src/modules/auto-router/auto-router.module.ts index 8918f96e1..15b8ad6d9 100644 --- a/src/modules/auto-router/auto-router.module.ts +++ b/src/modules/auto-router/auto-router.module.ts @@ -33,6 +33,6 @@ import { TokenModule } from '../tokens/token.module'; AutoRouterTransactionService, PairTransactionService, ], - exports: [AutoRouterService], + exports: [AutoRouterService, AutoRouterTransactionService], }) export class AutoRouterModule {} diff --git a/src/modules/auto-router/services/auto-router.transactions.service.ts b/src/modules/auto-router/services/auto-router.transactions.service.ts index d43caab38..43b0f8791 100644 --- a/src/modules/auto-router/services/auto-router.transactions.service.ts +++ b/src/modules/auto-router/services/auto-router.transactions.service.ts @@ -4,6 +4,7 @@ import { BigUIntValue, BytesValue, TokenTransfer, + TypedValue, } from '@multiversx/sdk-core'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; @@ -55,8 +56,8 @@ export class AutoRouterTransactionService { const transactionArgs = args.swapType == SWAP_TYPE.fixedInput - ? await this.multiPairFixedInputSwaps(args) - : await this.multiPairFixedOutputSwaps(args); + ? this.multiPairFixedInputSwaps(args) + : this.multiPairFixedOutputSwaps(args); transactions.push( contract.methodsExplicit @@ -78,10 +79,8 @@ export class AutoRouterTransactionService { return transactions; } - private async multiPairFixedInputSwaps( - args: MultiSwapTokensArgs, - ): Promise { - const swaps = []; + multiPairFixedInputSwaps(args: MultiSwapTokensArgs): TypedValue[] { + const swaps: TypedValue[] = []; const intermediaryTolerance = args.tolerance / args.addressRoute.length; @@ -113,10 +112,8 @@ export class AutoRouterTransactionService { return swaps; } - private async multiPairFixedOutputSwaps( - args: MultiSwapTokensArgs, - ): Promise { - const swaps = []; + multiPairFixedOutputSwaps(args: MultiSwapTokensArgs): TypedValue[] { + const swaps: TypedValue[] = []; const intermediaryTolerance = args.tolerance / args.addressRoute.length; diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index 658e92c56..7f12240d6 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -6,12 +6,20 @@ import { PositionCreatorComputeService } from './services/position.creator.compu import { PositionCreatorTransactionService } from './services/position.creator.transaction'; import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; import { AutoRouterModule } from '../auto-router/auto-router.module'; +import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; +import { StakingProxyModule } from '../staking-proxy/staking.proxy.module'; +import { StakingModule } from '../staking/staking.module'; +import { TokenModule } from '../tokens/token.module'; @Module({ imports: [ PairModule, RouterModule, AutoRouterModule, + FarmModuleV2, + StakingModule, + StakingProxyModule, + TokenModule, MXCommunicationModule, ], providers: [ diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 90e3a8c7d..b02b37eaf 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -1,13 +1,30 @@ +import { TypedValue } from '@multiversx/sdk-core/out'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; +import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; +import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; +import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; +import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { PairService } from 'src/modules/pair/services/pair.service'; +import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; import { RouterService } from 'src/modules/router/services/router.service'; +export type PositionCreatorSingleTokenPairInput = { + swapRouteArgs: TypedValue[]; + amount0Min: BigNumber; + amount1Min: BigNumber; +}; + @Injectable() export class PositionCreatorComputeService { constructor( + private readonly pairAbi: PairAbiService, private readonly pairService: PairService, + private readonly routerAbi: RouterAbiService, private readonly routerService: RouterService, + private readonly autoRouterService: AutoRouterService, + private readonly autoRouterTransaction: AutoRouterTransactionService, ) {} async computeSwap( @@ -39,4 +56,75 @@ export class PositionCreatorComputeService { return new BigNumber(amountOut); } + + async computeSingleTokenPairInput( + pairAddress: string, + payment: EsdtTokenPayment, + tolerance: number, + ): Promise { + const acceptedPairedTokensIDs = + await this.routerAbi.commonTokensForUserPairs(); + + const [firstTokenID, secondTokenID] = await Promise.all([ + this.pairAbi.firstTokenID(pairAddress), + this.pairAbi.secondTokenID(pairAddress), + ]); + + const swapToTokenID = acceptedPairedTokensIDs.includes(firstTokenID) + ? firstTokenID + : secondTokenID; + + const swapRoute = await this.autoRouterService.swap({ + tokenInID: payment.tokenIdentifier, + amountIn: payment.amount, + tokenOutID: swapToTokenID, + tolerance, + }); + + const halfPayment = new BigNumber(swapRoute.amountOut) + .dividedBy(2) + .integerValue() + .toFixed(); + + const remainingPayment = new BigNumber(swapRoute.amountOut) + .minus(halfPayment) + .toFixed(); + + const [amount0, amount1] = await Promise.all([ + await this.computeSwap( + swapRoute.tokenOutID, + firstTokenID, + halfPayment, + ), + await this.computeSwap( + swapRoute.tokenOutID, + secondTokenID, + remainingPayment, + ), + ]); + + const amount0Min = new BigNumber(amount0) + .multipliedBy(1 - tolerance) + .integerValue(); + const amount1Min = new BigNumber(amount1) + .multipliedBy(1 - tolerance) + .integerValue(); + + const swapRouteArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoute.tokenInID, + tokenOutID: swapRoute.tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: swapRoute.pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoute.intermediaryAmounts, + tokenRoute: swapRoute.tokenRoute, + }); + + return { + swapRouteArgs, + amount0Min, + amount1Min, + }; + } } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 3ca0aef08..05976b182 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -15,15 +15,26 @@ import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; import { PositionCreatorComputeService } from './position.creator.compute'; import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; -import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; +import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; +import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; +import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; +import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; +import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; +import { PairService } from 'src/modules/pair/services/pair.service'; +import { TokenService } from 'src/modules/tokens/services/token.service'; @Injectable() export class PositionCreatorTransactionService { constructor( private readonly autoRouterService: AutoRouterService, - private readonly routerAbi: RouterAbiService, + private readonly autoRouterTransaction: AutoRouterTransactionService, private readonly posCreatorCompute: PositionCreatorComputeService, private readonly pairAbi: PairAbiService, + private readonly pairService: PairService, + private readonly farmAbiV2: FarmAbiServiceV2, + private readonly stakingAbi: StakingAbiService, + private readonly stakingProxyAbi: StakingProxyAbiService, + private readonly tokenService: TokenService, private readonly mxProxy: MXProxyService, ) {} @@ -32,13 +43,41 @@ export class PositionCreatorTransactionService { payment: EsdtTokenPayment, tolerance: number, ): Promise { - const acceptedPairedTokensIDs = - await this.routerAbi.commonTokensForUserPairs(); + const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( + false, + ); - const [firstTokenID, secondTokenID] = await Promise.all([ - this.pairAbi.firstTokenID(pairAddress), - this.pairAbi.secondTokenID(pairAddress), - ]); + if (!uniqueTokensIDs.includes(payment.tokenIdentifier)) { + throw new Error('Invalid token'); + } + + const singleTokenPairInput = + await this.posCreatorCompute.computeSingleTokenPairInput( + pairAddress, + payment, + tolerance, + ); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .createLpPosFromSingleToken([ + new AddressValue(Address.fromBech32(pairAddress)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]) + .withSingleESDTTransfer( + TokenTransfer.fungibleFromBigInteger( + payment.tokenIdentifier, + new BigNumber(payment.amount), + ), + ) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } const swapToTokenID = acceptedPairedTokensIDs.includes(firstTokenID) ? firstTokenID From 820f387edf866be44b19c4b3f2933f21b8aab4ae Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:25:20 +0200 Subject: [PATCH 064/313] MEX-410: add query for farm position single token transaction Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 22 ++++++++ .../services/position.creator.transaction.ts | 55 ++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index 6f8aa6f76..53471efac 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -35,4 +35,26 @@ export class PositionCreatorResolver { tolerance, ); } + + @Query(() => TransactionModel) + async createFarmPositionSingleToken( + @Args('farmAddress') farmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createFarmPositionSingleToken( + farmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 05976b182..773af057b 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -79,9 +79,58 @@ export class PositionCreatorTransactionService { .toPlainObject(); } - const swapToTokenID = acceptedPairedTokensIDs.includes(firstTokenID) - ? firstTokenID - : secondTokenID; + async createFarmPositionSingleToken( + farmAddress: string, + payments: EsdtTokenPayment[], + tolerance: number, + ): Promise { + const [pairAddress, farmTokenID, uniqueTokensIDs] = await Promise.all([ + this.farmAbiV2.pairContractAddress(farmAddress), + this.farmAbiV2.farmTokenID(farmAddress), + this.tokenService.getUniqueTokenIDs(false), + ]); + + if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { + throw new Error('Invalid ESDT token payment'); + } + + for (const payment of payments.slice(1)) { + if (payment.tokenIdentifier !== farmTokenID) { + throw new Error('Invalid farm token payment'); + } + } + + const singleTokenPairInput = + await this.posCreatorCompute.computeSingleTokenPairInput( + pairAddress, + payments[0], + tolerance, + ); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .createFarmPosFromSingleToken([ + new AddressValue(Address.fromBech32(farmAddress)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]) + .withMultiESDTNFTTransfer( + payments.map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), + ), + ) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + const swapRoute = await this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, From 4547569e0d14cb662809d172a49057d7b563d562 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:26:55 +0200 Subject: [PATCH 065/313] MEX-410: add query for dual farm single token position transaction Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 21 ++++++++ .../services/position.creator.transaction.ts | 53 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index 53471efac..a70a7525b 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -57,4 +57,25 @@ export class PositionCreatorResolver { ); } + @Query(() => TransactionModel) + async createDualFarmPositionSingleToken( + @Args('dualFarmAddress') dualFarmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createDualFarmPositionSingleToken( + dualFarmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 773af057b..17a344cd0 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -131,6 +131,59 @@ export class PositionCreatorTransactionService { .toPlainObject(); } + async createDualFarmPositionSingleToken( + stakingProxyAddress: string, + payments: EsdtTokenPayment[], + tolerance: number, + ): Promise { + const [pairAddress, dualYieldTokenID, uniqueTokensIDs] = + await Promise.all([ + this.stakingProxyAbi.pairAddress(stakingProxyAddress), + this.stakingProxyAbi.dualYieldTokenID(stakingProxyAddress), + this.tokenService.getUniqueTokenIDs(false), + ]); + + if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { + throw new Error('Invalid ESDT token payment'); + } + + for (const payment of payments.slice(1)) { + if (payment.tokenIdentifier !== dualYieldTokenID) { + throw new Error('Invalid dual yield token payment'); + } + } + + const singleTokenPairInput = + await this.posCreatorCompute.computeSingleTokenPairInput( + pairAddress, + payments[0], + tolerance, + ); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .createMetastakingPosFromSingleToken([ + new AddressValue(Address.fromBech32(stakingProxyAddress)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]) + .withMultiESDTNFTTransfer( + payments.map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), + ), + ) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + const swapRoute = await this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, From 73f4f2cb1e0d6a5c693d3234ac7f0fa35d4b184f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:28:15 +0200 Subject: [PATCH 066/313] MEX-410: add query for staking any single token transaction Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 17 +++++ .../services/position.creator.transaction.ts | 68 ++++++++----------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index a70a7525b..a5fb739f3 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -78,4 +78,21 @@ export class PositionCreatorResolver { ); } + @Query(() => TransactionModel) + async createStakingPositionSingleToken( + @Args('stakingAddress') stakingAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createStakingPositionSingleToken( + stakingAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + } + } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 17a344cd0..347c9ee7b 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -184,55 +184,43 @@ export class PositionCreatorTransactionService { .toPlainObject(); } + async createStakingPositionSingleToken( + stakingAddress: string, + payment: EsdtTokenPayment, + tolerance: number, + ): Promise { + const [farmingTokenID, uniqueTokensIDs] = await Promise.all([ + this.stakingAbi.farmingTokenID(stakingAddress), + this.tokenService.getUniqueTokenIDs(false), + ]); + + if (!uniqueTokensIDs.includes(payment.tokenIdentifier)) { + throw new Error('Invalid ESDT token payment'); + } const swapRoute = await this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, amountIn: payment.amount, - tokenOutID: swapToTokenID, + tokenOutID: farmingTokenID, tolerance, }); - const halfPayment = new BigNumber(swapRoute.amountOut) - .dividedBy(2) - .integerValue() - .toFixed(); - - const remainingPayment = new BigNumber(swapRoute.amountOut) - .minus(halfPayment) - .toFixed(); - - const [amount0, amount1] = await Promise.all([ - await this.posCreatorCompute.computeSwap( - swapRoute.tokenOutID, - firstTokenID, - halfPayment, - ), - await this.posCreatorCompute.computeSwap( - swapRoute.tokenOutID, - secondTokenID, - remainingPayment, - ), - ]); - - const amount0Min = new BigNumber(amount0) - .multipliedBy(1 - tolerance) - .integerValue(); - const amount1Min = new BigNumber(amount1) - .multipliedBy(1 - tolerance) - .integerValue(); - const contract = await this.mxProxy.getPostitionCreatorContract(); + const multiSwapArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoute.tokenInID, + tokenOutID: swapRoute.tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: swapRoute.pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoute.intermediaryAmounts, + tokenRoute: swapRoute.tokenRoute, + }); + return contract.methodsExplicit - .createLpPosFromSingleToken([ - new AddressValue(Address.fromBech32(pairAddress)), - new BigUIntValue(amount0Min), - new BigUIntValue(amount1Min), - new AddressValue( - Address.fromBech32(swapRoute.pairs[0].address), - ), - new BytesValue(Buffer.from('swapTokensFixedInput')), - new TokenIdentifierValue(swapRoute.tokenOutID), + .createFarmStakingPosFromSingleToken([ + new AddressValue(Address.fromBech32(stakingAddress)), new BigUIntValue( new BigNumber( swapRoute.intermediaryAmounts[ @@ -240,6 +228,7 @@ export class PositionCreatorTransactionService { ], ), ), + ...multiSwapArgs, ]) .withSingleESDTTransfer( TokenTransfer.fungibleFromBigInteger( @@ -252,4 +241,5 @@ export class PositionCreatorTransactionService { .buildTransaction() .toPlainObject(); } + } From cc21bc144b73c7b2f4cf0fb374094a38a5a594cf Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:29:18 +0200 Subject: [PATCH 067/313] MEX-410: add query for farm position dual tokens transaction Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 21 ++++++ .../services/position.creator.transaction.ts | 66 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index a5fb739f3..4dd2495c4 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -95,4 +95,25 @@ export class PositionCreatorResolver { ); } + @Query(() => TransactionModel) + async createFarmPositionDualTokens( + @Args('farmAddress') farmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createFarmPositionDualTokens( + farmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 347c9ee7b..3e18f5055 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -242,4 +242,70 @@ export class PositionCreatorTransactionService { .toPlainObject(); } + async createFarmPositionDualTokens( + farmAddress: string, + payments: EsdtTokenPayment[], + tolerance: number, + ): Promise { + const pairAddress = await this.farmAbiV2.pairContractAddress( + farmAddress, + ); + const [firstTokenID, secondTokenID] = await Promise.all([ + this.pairAbi.firstTokenID(pairAddress), + this.pairAbi.secondTokenID(pairAddress), + ]); + + if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { + throw new Error('Invalid tokens payments'); + } + + const [firstPayment, secondPayment] = + payments[0].tokenIdentifier === firstTokenID + ? [payments[0], payments[1]] + : [payments[1], payments[0]]; + + const amount0Min = new BigNumber(firstPayment.amount) + .multipliedBy(1 - tolerance) + .integerValue(); + const amount1Min = new BigNumber(secondPayment.amount) + .multipliedBy(1 - tolerance) + .integerValue(); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .createFarmPosFromTwoTokens([ + new AddressValue(Address.fromBech32(farmAddress)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + ]) + .withMultiESDTNFTTransfer([ + TokenTransfer.fungibleFromBigInteger( + firstPayment.tokenIdentifier, + new BigNumber(firstPayment.amount), + ), + TokenTransfer.fungibleFromBigInteger( + secondPayment.tokenIdentifier, + + new BigNumber(secondPayment.amount), + ), + ]) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + + private checkTokensPayments( + payments: EsdtTokenPayment[], + firstTokenID: string, + secondTokenID: string, + ): boolean { + return ( + (payments[0].tokenIdentifier === firstTokenID && + payments[1].tokenIdentifier === secondTokenID) || + (payments[1].tokenIdentifier === firstTokenID && + payments[0].tokenIdentifier === secondTokenID) + ); + } } From f652e10c71117f877dca8c241366f3e49207523b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:33:07 +0200 Subject: [PATCH 068/313] MEX-410: add query for dual farm position transaction with dual tokens Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 21 +++++++ .../services/position.creator.transaction.ts | 55 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index 4dd2495c4..0c05edbdf 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -116,4 +116,25 @@ export class PositionCreatorResolver { ); } + @Query(() => TransactionModel) + async createDualFarmPositionDualTokens( + @Args('dualFarmAddress') dualFarmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createDualFarmPositionDualTokens( + dualFarmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 3e18f5055..75a2d54ff 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -296,6 +296,61 @@ export class PositionCreatorTransactionService { .toPlainObject(); } + async createDualFarmPositionDualTokens( + stakingProxyAddress: string, + payments: EsdtTokenPayment[], + tolerance: number, + ): Promise { + const pairAddress = await this.stakingProxyAbi.pairAddress( + stakingProxyAddress, + ); + const [firstTokenID, secondTokenID] = await Promise.all([ + this.pairAbi.firstTokenID(pairAddress), + this.pairAbi.secondTokenID(pairAddress), + ]); + + if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { + throw new Error('Invalid tokens payments'); + } + + const [firstPayment, secondPayment] = + payments[0].tokenIdentifier === firstTokenID + ? [payments[0], payments[1]] + : [payments[1], payments[0]]; + + const amount0Min = new BigNumber(firstPayment.amount) + .multipliedBy(1 - tolerance) + .integerValue(); + const amount1Min = new BigNumber(secondPayment.amount) + .multipliedBy(1 - tolerance) + .integerValue(); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .createMetastakingPosFromTwoTokens([ + new AddressValue(Address.fromBech32(stakingProxyAddress)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + ]) + .withMultiESDTNFTTransfer([ + TokenTransfer.fungibleFromBigInteger( + firstPayment.tokenIdentifier, + new BigNumber(firstPayment.amount), + ), + TokenTransfer.fungibleFromBigInteger( + secondPayment.tokenIdentifier, + + new BigNumber(secondPayment.amount), + ), + ]) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + + private checkTokensPayments( payments: EsdtTokenPayment[], firstTokenID: string, From 5c4f862b5534f2bc9a466c100c1f4c071eead52c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 Nov 2023 21:34:07 +0200 Subject: [PATCH 069/313] MEX-410: add query for exit farm into dual tokens transaction Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 16 ++++++++ .../services/position.creator.transaction.ts | 41 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index 0c05edbdf..015353760 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -137,4 +137,20 @@ export class PositionCreatorResolver { ); } + @Query(() => TransactionModel) + async exitFarmPositionDualTokens( + @Args('farmAddress') farmAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.exitFarmPositionDualTokens( + farmAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + } } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 75a2d54ff..39ad7ba59 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -2,8 +2,6 @@ import { Address, AddressValue, BigUIntValue, - BytesValue, - TokenIdentifierValue, TokenTransfer, } from '@multiversx/sdk-core/out'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; @@ -350,6 +348,45 @@ export class PositionCreatorTransactionService { .toPlainObject(); } + async exitFarmPositionDualTokens( + farmAddress: string, + payment: EsdtTokenPayment, + tolerance: number, + ): Promise { + const pairAddress = await this.farmAbiV2.pairContractAddress( + farmAddress, + ); + const liquidityPosition = await this.pairService.getLiquidityPosition( + pairAddress, + payment.amount, + ); + const amount0Min = new BigNumber(liquidityPosition.firstTokenAmount) + .multipliedBy(1 - tolerance) + .integerValue(); + const amount1Min = new BigNumber(liquidityPosition.secondTokenAmount) + .multipliedBy(1 - tolerance) + .integerValue(); + + const contract = await this.mxProxy.getPostitionCreatorContract(); + + return contract.methodsExplicit + .exitFarmPos([ + new AddressValue(Address.fromBech32(farmAddress)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + ]) + .withSingleESDTNFTTransfer( + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), + ) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } private checkTokensPayments( payments: EsdtTokenPayment[], From 71f8741ddafd75df715953fef3792cdfd9a9a768 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 23 Nov 2023 19:42:18 +0200 Subject: [PATCH 070/313] MEX-410: add additional payments for position creator transactions Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 16 ++-- .../services/position.creator.transaction.ts | 92 ++++++++++++++----- 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index 015353760..ced82e9f0 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -81,16 +81,20 @@ export class PositionCreatorResolver { @Query(() => TransactionModel) async createStakingPositionSingleToken( @Args('stakingAddress') stakingAddress: string, - @Args('payment') payment: InputTokenModel, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], @Args('tolerance') tolerance: number, ): Promise { return this.posCreatorTransaction.createStakingPositionSingleToken( stakingAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), tolerance, ); } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 39ad7ba59..ba6d7bb5a 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -46,7 +46,7 @@ export class PositionCreatorTransactionService { ); if (!uniqueTokensIDs.includes(payment.tokenIdentifier)) { - throw new Error('Invalid token'); + throw new Error('Invalid ESDT token payment'); } const singleTokenPairInput = @@ -184,21 +184,29 @@ export class PositionCreatorTransactionService { async createStakingPositionSingleToken( stakingAddress: string, - payment: EsdtTokenPayment, + payments: EsdtTokenPayment[], tolerance: number, ): Promise { - const [farmingTokenID, uniqueTokensIDs] = await Promise.all([ - this.stakingAbi.farmingTokenID(stakingAddress), - this.tokenService.getUniqueTokenIDs(false), - ]); + const [farmingTokenID, farmTokenID, uniqueTokensIDs] = + await Promise.all([ + this.stakingAbi.farmingTokenID(stakingAddress), + this.stakingAbi.farmTokenID(stakingAddress), + this.tokenService.getUniqueTokenIDs(false), + ]); - if (!uniqueTokensIDs.includes(payment.tokenIdentifier)) { + if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { throw new Error('Invalid ESDT token payment'); } + for (const payment of payments.slice(1)) { + if (payment.tokenIdentifier !== farmTokenID) { + throw new Error('Invalid staking token payment'); + } + } + const swapRoute = await this.autoRouterService.swap({ - tokenInID: payment.tokenIdentifier, - amountIn: payment.amount, + tokenInID: payments[0].tokenIdentifier, + amountIn: payments[0].amount, tokenOutID: farmingTokenID, tolerance, }); @@ -228,10 +236,13 @@ export class PositionCreatorTransactionService { ), ...multiSwapArgs, ]) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - payment.tokenIdentifier, - new BigNumber(payment.amount), + .withMultiESDTNFTTransfer( + payments.map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), ), ) .withGasLimit(gasConfig.positionCreator.singleToken) @@ -248,15 +259,22 @@ export class PositionCreatorTransactionService { const pairAddress = await this.farmAbiV2.pairContractAddress( farmAddress, ); - const [firstTokenID, secondTokenID] = await Promise.all([ + const [firstTokenID, secondTokenID, farmTokenID] = await Promise.all([ this.pairAbi.firstTokenID(pairAddress), this.pairAbi.secondTokenID(pairAddress), + this.farmAbiV2.farmTokenID(farmAddress), ]); if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { throw new Error('Invalid tokens payments'); } + for (const payment of payments.slice(2)) { + if (payment.tokenIdentifier !== farmTokenID) { + throw new Error('Invalid farm token payment'); + } + } + const [firstPayment, secondPayment] = payments[0].tokenIdentifier === firstTokenID ? [payments[0], payments[1]] @@ -287,6 +305,15 @@ export class PositionCreatorTransactionService { new BigNumber(secondPayment.amount), ), + ...payments + .slice(2) + .map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), + ), ]) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) @@ -302,15 +329,23 @@ export class PositionCreatorTransactionService { const pairAddress = await this.stakingProxyAbi.pairAddress( stakingProxyAddress, ); - const [firstTokenID, secondTokenID] = await Promise.all([ - this.pairAbi.firstTokenID(pairAddress), - this.pairAbi.secondTokenID(pairAddress), - ]); + const [firstTokenID, secondTokenID, dualYieldTokenID] = + await Promise.all([ + this.pairAbi.firstTokenID(pairAddress), + this.pairAbi.secondTokenID(pairAddress), + this.stakingProxyAbi.dualYieldTokenID(stakingProxyAddress), + ]); if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { throw new Error('Invalid tokens payments'); } + for (const payment of payments.slice(2)) { + if (payment.tokenIdentifier !== dualYieldTokenID) { + throw new Error('Invalid dual farm token payment'); + } + } + const [firstPayment, secondPayment] = payments[0].tokenIdentifier === firstTokenID ? [payments[0], payments[1]] @@ -341,6 +376,15 @@ export class PositionCreatorTransactionService { new BigNumber(secondPayment.amount), ), + ...payments + .slice(2) + .map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), + ), ]) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) @@ -353,9 +397,15 @@ export class PositionCreatorTransactionService { payment: EsdtTokenPayment, tolerance: number, ): Promise { - const pairAddress = await this.farmAbiV2.pairContractAddress( - farmAddress, - ); + const [pairAddress, farmTokenID] = await Promise.all([ + this.farmAbiV2.pairContractAddress(farmAddress), + this.farmAbiV2.farmTokenID(farmAddress), + ]); + + if (payment.tokenIdentifier !== farmTokenID) { + throw new Error('Invalid farm token payment'); + } + const liquidityPosition = await this.pairService.getLiquidityPosition( pairAddress, payment.amount, From e7d47537509eda20df49a094b82290c7d8830727 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 23 Nov 2023 19:43:31 +0200 Subject: [PATCH 071/313] MEX-410: add unit tests for position creator transactions Signed-off-by: Claudiu Lataretu --- src/config/default.json | 3 +- .../position.creator.transaction.spec.ts | 925 ++++++++++++++++++ .../router/mocks/router.abi.service.mock.ts | 2 +- 3 files changed, 928 insertions(+), 2 deletions(-) create mode 100644 src/modules/position-creator/specs/position.creator.transaction.spec.ts diff --git a/src/config/default.json b/src/config/default.json index 2bef8e077..fef9e1dbf 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -42,7 +42,8 @@ "energyUpdate": "erd1qqqqqqqqqqqqqpgqqns0u3hw0e3j0km9h77emuear4xq7k7fd8ss0cwgja", "tokenUnstake": "erd1qqqqqqqqqqqqqpgqnysvq99c2t4a9pvvv22elnl6p73el8vw0n4spyfv7p", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq9ej9vcnr38l69rgkc735kgv0qlu2ptrsd8ssu9rwtu", - "escrow": "erd1qqqqqqqqqqqqqpgqz0wkk0j6y4h0mcxfxsg023j4x5sfgrmz0n4s4swp7a" + "escrow": "erd1qqqqqqqqqqqqqpgqz0wkk0j6y4h0mcxfxsg023j4x5sfgrmz0n4s4swp7a", + "positionCreator": "erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6" }, "tokenProviderUSD": "WEGLD-71e90a", "tokensSupply": ["MEX-27f4cd"], diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts new file mode 100644 index 000000000..ee33e0532 --- /dev/null +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -0,0 +1,925 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PositionCreatorTransactionService } from '../services/position.creator.transaction'; +import { PositionCreatorComputeService } from '../services/position.creator.compute'; +import { PairAbiServiceProvider } from 'src/modules/pair/mocks/pair.abi.service.mock'; +import { PairService } from 'src/modules/pair/services/pair.service'; +import { RouterAbiServiceProvider } from 'src/modules/router/mocks/router.abi.service.mock'; +import { RouterService } from 'src/modules/router/services/router.service'; +import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; +import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; +import { FarmAbiServiceProviderV2 } from 'src/modules/farm/mocks/farm.v2.abi.service.mock'; +import { StakingAbiServiceProvider } from 'src/modules/staking/mocks/staking.abi.service.mock'; +import { StakingProxyAbiServiceProvider } from 'src/modules/staking-proxy/mocks/staking.proxy.abi.service.mock'; +import { TokenServiceProvider } from 'src/modules/tokens/mocks/token.service.mock'; +import { MXProxyServiceProvider } from 'src/services/multiversx-communication/mx.proxy.service.mock'; +import { PairComputeServiceProvider } from 'src/modules/pair/mocks/pair.compute.service.mock'; +import { WrapAbiServiceProvider } from 'src/modules/wrapping/mocks/wrap.abi.service.mock'; +import { WinstonModule } from 'nest-winston'; +import winston from 'winston'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { ApiConfigService } from 'src/helpers/api.config.service'; +import { ContextGetterServiceProvider } from 'src/services/context/mocks/context.getter.service.mock'; +import { AutoRouterComputeService } from 'src/modules/auto-router/services/auto-router.compute.service'; +import { PairTransactionService } from 'src/modules/pair/services/pair.transactions.service'; +import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.transactions.service'; +import { WrapService } from 'src/modules/wrapping/services/wrap.service'; +import { RemoteConfigGetterServiceProvider } from 'src/modules/remote-config/mocks/remote-config.getter.mock'; +import { Address } from '@multiversx/sdk-core/out'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { encodeTransactionData } from 'src/helpers/helpers'; +import exp from 'constants'; +import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; + +describe('PositionCreatorTransaction', () => { + let module: TestingModule; + + beforeEach(async () => { + module = await Test.createTestingModule({ + imports: [ + WinstonModule.forRoot({ + transports: [new winston.transports.Console({})], + }), + ConfigModule.forRoot({}), + DynamicModuleUtils.getCacheModule(), + ], + providers: [ + PositionCreatorTransactionService, + PositionCreatorComputeService, + PairAbiServiceProvider, + PairService, + PairComputeServiceProvider, + PairTransactionService, + WrapService, + WrapAbiServiceProvider, + WrapTransactionsService, + RouterAbiServiceProvider, + RouterService, + AutoRouterService, + AutoRouterTransactionService, + AutoRouterComputeService, + FarmAbiServiceProviderV2, + StakingAbiServiceProvider, + StakingProxyAbiServiceProvider, + TokenServiceProvider, + RemoteConfigGetterServiceProvider, + MXProxyServiceProvider, + ConfigService, + ApiConfigService, + ContextGetterServiceProvider, + ], + }).compile(); + }); + + it('should be defined', () => { + expect(module).toBeDefined(); + }); + + describe('Create liquidity position single token', () => { + it('should return error on ESDT token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createLiquidityPositionSingleToken( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ), + ).rejects.toThrowError('Invalid ESDT token payment'); + }); + + it('should return transaction with single token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = + await service.createLiquidityPositionSingleToken( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + + describe('Create farm position single token', () => { + it('should return error on ESDT token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createFarmPositionSingleToken( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid ESDT token payment'); + }); + + it('should return error on farm token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createFarmPositionSingleToken( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid farm token payment'); + }); + + it('should return transaction no merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionSingleToken( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + + it('should return transaction with merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionSingleToken( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + + describe('Create dual farm position single token', () => { + it('should return error on ESDT token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid ESDT token payment'); + }); + + it('should return error on dual farm token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid dual yield token payment'); + }); + + it('should return transaction no merge dual farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + + it('should return transaction with merge dual farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-1234', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + + describe('Create staking position single token', () => { + it('should return error on ESDT token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createStakingPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid ESDT token payment'); + }); + + it('should return error on staking token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createStakingPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'STAKETOK-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid staking token payment'); + }); + + it('should return transaction no merge staking tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createStakingPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + + it('should return transaction with merge staking tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createStakingPositionSingleToken( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'STAKETOK-1111', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + + describe('Create farm position dual tokens', () => { + it('should return error on invalid payments', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createFarmPositionDualTokens( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid tokens payments'); + }); + + it('should return error on invalid farm token merge', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.createFarmPositionDualTokens( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid farm token payment'); + }); + + it('should return transaction no merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionDualTokens( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + + it('should return transaction no merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionDualTokens( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + + describe('Create dual farm position dual tokens', () => { + it('should return error on invalid payments', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + expect( + service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid tokens payments'); + }); + + it('should return error on invalid farm token merge', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + expect( + service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid dual farm token payment'); + }); + + it('should return transaction no merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + + it('should return transaction no merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-1234', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + + describe('Exit farm position dual tokens', () => { + it('should return error on invalid farm token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + expect( + service.exitFarmPositionDualTokens( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ), + ).rejects.toThrowError('Invalid farm token payment'); + }); + + it('should return transaction', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.exitFarmPositionDualTokens( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + 0.01, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: '', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'ESDTNFTTransfer@EGLDMEXFL-abcdef@01@100000000000000000000@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@exitFarmPos@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); +}); diff --git a/src/modules/router/mocks/router.abi.service.mock.ts b/src/modules/router/mocks/router.abi.service.mock.ts index 4bcacaf8e..d3b9afcb7 100644 --- a/src/modules/router/mocks/router.abi.service.mock.ts +++ b/src/modules/router/mocks/router.abi.service.mock.ts @@ -52,7 +52,7 @@ export class RouterAbiServiceMock implements IRouterAbiService { }); } async commonTokensForUserPairs(): Promise { - return ['USDC-123456']; + return ['USDC-123456', 'WEGLD-123456']; } } From 586eb9023c5c898b5f83f5ae7279216dd9e0b5e6 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 23 Nov 2023 22:02:41 +0200 Subject: [PATCH 072/313] MEX-410: add auth guard to position creator transactions resolver Signed-off-by: Claudiu Lataretu --- .../position.creator.resolver.ts | 149 +-------------- .../position.creator.transaction.resolver.ts | 170 ++++++++++++++++++ .../services/position.creator.transaction.ts | 14 ++ 3 files changed, 185 insertions(+), 148 deletions(-) create mode 100644 src/modules/position-creator/position.creator.transaction.resolver.ts diff --git a/src/modules/position-creator/position.creator.resolver.ts b/src/modules/position-creator/position.creator.resolver.ts index ced82e9f0..05810fa33 100644 --- a/src/modules/position-creator/position.creator.resolver.ts +++ b/src/modules/position-creator/position.creator.resolver.ts @@ -1,160 +1,13 @@ -import { Args, Query, Resolver } from '@nestjs/graphql'; +import { Query, Resolver } from '@nestjs/graphql'; import { PositionCreatorModel } from './models/position.creator.model'; import { scAddress } from 'src/config'; -import { TransactionModel } from 'src/models/transaction.model'; -import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; -import { PositionCreatorTransactionService } from './services/position.creator.transaction'; -import { InputTokenModel } from 'src/models/inputToken.model'; @Resolver(PositionCreatorModel) export class PositionCreatorResolver { - constructor( - private readonly posCreatorTransaction: PositionCreatorTransactionService, - ) {} - @Query(() => PositionCreatorModel) async getPositionCreator(): Promise { return new PositionCreatorModel({ address: scAddress.positionCreator, }); } - - @Query(() => TransactionModel) - async createPositionSingleToken( - @Args('pairAddress') pairAddress: string, - @Args('payment') payment: InputTokenModel, - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createLiquidityPositionSingleToken( - pairAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - tolerance, - ); - } - - @Query(() => TransactionModel) - async createFarmPositionSingleToken( - @Args('farmAddress') farmAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createFarmPositionSingleToken( - farmAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, - ); - } - - @Query(() => TransactionModel) - async createDualFarmPositionSingleToken( - @Args('dualFarmAddress') dualFarmAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createDualFarmPositionSingleToken( - dualFarmAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, - ); - } - - @Query(() => TransactionModel) - async createStakingPositionSingleToken( - @Args('stakingAddress') stakingAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createStakingPositionSingleToken( - stakingAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, - ); - } - - @Query(() => TransactionModel) - async createFarmPositionDualTokens( - @Args('farmAddress') farmAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createFarmPositionDualTokens( - farmAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, - ); - } - - @Query(() => TransactionModel) - async createDualFarmPositionDualTokens( - @Args('dualFarmAddress') dualFarmAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createDualFarmPositionDualTokens( - dualFarmAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, - ); - } - - @Query(() => TransactionModel) - async exitFarmPositionDualTokens( - @Args('farmAddress') farmAddress: string, - @Args('payment') payment: InputTokenModel, - @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.exitFarmPositionDualTokens( - farmAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - tolerance, - ); - } } diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts new file mode 100644 index 000000000..6885f73c5 --- /dev/null +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -0,0 +1,170 @@ +import { UseGuards } from '@nestjs/common'; +import { Args, Query, Resolver } from '@nestjs/graphql'; +import { JwtOrNativeAuthGuard } from '../auth/jwt.or.native.auth.guard'; +import { TransactionModel } from 'src/models/transaction.model'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { PositionCreatorTransactionService } from './services/position.creator.transaction'; +import { InputTokenModel } from 'src/models/inputToken.model'; +import { UserAuthResult } from '../auth/user.auth.result'; +import { AuthUser } from '../auth/auth.user'; + +@Resolver() +@UseGuards(JwtOrNativeAuthGuard) +export class PositionCreatorTransactionResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + ) {} + + @Query(() => TransactionModel) + async createPositionSingleToken( + @AuthUser() user: UserAuthResult, + @Args('pairAddress') pairAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createLiquidityPositionSingleToken( + user.address, + pairAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + } + + @Query(() => TransactionModel) + async createFarmPositionSingleToken( + @AuthUser() user: UserAuthResult, + @Args('farmAddress') farmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createFarmPositionSingleToken( + user.address, + farmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + + @Query(() => TransactionModel) + async createDualFarmPositionSingleToken( + @AuthUser() user: UserAuthResult, + @Args('dualFarmAddress') dualFarmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createDualFarmPositionSingleToken( + user.address, + dualFarmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + + @Query(() => TransactionModel) + async createStakingPositionSingleToken( + @AuthUser() user: UserAuthResult, + @Args('stakingAddress') stakingAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createStakingPositionSingleToken( + user.address, + stakingAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + + @Query(() => TransactionModel) + async createFarmPositionDualTokens( + @AuthUser() user: UserAuthResult, + @Args('farmAddress') farmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createFarmPositionDualTokens( + user.address, + farmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + + @Query(() => TransactionModel) + async createDualFarmPositionDualTokens( + @AuthUser() user: UserAuthResult, + @Args('dualFarmAddress') dualFarmAddress: string, + @Args('payments', { type: () => [InputTokenModel] }) + payments: InputTokenModel[], + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createDualFarmPositionDualTokens( + user.address, + dualFarmAddress, + payments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + tolerance, + ); + } + + @Query(() => TransactionModel) + async exitFarmPositionDualTokens( + @AuthUser() user: UserAuthResult, + @Args('farmAddress') farmAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.exitFarmPositionDualTokens( + user.address, + farmAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + } +} diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index ba6d7bb5a..b8b476641 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -37,6 +37,7 @@ export class PositionCreatorTransactionService { ) {} async createLiquidityPositionSingleToken( + sender: string, pairAddress: string, payment: EsdtTokenPayment, tolerance: number, @@ -71,6 +72,7 @@ export class PositionCreatorTransactionService { new BigNumber(payment.amount), ), ) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -78,6 +80,7 @@ export class PositionCreatorTransactionService { } async createFarmPositionSingleToken( + sender: string, farmAddress: string, payments: EsdtTokenPayment[], tolerance: number, @@ -123,6 +126,7 @@ export class PositionCreatorTransactionService { ), ), ) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -130,6 +134,7 @@ export class PositionCreatorTransactionService { } async createDualFarmPositionSingleToken( + sender: string, stakingProxyAddress: string, payments: EsdtTokenPayment[], tolerance: number, @@ -176,6 +181,7 @@ export class PositionCreatorTransactionService { ), ), ) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -183,6 +189,7 @@ export class PositionCreatorTransactionService { } async createStakingPositionSingleToken( + sender: string, stakingAddress: string, payments: EsdtTokenPayment[], tolerance: number, @@ -245,6 +252,7 @@ export class PositionCreatorTransactionService { ), ), ) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -252,6 +260,7 @@ export class PositionCreatorTransactionService { } async createFarmPositionDualTokens( + sender: string, farmAddress: string, payments: EsdtTokenPayment[], tolerance: number, @@ -315,6 +324,7 @@ export class PositionCreatorTransactionService { ), ), ]) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -322,6 +332,7 @@ export class PositionCreatorTransactionService { } async createDualFarmPositionDualTokens( + sender: string, stakingProxyAddress: string, payments: EsdtTokenPayment[], tolerance: number, @@ -386,6 +397,7 @@ export class PositionCreatorTransactionService { ), ), ]) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() @@ -393,6 +405,7 @@ export class PositionCreatorTransactionService { } async exitFarmPositionDualTokens( + sender: string, farmAddress: string, payment: EsdtTokenPayment, tolerance: number, @@ -432,6 +445,7 @@ export class PositionCreatorTransactionService { new BigNumber(payment.amount), ), ) + .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) .buildTransaction() From e00629a734e1afcc63b73f0e184c3ed82dc71c21 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 23 Nov 2023 22:16:49 +0200 Subject: [PATCH 073/313] MEX-410: add sender for position creator transactions unit tests Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 70 +++++++++++++------ .../specs/router.transactions.service.spec.ts | 4 +- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index ee33e0532..0acd9f074 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -82,6 +82,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createLiquidityPositionSingleToken( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000012', ).bech32(), @@ -101,6 +102,7 @@ describe('PositionCreatorTransaction', () => { ); const transaction = await service.createLiquidityPositionSingleToken( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000012', ).bech32(), @@ -117,7 +119,7 @@ describe('PositionCreatorTransaction', () => { value: '0', receiver: 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', - sender: '', + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -142,6 +144,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createFarmPositionSingleToken( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -163,6 +166,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createFarmPositionSingleToken( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -188,6 +192,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.createFarmPositionSingleToken( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -204,8 +209,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -227,6 +232,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.createFarmPositionSingleToken( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -248,8 +254,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -274,6 +280,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -293,6 +300,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -325,6 +333,7 @@ describe('PositionCreatorTransaction', () => { ); const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -339,8 +348,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -371,6 +380,7 @@ describe('PositionCreatorTransaction', () => { ); const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -390,8 +400,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -416,6 +426,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createStakingPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -435,6 +446,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createStakingPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -458,6 +470,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.createStakingPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -472,8 +485,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -495,6 +508,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.createStakingPositionSingleToken( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -514,8 +528,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -540,6 +554,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createFarmPositionDualTokens( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -566,6 +581,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.createFarmPositionDualTokens( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -596,6 +612,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.createFarmPositionDualTokens( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -617,8 +634,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -640,6 +657,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.createFarmPositionDualTokens( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -666,8 +684,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -701,6 +719,7 @@ describe('PositionCreatorTransaction', () => { expect( service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -734,6 +753,7 @@ describe('PositionCreatorTransaction', () => { expect( service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -771,6 +791,7 @@ describe('PositionCreatorTransaction', () => { ); const transaction = await service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -790,8 +811,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -822,6 +843,7 @@ describe('PositionCreatorTransaction', () => { ); const transaction = await service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), Address.Zero().bech32(), [ new EsdtTokenPayment({ @@ -846,8 +868,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, @@ -872,6 +894,7 @@ describe('PositionCreatorTransaction', () => { ); expect( service.exitFarmPositionDualTokens( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -890,6 +913,7 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const transaction = await service.exitFarmPositionDualTokens( + Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), @@ -904,8 +928,8 @@ describe('PositionCreatorTransaction', () => { expect(transaction).toEqual({ nonce: 0, value: '0', - receiver: '', - sender: '', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, diff --git a/src/modules/router/specs/router.transactions.service.spec.ts b/src/modules/router/specs/router.transactions.service.spec.ts index 23a10f8bc..2bd4a242a 100644 --- a/src/modules/router/specs/router.transactions.service.spec.ts +++ b/src/modules/router/specs/router.transactions.service.spec.ts @@ -655,7 +655,7 @@ describe('RouterService', () => { nonce: 1, amount: '1000000000000000000', attributes: - 'AAAAEEVHTERNRVhMUC1hYmNkZWYAAAAAAAAAAAAAAAAAAAAB', + 'AAAAEVRPSzVUT0s2TFAtYWJjZGVmAAAAAAAAAAAAAAAAAAAAAQ==', }), ), ).rejects.toThrow('Not a valid user defined pair'); @@ -678,7 +678,7 @@ describe('RouterService', () => { nonce: 1, amount: '1000', attributes: - 'AAAAEUVHTERVU0RDTFAtYWJjZGVmAAAAAAAAAAAAAAAAAAAAAg==', + 'AAAAEVRPSzVVU0RDTFAtYWJjZGVmAAAAAAAAAAAAAAAAAAAAAg==', }), ), ).rejects.toThrow('Not enough value locked'); From eb5d78d58f4f35cec630b13b45584f66f29e8336 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 24 Nov 2023 18:27:35 +0200 Subject: [PATCH 074/313] MEX-410: add missing position creator module provider Signed-off-by: Claudiu Lataretu --- src/modules/position-creator/position.creator.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index 7f12240d6..bfd8391b2 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -10,6 +10,7 @@ import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; import { StakingProxyModule } from '../staking-proxy/staking.proxy.module'; import { StakingModule } from '../staking/staking.module'; import { TokenModule } from '../tokens/token.module'; +import { PositionCreatorTransactionResolver } from './position.creator.transaction.resolver'; @Module({ imports: [ @@ -26,6 +27,7 @@ import { TokenModule } from '../tokens/token.module'; PositionCreatorComputeService, PositionCreatorTransactionService, PositionCreatorResolver, + PositionCreatorTransactionResolver, ], exports: [], }) From 497a86097e43af03acb4ce0e13c944784dc5357c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 27 Nov 2023 18:18:52 +0200 Subject: [PATCH 075/313] MEX-410: imporve position creator compute swap method Signed-off-by: Claudiu Lataretu --- .../services/position.creator.compute.ts | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index b02b37eaf..95168b5e1 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -1,5 +1,6 @@ import { TypedValue } from '@multiversx/sdk-core/out'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { PerformanceProfiler } from '@multiversx/sdk-nestjs-monitoring'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; @@ -8,7 +9,6 @@ import { AutoRouterTransactionService } from 'src/modules/auto-router/services/a import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { PairService } from 'src/modules/pair/services/pair.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; -import { RouterService } from 'src/modules/router/services/router.service'; export type PositionCreatorSingleTokenPairInput = { swapRouteArgs: TypedValue[]; @@ -22,12 +22,12 @@ export class PositionCreatorComputeService { private readonly pairAbi: PairAbiService, private readonly pairService: PairService, private readonly routerAbi: RouterAbiService, - private readonly routerService: RouterService, private readonly autoRouterService: AutoRouterService, private readonly autoRouterTransaction: AutoRouterTransactionService, ) {} async computeSwap( + pairAddress: string, fromTokenID: string, toTokenID: string, amount: string, @@ -36,20 +36,8 @@ export class PositionCreatorComputeService { return new BigNumber(amount); } - const pairs = await this.routerService.getAllPairs(0, 1, { - address: null, - issuedLpToken: true, - firstTokenID: fromTokenID, - secondTokenID: toTokenID, - state: 'Active', - }); - - if (pairs.length === 0) { - throw new Error('Pair not found'); - } - const amountOut = await this.pairService.getAmountOut( - pairs[0].address, + pairAddress, fromTokenID, amount, ); @@ -74,6 +62,8 @@ export class PositionCreatorComputeService { ? firstTokenID : secondTokenID; + const profiler = new PerformanceProfiler(); + const swapRoute = await this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, amountIn: payment.amount, @@ -81,6 +71,8 @@ export class PositionCreatorComputeService { tolerance, }); + profiler.stop('swap route', true); + const halfPayment = new BigNumber(swapRoute.amountOut) .dividedBy(2) .integerValue() @@ -92,11 +84,13 @@ export class PositionCreatorComputeService { const [amount0, amount1] = await Promise.all([ await this.computeSwap( + pairAddress, swapRoute.tokenOutID, firstTokenID, halfPayment, ), await this.computeSwap( + pairAddress, swapRoute.tokenOutID, secondTokenID, remainingPayment, From c578fd8db2054de73d6a38eedec4bedf97f4588b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Dec 2023 12:35:36 +0200 Subject: [PATCH 076/313] MEX-414: add composable tasks module - new module for composable tasks sc integration - implement transaction generator for composable tasks - implement custom composable task for wrap EGLD and swap tokens Signed-off-by: Claudiu Lataretu --- src/abis/composable-tasks.abi.json | 136 +++++++++++++++++ src/config/default.json | 3 +- src/config/testnet.json | 3 +- src/models/esdtTokenPayment.model.ts | 22 +++ .../composable.tasks.module.ts | 11 ++ .../models/composable.tasks.model.ts | 28 ++++ .../services/composable.tasks.transaction.ts | 138 ++++++++++++++++++ src/public.app.module.ts | 2 + .../mx.proxy.service.ts | 8 + 9 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 src/abis/composable-tasks.abi.json create mode 100644 src/modules/composable-tasks/composable.tasks.module.ts create mode 100644 src/modules/composable-tasks/models/composable.tasks.model.ts create mode 100644 src/modules/composable-tasks/services/composable.tasks.transaction.ts diff --git a/src/abis/composable-tasks.abi.json b/src/abis/composable-tasks.abi.json new file mode 100644 index 000000000..c62ebddd4 --- /dev/null +++ b/src/abis/composable-tasks.abi.json @@ -0,0 +1,136 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.71.0-nightly", + "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de", + "commitDate": "2023-05-25", + "channel": "Nightly", + "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)" + }, + "contractCrate": { + "name": "composable-tasks", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.44.0" + } + }, + "name": "ComposableTasksContract", + "constructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "composeTasks", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "expected_token_out", + "type": "EgldOrEsdtTokenPayment" + }, + { + "name": "tasks", + "type": "variadic>>", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setWrapEgldAddr", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_addr", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "setrouterAddr", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_addr", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "setPairAddrForTokens", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "first_token_id", + "type": "TokenIdentifier" + }, + { + "name": "second_token_id", + "type": "TokenIdentifier" + }, + { + "name": "new_addr", + "type": "Address" + } + ], + "outputs": [] + } + ], + "events": [], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EgldOrEsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "EgldOrEsdtTokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "TaskType": { + "type": "enum", + "variants": [ + { + "name": "WrapEGLD", + "discriminant": 0 + }, + { + "name": "UnwrapEGLD", + "discriminant": 1 + }, + { + "name": "Swap", + "discriminant": 2 + }, + { + "name": "RouterSwap", + "discriminant": 3 + }, + { + "name": "SendEgldOrEsdt", + "discriminant": 4 + } + ] + } + } +} diff --git a/src/config/default.json b/src/config/default.json index dce40981b..940caaebc 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -42,7 +42,8 @@ "energyUpdate": "erd1qqqqqqqqqqqqqpgqqns0u3hw0e3j0km9h77emuear4xq7k7fd8ss0cwgja", "tokenUnstake": "erd1qqqqqqqqqqqqqpgqnysvq99c2t4a9pvvv22elnl6p73el8vw0n4spyfv7p", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq9ej9vcnr38l69rgkc735kgv0qlu2ptrsd8ssu9rwtu", - "escrow": "erd1qqqqqqqqqqqqqpgqz0wkk0j6y4h0mcxfxsg023j4x5sfgrmz0n4s4swp7a" + "escrow": "erd1qqqqqqqqqqqqqpgqz0wkk0j6y4h0mcxfxsg023j4x5sfgrmz0n4s4swp7a", + "composableTasks": "erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh" }, "tokenProviderUSD": "WEGLD-71e90a", "tokensSupply": ["MEX-27f4cd"], diff --git a/src/config/testnet.json b/src/config/testnet.json index 6ef54bf38..9040e5db9 100644 --- a/src/config/testnet.json +++ b/src/config/testnet.json @@ -27,7 +27,8 @@ "energyUpdate": "erd1qqqqqqqqqqqqqpgqqns0u3hw0e3j0km9h77emuear4xq7k7fd8ss0cwgja", "tokenUnstake": "erd1qqqqqqqqqqqqqpgq9smwjt59tdjdx8etcfr4m5pm4m2cqty0295qf9ytj8", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqp628sk5y6c5w8twdddmf4n6kcpatnfuk295q0xuzle", - "escrow": "erd1qqqqqqqqqqqqqpgqpd6ymzum53u0645a4v2hrmpmf2aqh2jw295q8dsv45" + "escrow": "erd1qqqqqqqqqqqqqpgqpd6ymzum53u0645a4v2hrmpmf2aqh2jw295q8dsv45", + "composableTasks": "erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh" }, "governance": { "oldEnergy": {}, diff --git a/src/models/esdtTokenPayment.model.ts b/src/models/esdtTokenPayment.model.ts index 5818b74ab..f92108570 100644 --- a/src/models/esdtTokenPayment.model.ts +++ b/src/models/esdtTokenPayment.model.ts @@ -64,3 +64,25 @@ export class EsdtTokenType { ]); } } + +export class EgldOrEsdtTokenPayment { + tokenIdentifier: string; + nonce: number; + amount: string; + + constructor(init?: Partial) { + Object.assign(this, init); + } + + static getStructure(): StructType { + return new StructType('EgldOrEsdtTokenPayment', [ + new FieldDefinition( + 'token_identifier', + '', + new TokenIdentifierType(), + ), + new FieldDefinition('token_nonce', '', new U64Type()), + new FieldDefinition('amount', '', new BigUIntType()), + ]); + } +} diff --git a/src/modules/composable-tasks/composable.tasks.module.ts b/src/modules/composable-tasks/composable.tasks.module.ts new file mode 100644 index 000000000..7c170280f --- /dev/null +++ b/src/modules/composable-tasks/composable.tasks.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; +import { ComposableTasksTransactionService } from './services/composable.tasks.transaction'; +import { ComposableTasksResolver } from './composable.tasks.resolver'; + +@Module({ + imports: [MXCommunicationModule], + providers: [ComposableTasksTransactionService, ComposableTasksResolver], + exports: [], +}) +export class ComposableTasksModule {} diff --git a/src/modules/composable-tasks/models/composable.tasks.model.ts b/src/modules/composable-tasks/models/composable.tasks.model.ts new file mode 100644 index 000000000..d05bed814 --- /dev/null +++ b/src/modules/composable-tasks/models/composable.tasks.model.ts @@ -0,0 +1,28 @@ +import { EnumType, EnumVariantDefinition } from '@multiversx/sdk-core/out'; +import { Field, ObjectType } from '@nestjs/graphql'; + +export enum ComposableTaskType { + WRAP_EGLD = 'WrapEGLD', + UNWRAP_EGLD = 'UnwrapEGLD', + SWAP = 'Swap', + ROUTER_SWAP = 'RouterSwap', + SEND_EGLD_OR_ESDT = 'SendEgldOrEsdt', +} + +export class ComposableTaskEnumType { + static getEnumType(): EnumType { + return new EnumType('ComposableTaskType', [ + new EnumVariantDefinition('WrapEGLD', 0), + new EnumVariantDefinition('UnwrapEGLD', 1), + new EnumVariantDefinition('Swap', 2), + new EnumVariantDefinition('RouterSwap', 3), + new EnumVariantDefinition('SendEgldOrEsdt', 4), + ]); + } +} + +@ObjectType() +export class ComposableTaskModel { + @Field() + address: string; +} diff --git a/src/modules/composable-tasks/services/composable.tasks.transaction.ts b/src/modules/composable-tasks/services/composable.tasks.transaction.ts new file mode 100644 index 000000000..7e6f76553 --- /dev/null +++ b/src/modules/composable-tasks/services/composable.tasks.transaction.ts @@ -0,0 +1,138 @@ +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { Injectable } from '@nestjs/common'; +import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; +import { + ComposableTaskEnumType, + ComposableTaskType, +} from '../models/composable.tasks.model'; +import { TransactionModel } from 'src/models/transaction.model'; +import { + BigUIntValue, + BytesType, + BytesValue, + EnumValue, + EnumVariantDefinition, + Field, + List, + ListType, + Struct, + TokenIdentifierValue, + TokenTransfer, + TypedValue, + U64Value, +} from '@multiversx/sdk-core/out'; +import BigNumber from 'bignumber.js'; +import { gasConfig, mxConfig } from 'src/config'; +import { EgldOrEsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; +import { decimalToHex } from 'src/utils/token.converters'; + +export type ComposableTask = { + type: ComposableTaskType; + arguments: BytesValue[]; +}; + +@Injectable() +export class ComposableTasksTransactionService { + constructor(private readonly mxPorxy: MXProxyService) {} + + async getComposeTasksTransaction( + payment: EsdtTokenPayment, + tokenOut: EgldOrEsdtTokenPayment, + tasks: ComposableTask[], + ): Promise { + const contract = await this.mxPorxy.getComposableTasksSmartContract(); + + const rawTasks: TypedValue[] = []; + + for (const task of tasks) { + rawTasks.push( + new EnumValue( + ComposableTaskEnumType.getEnumType(), + new EnumVariantDefinition( + task.type, + ComposableTaskEnumType.getEnumType().getVariantByName( + task.type, + ).discriminant, + ), + [], + ), + ); + rawTasks.push( + new List(new ListType(new BytesType()), task.arguments), + ); + } + + let interaction = contract.methodsExplicit + .composeTasks([ + new Struct(EgldOrEsdtTokenPayment.getStructure(), [ + new Field( + new TokenIdentifierValue(tokenOut.tokenIdentifier), + 'token_identifier', + ), + new Field(new U64Value(new BigNumber(0)), 'token_nonce'), + new Field( + new BigUIntValue(new BigNumber(tokenOut.amount)), + 'amount', + ), + ]), + ...rawTasks, + ]) + .withGasLimit(gasConfig.composableTasks.default) + .withChainID(mxConfig.chainID); + + switch (payment.tokenIdentifier) { + case 'EGLD': + interaction = interaction.withValue( + new BigUIntValue(new BigNumber(payment.amount)), + ); + break; + default: + interaction = interaction.withSingleESDTTransfer( + TokenTransfer.fungibleFromBigInteger( + payment.tokenIdentifier, + new BigNumber(payment.amount), + ), + ); + break; + } + + return interaction.buildTransaction().toPlainObject(); + } + + async wrapEgldAndSwapTransaction( + value: string, + tokenOutID: string, + tokenOutAmountMin: string, + ): Promise { + const wrapTask: ComposableTask = { + type: ComposableTaskType.WRAP_EGLD, + arguments: [], + }; + console.log(decimalToHex(new BigNumber(tokenOutAmountMin))); + const swapTask: ComposableTask = { + type: ComposableTaskType.SWAP, + arguments: [ + new BytesValue(Buffer.from(tokenOutID, 'utf-8')), + new BytesValue( + Buffer.from( + decimalToHex(new BigNumber(tokenOutAmountMin)), + 'hex', + ), + ), + ], + }; + + return this.getComposeTasksTransaction( + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: value, + }), + new EgldOrEsdtTokenPayment({ + tokenIdentifier: tokenOutID, + amount: tokenOutAmountMin, + }), + [wrapTask, swapTask], + ); + } +} diff --git a/src/public.app.module.ts b/src/public.app.module.ts index 45af2ce73..f94270f5e 100644 --- a/src/public.app.module.ts +++ b/src/public.app.module.ts @@ -36,6 +36,7 @@ import { EscrowModule } from './modules/escrow/escrow.module'; import { GovernanceModule } from './modules/governance/governance.module'; import { DynamicModuleUtils } from './utils/dynamic.module.utils'; import '@multiversx/sdk-nestjs-common/lib/utils/extensions/array.extensions'; +import { ComposableTasksModule } from './modules/composable-tasks/composable.tasks.module'; @Module({ imports: [ @@ -95,6 +96,7 @@ import '@multiversx/sdk-nestjs-common/lib/utils/extensions/array.extensions'; LockedTokenWrapperModule, EscrowModule, GovernanceModule, + ComposableTasksModule, DynamicModuleUtils.getCacheModule(), ], }) diff --git a/src/services/multiversx-communication/mx.proxy.service.ts b/src/services/multiversx-communication/mx.proxy.service.ts index 16487fb11..19441e4b8 100644 --- a/src/services/multiversx-communication/mx.proxy.service.ts +++ b/src/services/multiversx-communication/mx.proxy.service.ts @@ -223,6 +223,14 @@ export class MXProxyService { ); } + async getComposableTasksSmartContract(): Promise { + return this.getSmartContract( + scAddress.composableTasks, + abiConfig.composableTasks, + 'ComposableTasksContract', + ); + } + async getSmartContract( contractAddress: string, contractAbiPath: string, From 515af814310b12ed20786dd079b51ecab7029eb1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Dec 2023 12:45:09 +0200 Subject: [PATCH 077/313] MEX-414: add composable-tasks default configs Signed-off-by: Claudiu Lataretu --- src/config/default.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 940caaebc..944031940 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -494,6 +494,9 @@ "governance": { "vote": 50000000 }, + "composableTasks": { + "default": 30000000 + }, "lockedAssetCreate": 5000000, "wrapeGLD": 4200000, "claimLockedAssets": 45500000 @@ -534,7 +537,8 @@ "governance": { "energy": "./src/abis/governance-v2.abi.json", "tokenSnapshot": "./src/abis/governance-v2-merkle-proof.abi.json" - } + }, + "composableTasks": "./src/abis/composable-tasks.abi.json" }, "cron": { "transactionCollectorMaxHyperblocks": 10, From 453dd7cfb2c16774ffdb48bd0bea3846ef2515c7 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Dec 2023 16:12:12 +0200 Subject: [PATCH 078/313] Update locked token wrapped testnet address Signed-off-by: Claudiu Lataretu --- src/config/testnet.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/testnet.json b/src/config/testnet.json index e6f26559a..3a673f2bd 100644 --- a/src/config/testnet.json +++ b/src/config/testnet.json @@ -26,7 +26,7 @@ "feesCollector": "erd1qqqqqqqqqqqqqpgqt5mtw8hjy5n8vkap5ea3nmd4qd9rwyzvexksw07tns", "energyUpdate": "erd1qqqqqqqqqqqqqpgqyax9yu7uv04408epmrn23n6t252pylva295qv40w7y", "tokenUnstake": "erd1qqqqqqqqqqqqqpgqmc6lkxzn7cmj2c3ztczapds9ch2c4w9h0n4stl2uq7", - "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqp628sk5y6c5w8twdddmf4n6kcpatnfuk295q0xuzle", + "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqs9c75ev7kzttp33gq0868qcwynv2ajgt0n4s9k628r", "escrow": "erd1qqqqqqqqqqqqqpgqcsmrv0hh6fvmhe4e7q4resj7rkeqxp7p0n4s0qfapx" }, "governance": { From 2be4c52bf63c84a27311be87257233f9205b04c3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 5 Dec 2023 14:06:36 +0200 Subject: [PATCH 079/313] update testnet config addresses Signed-off-by: Claudiu Lataretu --- src/config/testnet.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/testnet.json b/src/config/testnet.json index 3a673f2bd..1bece1f52 100644 --- a/src/config/testnet.json +++ b/src/config/testnet.json @@ -24,8 +24,8 @@ ], "simpleLockEnergy": "erd1qqqqqqqqqqqqqpgqgcd4wquv46u5u9gvvz4x3avuwmjqwfstexksk37nf4", "feesCollector": "erd1qqqqqqqqqqqqqpgqt5mtw8hjy5n8vkap5ea3nmd4qd9rwyzvexksw07tns", - "energyUpdate": "erd1qqqqqqqqqqqqqpgqyax9yu7uv04408epmrn23n6t252pylva295qv40w7y", - "tokenUnstake": "erd1qqqqqqqqqqqqqpgqmc6lkxzn7cmj2c3ztczapds9ch2c4w9h0n4stl2uq7", + "energyUpdate": "erd1qqqqqqqqqqqqqpgq37e5r67hvtrkyhs6yadwvwtk3rxk792e0n4s066pa5", + "tokenUnstake": "erd1qqqqqqqqqqqqqpgqyxus98ytge6yt2ptesjxa5dzjfuz60dgexks3zee0k", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqs9c75ev7kzttp33gq0868qcwynv2ajgt0n4s9k628r", "escrow": "erd1qqqqqqqqqqqqqpgqcsmrv0hh6fvmhe4e7q4resj7rkeqxp7p0n4s0qfapx" }, From e2216020097e33e3faf838bb6af978bb34ad6c9f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 6 Dec 2023 16:58:08 +0200 Subject: [PATCH 080/313] MEX-414: add compose tasks for swap and unwrap egld Signed-off-by: Claudiu Lataretu --- .../composable.tasks.module.ts | 5 ++- .../services/composable.tasks.transaction.ts | 43 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/modules/composable-tasks/composable.tasks.module.ts b/src/modules/composable-tasks/composable.tasks.module.ts index 7c170280f..24a5cb46d 100644 --- a/src/modules/composable-tasks/composable.tasks.module.ts +++ b/src/modules/composable-tasks/composable.tasks.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; import { ComposableTasksTransactionService } from './services/composable.tasks.transaction'; import { ComposableTasksResolver } from './composable.tasks.resolver'; +import { WrappingModule } from '../wrapping/wrap.module'; @Module({ - imports: [MXCommunicationModule], + imports: [MXCommunicationModule, WrappingModule], providers: [ComposableTasksTransactionService, ComposableTasksResolver], - exports: [], + exports: [ComposableTasksTransactionService], }) export class ComposableTasksModule {} diff --git a/src/modules/composable-tasks/services/composable.tasks.transaction.ts b/src/modules/composable-tasks/services/composable.tasks.transaction.ts index 7e6f76553..b0f2992d7 100644 --- a/src/modules/composable-tasks/services/composable.tasks.transaction.ts +++ b/src/modules/composable-tasks/services/composable.tasks.transaction.ts @@ -25,6 +25,7 @@ import BigNumber from 'bignumber.js'; import { gasConfig, mxConfig } from 'src/config'; import { EgldOrEsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; import { decimalToHex } from 'src/utils/token.converters'; +import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; export type ComposableTask = { type: ComposableTaskType; @@ -33,7 +34,10 @@ export type ComposableTask = { @Injectable() export class ComposableTasksTransactionService { - constructor(private readonly mxPorxy: MXProxyService) {} + constructor( + private readonly mxPorxy: MXProxyService, + private readonly wrapAbi: WrapAbiService, + ) {} async getComposeTasksTransaction( payment: EsdtTokenPayment, @@ -99,7 +103,7 @@ export class ComposableTasksTransactionService { return interaction.buildTransaction().toPlainObject(); } - async wrapEgldAndSwapTransaction( + async wrapEgldAndSwapFixedInputTransaction( value: string, tokenOutID: string, tokenOutAmountMin: string, @@ -108,7 +112,7 @@ export class ComposableTasksTransactionService { type: ComposableTaskType.WRAP_EGLD, arguments: [], }; - console.log(decimalToHex(new BigNumber(tokenOutAmountMin))); + const swapTask: ComposableTask = { type: ComposableTaskType.SWAP, arguments: [ @@ -135,4 +139,37 @@ export class ComposableTasksTransactionService { [wrapTask, swapTask], ); } + + async swapFixedInputAndUnwrapEgldTransaction( + payment: EsdtTokenPayment, + minimumValue: string, + ): Promise { + const wrappedEgldTokenID = await this.wrapAbi.wrappedEgldTokenID(); + + const swapTask: ComposableTask = { + type: ComposableTaskType.SWAP, + arguments: [ + new BytesValue(Buffer.from(wrappedEgldTokenID, 'utf-8')), + new BytesValue( + Buffer.from( + decimalToHex(new BigNumber(minimumValue)), + 'hex', + ), + ), + ], + }; + const unwrapTask: ComposableTask = { + type: ComposableTaskType.UNWRAP_EGLD, + arguments: [], + }; + + return this.getComposeTasksTransaction( + payment, + new EgldOrEsdtTokenPayment({ + tokenIdentifier: 'EGLD', + amount: minimumValue, + }), + [swapTask, unwrapTask], + ); + } } From 34d81dd44c6cc44d7aebab46a70c59304e8d7e9d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 6 Dec 2023 16:59:50 +0200 Subject: [PATCH 081/313] MEX-414: use composable tasks for pair swap tokens fixed input Signed-off-by: Claudiu Lataretu --- src/modules/pair/pair.module.ts | 2 + src/modules/pair/pair.resolver.ts | 4 +- .../services/pair.transactions.service.ts | 125 ++++++------------ 3 files changed, 47 insertions(+), 84 deletions(-) diff --git a/src/modules/pair/pair.module.ts b/src/modules/pair/pair.module.ts index 8e7b1310a..c02ea8327 100644 --- a/src/modules/pair/pair.module.ts +++ b/src/modules/pair/pair.module.ts @@ -13,6 +13,7 @@ import { DatabaseModule } from 'src/services/database/database.module'; import { TokenModule } from '../tokens/token.module'; import { RouterModule } from '../router/router.module'; import { CommonAppModule } from 'src/common.app.module'; +import { ComposableTasksModule } from '../composable-tasks/composable.tasks.module'; @Module({ imports: [ CommonAppModule, @@ -23,6 +24,7 @@ import { CommonAppModule } from 'src/common.app.module'; DatabaseModule, forwardRef(() => RouterModule), forwardRef(() => TokenModule), + ComposableTasksModule, ], providers: [ PairService, diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 89cd6a0a7..7d9151a7b 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -320,11 +320,11 @@ export class PairResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => [TransactionModel]) + @Query(() => TransactionModel) async swapTokensFixedInput( @Args() args: SwapTokensFixedInputArgs, @AuthUser() user: UserAuthResult, - ): Promise { + ): Promise { return this.transactionService.swapTokensFixedInput(user.address, args); } diff --git a/src/modules/pair/services/pair.transactions.service.ts b/src/modules/pair/services/pair.transactions.service.ts index 7b78c2854..9ad582dc3 100644 --- a/src/modules/pair/services/pair.transactions.service.ts +++ b/src/modules/pair/services/pair.transactions.service.ts @@ -24,6 +24,8 @@ import { InputTokenModel } from 'src/models/inputToken.model'; import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; import { PairAbiService } from './pair.abi.service'; import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; +import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; @Injectable() export class PairTransactionService { @@ -33,6 +35,7 @@ export class PairTransactionService { private readonly pairAbi: PairAbiService, private readonly wrapAbi: WrapAbiService, private readonly wrapTransaction: WrapTransactionsService, + private readonly composableTasksTransaction: ComposableTasksTransactionService, ) {} async addInitialLiquidityBatch( @@ -266,7 +269,7 @@ export class PairTransactionService { async swapTokensFixedInput( sender: string, args: SwapTokensFixedInputArgs, - ): Promise { + ): Promise { await this.validateTokens(args.pairAddress, [ new InputTokenModel({ tokenID: args.tokenInID, @@ -277,13 +280,6 @@ export class PairTransactionService { nonce: 0, }), ]); - const transactions = []; - let endpointArgs: TypedValue[]; - const [wrappedTokenID, contract, trustedSwapPairs] = await Promise.all([ - this.wrapAbi.wrappedEgldTokenID(), - this.mxProxy.getPairSmartContract(args.pairAddress), - this.pairAbi.trustedSwapPairs(args.pairAddress), - ]); const amountIn = new BigNumber(args.amountIn); const amountOut = new BigNumber(args.amountOut); @@ -292,85 +288,50 @@ export class PairTransactionService { .multipliedBy(amountOut) .integerValue(); + if (args.tokenInID === mxConfig.EGLDIdentifier) { + return this.composableTasksTransaction.wrapEgldAndSwapFixedInputTransaction( + args.amountIn, + args.tokenOutID, + amountOutMin.toFixed(), + ); + } + + if (args.tokenOutID === mxConfig.EGLDIdentifier) { + return this.composableTasksTransaction.swapFixedInputAndUnwrapEgldTransaction( + new EsdtTokenPayment({ + tokenIdentifier: args.tokenInID, + tokenNonce: 0, + amount: args.amountIn, + }), + amountOutMin.toFixed(), + ); + } + + const [contract, trustedSwapPairs] = await Promise.all([ + this.mxProxy.getPairSmartContract(args.pairAddress), + this.pairAbi.trustedSwapPairs(args.pairAddress), + ]); + const gasLimit = trustedSwapPairs.length === 0 ? gasConfig.pairs.swapTokensFixedInput.default : gasConfig.pairs.swapTokensFixedInput.withFeeSwap; - switch (mxConfig.EGLDIdentifier) { - case args.tokenInID: - transactions.push( - await this.wrapTransaction.wrapEgld(sender, args.amountIn), - ); - endpointArgs = [ - BytesValue.fromUTF8(args.tokenOutID), - new BigUIntValue(amountOutMin), - ]; - transactions.push( - contract.methodsExplicit - .swapTokensFixedInput(endpointArgs) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - wrappedTokenID, - new BigNumber(amountIn), - ), - ) - .withGasLimit(gasLimit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - break; - case args.tokenOutID: - endpointArgs = [ - BytesValue.fromUTF8(wrappedTokenID), - new BigUIntValue(amountOutMin), - ]; - transactions.push( - contract.methodsExplicit - .swapTokensFixedInput(endpointArgs) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - args.tokenInID, - new BigNumber(amountIn), - ), - ) - .withGasLimit(gasLimit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - transactions.push( - await this.wrapTransaction.unwrapEgld( - sender, - amountOutMin.toString(), - ), - ); - break; - default: - endpointArgs = [ - BytesValue.fromUTF8(args.tokenOutID), - new BigUIntValue(amountOutMin), - ]; - - transactions.push( - contract.methodsExplicit - .swapTokensFixedInput(endpointArgs) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - args.tokenInID, - new BigNumber(amountIn), - ), - ) - .withGasLimit(gasLimit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - break; - } - - return transactions; + return contract.methodsExplicit + .swapTokensFixedInput([ + BytesValue.fromUTF8(args.tokenOutID), + new BigUIntValue(amountOutMin), + ]) + .withSingleESDTTransfer( + TokenTransfer.fungibleFromBigInteger( + args.tokenInID, + new BigNumber(amountIn), + ), + ) + .withGasLimit(gasLimit) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); } @ErrorLoggerAsync({ From 5e6bd031429ec2ed7993edd69242cc9d5041f28a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 6 Dec 2023 17:01:34 +0200 Subject: [PATCH 082/313] MEX-414: use composable tasks for auto router single swap fixed input Signed-off-by: Claudiu Lataretu --- src/modules/auto-router/auto-router.module.ts | 2 ++ .../auto-router/auto-router.resolver.ts | 2 +- .../services/auto-router.service.ts | 34 ++++++++++++------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/modules/auto-router/auto-router.module.ts b/src/modules/auto-router/auto-router.module.ts index 15b8ad6d9..5b984568d 100644 --- a/src/modules/auto-router/auto-router.module.ts +++ b/src/modules/auto-router/auto-router.module.ts @@ -13,6 +13,7 @@ import { AutoRouterResolver } from './auto-router.resolver'; import { PairTransactionService } from '../pair/services/pair.transactions.service'; import { RemoteConfigModule } from '../remote-config/remote-config.module'; import { TokenModule } from '../tokens/token.module'; +import { ComposableTasksModule } from '../composable-tasks/composable.tasks.module'; @Module({ imports: [ @@ -24,6 +25,7 @@ import { TokenModule } from '../tokens/token.module'; WrappingModule, RouterModule, TokenModule, + ComposableTasksModule, RemoteConfigModule, ], providers: [ diff --git a/src/modules/auto-router/auto-router.resolver.ts b/src/modules/auto-router/auto-router.resolver.ts index 4e2fb195c..74b476461 100644 --- a/src/modules/auto-router/auto-router.resolver.ts +++ b/src/modules/auto-router/auto-router.resolver.ts @@ -57,7 +57,7 @@ export class AutoRouterResolver { async transactions( @Parent() parent: AutoRouteModel, @AuthUser() user: UserAuthResult, - ) { + ): Promise { try { return await this.autoRouterService.getTransactions( user.address, diff --git a/src/modules/auto-router/services/auto-router.service.ts b/src/modules/auto-router/services/auto-router.service.ts index 35801bc0f..06aab0843 100644 --- a/src/modules/auto-router/services/auto-router.service.ts +++ b/src/modules/auto-router/services/auto-router.service.ts @@ -25,6 +25,7 @@ import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { PairComputeService } from 'src/modules/pair/services/pair.compute.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; import { TokenService } from 'src/modules/tokens/services/token.service'; +import { TransactionModel } from 'src/models/transaction.model'; @Injectable() export class AutoRouterService { @@ -446,20 +447,27 @@ export class AutoRouterService { return routePairs; } - async getTransactions(sender: string, parent: AutoRouteModel) { + async getTransactions( + sender: string, + parent: AutoRouteModel, + ): Promise { if (parent.pairs.length == 1) { - if (parent.swapType === SWAP_TYPE.fixedInput) - return await this.pairTransactionService.swapTokensFixedInput( - sender, - { - pairAddress: parent.pairs[0].address, - tokenInID: parent.tokenInID, - tokenOutID: parent.tokenOutID, - amountIn: parent.amountIn, - amountOut: parent.amountOut, - tolerance: parent.tolerance, - }, - ); + if (parent.swapType === SWAP_TYPE.fixedInput) { + const transaction = + await this.pairTransactionService.swapTokensFixedInput( + sender, + { + pairAddress: parent.pairs[0].address, + tokenInID: parent.tokenInID, + tokenOutID: parent.tokenOutID, + amountIn: parent.amountIn, + amountOut: parent.amountOut, + tolerance: parent.tolerance, + }, + ); + + return [transaction]; + } return await this.pairTransactionService.swapTokensFixedOutput( sender, From 90c6c93f9a73ccec11d608045efb6804bf75101c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 7 Dec 2023 16:53:55 +0200 Subject: [PATCH 083/313] MEX-414: add tasks for wrap and multi pair swaps transaction Signed-off-by: Claudiu Lataretu --- .../auto-router.transactions.service.ts | 124 ++++++++++++------ 1 file changed, 87 insertions(+), 37 deletions(-) diff --git a/src/modules/auto-router/services/auto-router.transactions.service.ts b/src/modules/auto-router/services/auto-router.transactions.service.ts index 43b0f8791..d95ce8691 100644 --- a/src/modules/auto-router/services/auto-router.transactions.service.ts +++ b/src/modules/auto-router/services/auto-router.transactions.service.ts @@ -14,12 +14,18 @@ import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.tran import { TransactionModel } from '../../../models/transaction.model'; import { MXProxyService } from '../../../services/multiversx-communication/mx.proxy.service'; import { SWAP_TYPE } from '../models/auto-route.model'; +import { ComposableTaskType } from 'src/modules/composable-tasks/models/composable.tasks.model'; +import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { EgldOrEsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; +import { decimalToHex } from 'src/utils/token.converters'; @Injectable() export class AutoRouterTransactionService { constructor( private readonly mxProxy: MXProxyService, private readonly transactionsWrapService: WrapTransactionsService, + private readonly composeTasksTransactionService: ComposableTasksTransactionService, ) {} async multiPairSwap( @@ -27,30 +33,23 @@ export class AutoRouterTransactionService { args: MultiSwapTokensArgs, ): Promise { const transactions = []; - const [contract, wrapTransaction, unwrapTransaction] = - await Promise.all([ - this.mxProxy.getRouterSmartContract(), - this.wrapIfNeeded( - sender, - args.tokenInID, - args.intermediaryAmounts[0], - ), - this.unwrapIfNeeded( - sender, - args.tokenOutID, - args.intermediaryAmounts[ - args.intermediaryAmounts.length - 1 - ], - ), - ]); - - if (wrapTransaction) transactions.push(wrapTransaction); + const contract = await this.mxProxy.getRouterSmartContract(); const amountIn = new BigNumber(args.intermediaryAmounts[0]).plus( new BigNumber(args.intermediaryAmounts[0]).multipliedBy( args.swapType === SWAP_TYPE.fixedOutput ? args.tolerance : 0, ), ); + + if (args.tokenInID === mxConfig.EGLDIdentifier) { + return [ + await this.wrapEgldAndMultiSwapFixedInputTransaction( + args.intermediaryAmounts[0], + args, + ), + ]; + } + const gasLimit = args.addressRoute.length * gasConfig.router.multiPairSwapMultiplier; @@ -73,8 +72,16 @@ export class AutoRouterTransactionService { .buildTransaction() .toPlainObject(), ); - - if (unwrapTransaction) transactions.push(unwrapTransaction); + if (args.tokenOutID === mxConfig.EGLDIdentifier) { + transactions.push( + await this.transactionsWrapService.unwrapEgld( + sender, + args.intermediaryAmounts[ + args.intermediaryAmounts.length - 1 + ], + ), + ); + } return transactions; } @@ -192,26 +199,69 @@ export class AutoRouterTransactionService { return swaps; } - async wrapIfNeeded( - sender: string, - tokenID: string, - amount: string, + async wrapEgldAndMultiSwapFixedInputTransaction( + value: string, + args: MultiSwapTokensArgs, ): Promise { - if (tokenID === mxConfig.EGLDIdentifier) { - return await this.transactionsWrapService.wrapEgld(sender, amount); - } - } + const swaps: BytesValue[] = []; - async unwrapIfNeeded( - sender: string, - tokenID: string, - amount: string, - ): Promise { - if (tokenID === mxConfig.EGLDIdentifier) { - return await this.transactionsWrapService.unwrapEgld( - sender, - amount, + const intermediaryTolerance = args.tolerance / args.addressRoute.length; + + for (const [index, address] of args.addressRoute.entries()) { + const intermediaryToleranceMultiplier = + args.addressRoute.length - index; + + const toleranceAmount = new BigNumber( + args.intermediaryAmounts[index + 1], + ).multipliedBy( + intermediaryToleranceMultiplier * intermediaryTolerance, + ); + + const amountOutMin = new BigNumber( + args.intermediaryAmounts[index + 1], + ) + .minus(toleranceAmount) + .integerValue(); + + swaps.push( + ...[ + new BytesValue( + Buffer.from(Address.fromString(address).hex(), 'hex'), + ), + BytesValue.fromUTF8(args.tokenRoute[index + 1]), + new BytesValue( + Buffer.from( + decimalToHex(new BigNumber(amountOutMin)), + 'hex', + ), + ), + ], ); } + + return this.composeTasksTransactionService.getComposeTasksTransaction( + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: value, + }), + new EgldOrEsdtTokenPayment({ + tokenIdentifier: args.tokenRoute[args.tokenRoute.length - 1], + nonce: 0, + amount: args.intermediaryAmounts[ + args.intermediaryAmounts.length - 1 + ], + }), + [ + { + type: ComposableTaskType.WRAP_EGLD, + arguments: [], + }, + { + type: ComposableTaskType.ROUTER_SWAP, + arguments: swaps, + }, + ], + ); } } From 79508b55db0ad6b43f1e2a71ed5067aadfd1160a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 7 Dec 2023 17:26:34 +0200 Subject: [PATCH 084/313] MEX-414: add unit tests for composable tasks transaction Signed-off-by: Claudiu Lataretu --- src/config/test.json | 3 +- .../composable.tasks.transaction.spec.ts | 150 ++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts diff --git a/src/config/test.json b/src/config/test.json index 066923fe8..b7f633bab 100644 --- a/src/config/test.json +++ b/src/config/test.json @@ -5,7 +5,8 @@ "WEGLD_USDC": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfsr2ycrs", "proxyDexAddress": { "v1": "erd1qqqqqqqqqqqqqpgqrc4pg2xarca9z34njcxeur622qmfjp8w2jps89fxnl" - } + }, + "composableTasks": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu" }, "farms": { "v1.2": [ diff --git a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts new file mode 100644 index 000000000..40996c560 --- /dev/null +++ b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts @@ -0,0 +1,150 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ComposableTasksTransactionService } from '../services/composable.tasks.transaction'; +import { MXProxyServiceProvider } from 'src/services/multiversx-communication/mx.proxy.service.mock'; +import { WrapAbiServiceProvider } from 'src/modules/wrapping/mocks/wrap.abi.service.mock'; +import { WinstonModule } from 'nest-winston'; +import winston from 'winston'; +import { ConfigModule } from '@nestjs/config'; +import { ApiConfigService } from 'src/helpers/api.config.service'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; +import { EgldOrEsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; +import { ComposableTaskType } from '../models/composable.tasks.model'; +import { Address } from '@multiversx/sdk-core/out'; + +describe('Composable Tasks Transaction', () => { + let module: TestingModule; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [ + WinstonModule.forRoot({ + transports: [new winston.transports.Console({})], + }), + ConfigModule.forRoot({}), + ], + providers: [ + ComposableTasksTransactionService, + MXProxyServiceProvider, + WrapAbiServiceProvider, + ApiConfigService, + ], + }).compile(); + }); + + it('should be defined', () => { + const service = module.get( + ComposableTasksTransactionService, + ); + expect(service).toBeDefined(); + }); + + it('should get compose tasks transaction', async () => { + const service = module.get( + ComposableTasksTransactionService, + ); + + const payment = new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '1000000000000000000', + }); + const tokenOut = new EgldOrEsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + nonce: 0, + amount: '1000000000000000000', + }); + + const transaction = await service.getComposeTasksTransaction( + payment, + tokenOut, + [ + { + type: ComposableTaskType.WRAP_EGLD, + arguments: [], + }, + ], + ); + + expect(transaction).toEqual({ + chainID: 'T', + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MGRlMGI2YjNhNzY0MDAwMEBA', + gasLimit: 30000000, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: Address.Zero().bech32(), + receiverUsername: undefined, + sender: '', + senderUsername: undefined, + signature: undefined, + value: '1000000000000000000', + version: 1, + }); + }); + + it('should get wrap and swap fixed input compose transaction', async () => { + const service = module.get( + ComposableTasksTransactionService, + ); + + const transaction = await service.wrapEgldAndSwapFixedInputTransaction( + '1000000000000000000', + 'USDC-123456', + '20000000', + ); + + expect(transaction).toEqual({ + chainID: 'T', + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAxMzEyZDAwQEBAMDJAMDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDQwMTMxMmQwMA==', + gasLimit: 30000000, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: Address.Zero().bech32(), + receiverUsername: undefined, + sender: '', + senderUsername: undefined, + signature: undefined, + value: '1000000000000000000', + version: 1, + }); + }); + + it('should get swap fixed input and unwrap compose transaction', async () => { + const service = module.get( + ComposableTasksTransactionService, + ); + + const transaction = + await service.swapFixedInputAndUnwrapEgldTransaction( + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '20000000', + }), + '1000000000000000000', + ); + + expect(transaction).toEqual({ + chainID: 'T', + data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDEzMTJkMDBANjM2ZjZkNzA2ZjczNjU1NDYxNzM2YjczQDAwMDAwMDA0NDU0NzRjNDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAyQDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAxQA==', + gasLimit: 30000000, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: Address.Zero().bech32(), + receiverUsername: undefined, + sender: '', + senderUsername: undefined, + signature: undefined, + value: '0', + version: 1, + }); + }); +}); From 775a07dd6c7d2a8d8cf6f290f921793361816ece Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 7 Dec 2023 17:35:41 +0200 Subject: [PATCH 085/313] MEX-414: use composable task transaction for pair transactions unit test Signed-off-by: Claudiu Lataretu --- .../specs/pair.transactions.service.spec.ts | 60 ++++++------------- 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/src/modules/pair/specs/pair.transactions.service.spec.ts b/src/modules/pair/specs/pair.transactions.service.spec.ts index 877076abd..01b0f5abe 100644 --- a/src/modules/pair/specs/pair.transactions.service.spec.ts +++ b/src/modules/pair/specs/pair.transactions.service.spec.ts @@ -19,6 +19,7 @@ import { RouterAbiServiceProvider } from 'src/modules/router/mocks/router.abi.se import { WinstonModule } from 'nest-winston'; import winston from 'winston'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; describe('TransactionPairService', () => { let module: TestingModule; @@ -45,6 +46,7 @@ describe('TransactionPairService', () => { WrapTransactionsService, WrapService, TokenServiceProvider, + ComposableTasksTransactionService, PairTransactionService, ], }).compile(); @@ -454,47 +456,23 @@ describe('TransactionPairService', () => { }, ); - expect(transactions).toEqual([ - { - nonce: 0, - value: '5', - receiver: - 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.wrapeGLD, - data: encodeTransactionData('wrapEgld'), - chainID: mxConfig.chainID, - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }, - { - nonce: 0, - value: '0', - receiver: Address.fromHex( - '0000000000000000000000000000000000000000000000000000000000000012', - ).bech32(), - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.pairs.swapTokensFixedInput.default, - data: encodeTransactionData( - 'ESDTTransfer@WEGLD-123456@5@swapTokensFixedInput@MEX-123456@4', - ), - chainID: mxConfig.chainID, - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }, - ]); + expect(transactions).toEqual({ + chainID: 'T', + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwNEBAQDAyQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMTA0', + gasLimit: 30000000, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: Address.Zero().bech32(), + receiverUsername: undefined, + sender: '', + senderUsername: undefined, + signature: undefined, + value: '5', + version: 1, + }); }); it('should get swap tokens fixed output transaction + unwrap tx', async () => { From cdd84f1abf15ad9b3862281f67bbd1ce397ddee4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 11 Dec 2023 17:22:14 +0200 Subject: [PATCH 086/313] MEX-414: add composable tasks resolver Signed-off-by: Claudiu Lataretu --- .../composable-tasks/composable.tasks.resolver.ts | 13 +++++++++++++ .../models/composable.tasks.model.ts | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 src/modules/composable-tasks/composable.tasks.resolver.ts diff --git a/src/modules/composable-tasks/composable.tasks.resolver.ts b/src/modules/composable-tasks/composable.tasks.resolver.ts new file mode 100644 index 000000000..9967849fa --- /dev/null +++ b/src/modules/composable-tasks/composable.tasks.resolver.ts @@ -0,0 +1,13 @@ +import { Query, Resolver } from '@nestjs/graphql'; +import { ComposableTaskModel } from './models/composable.tasks.model'; +import { scAddress } from 'src/config'; + +@Resolver() +export class ComposableTasksResolver { + @Query(() => ComposableTaskModel) + async composableTask(): Promise { + return new ComposableTaskModel({ + address: scAddress.composableTasks, + }); + } +} diff --git a/src/modules/composable-tasks/models/composable.tasks.model.ts b/src/modules/composable-tasks/models/composable.tasks.model.ts index d05bed814..81e029369 100644 --- a/src/modules/composable-tasks/models/composable.tasks.model.ts +++ b/src/modules/composable-tasks/models/composable.tasks.model.ts @@ -25,4 +25,8 @@ export class ComposableTaskEnumType { export class ComposableTaskModel { @Field() address: string; + + constructor(init: Partial) { + Object.assign(this, init); + } } From bc6d7e8b1d86cfaa7230e2c4a69b7d381a1e9fc1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 11 Dec 2023 17:43:08 +0200 Subject: [PATCH 087/313] MEX-414: fix auto router unit test with composable task Signed-off-by: Claudiu Lataretu --- .../specs/auto-router.service.spec.ts | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/modules/auto-router/specs/auto-router.service.spec.ts b/src/modules/auto-router/specs/auto-router.service.spec.ts index 497097837..1b835206c 100644 --- a/src/modules/auto-router/specs/auto-router.service.spec.ts +++ b/src/modules/auto-router/specs/auto-router.service.spec.ts @@ -31,6 +31,7 @@ import { ConfigModule } from '@nestjs/config'; import { ApiConfigService } from 'src/helpers/api.config.service'; import winston from 'winston'; import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; +import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; describe('AutoRouterService', () => { let service: AutoRouterService; @@ -77,6 +78,7 @@ describe('AutoRouterService', () => { AutoRouterService, AutoRouterComputeService, AutoRouterTransactionService, + ComposableTasksTransactionService, ApiConfigService, ], exports: [], @@ -271,35 +273,13 @@ describe('AutoRouterService', () => { { nonce: 0, value: '1000000000000000000', - receiver: - 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + receiver: Address.Zero().bech32(), sender: '', receiverUsername: undefined, senderUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.wrapeGLD, - data: encodeTransactionData('wrapEgld'), - chainID: 'T', - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }, - { - nonce: 0, - value: '0', - receiver: Address.fromHex( - '0000000000000000000000000000000000000000000000000000000000000013', - ).bech32(), - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.pairs.swapTokensFixedInput.default, - data: encodeTransactionData( - 'ESDTTransfer@WEGLD-123456@01000000000000000000@swapTokensFixedInput@USDC-123456@4911161424654532', - ), + gasLimit: 30000000, + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNzExNzJhY2UwMjZiMGM0QEBAMDJAMDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDcxMTcyYWNlMDI2YjBjNA==', chainID: 'T', version: 1, options: undefined, From 155d06a80a010b0aee889e57bd0bec9f42e8cfac Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 11 Dec 2023 17:43:42 +0200 Subject: [PATCH 088/313] MEX-414: add missing dependency for position creator unit tests Signed-off-by: Claudiu Lataretu --- .../position-creator/specs/position.creator.transaction.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 0acd9f074..7cc3b8bab 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -30,6 +30,7 @@ import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { encodeTransactionData } from 'src/helpers/helpers'; import exp from 'constants'; import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; +import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -63,6 +64,7 @@ describe('PositionCreatorTransaction', () => { StakingProxyAbiServiceProvider, TokenServiceProvider, RemoteConfigGetterServiceProvider, + ComposableTasksTransactionService, MXProxyServiceProvider, ConfigService, ApiConfigService, From 5083f9669187fb61626176e6e3efe135629f1d43 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 11 Dec 2023 18:51:26 +0200 Subject: [PATCH 089/313] MEX-410: return batch transactions on position creator - use batch transactions on position creator with EGLD token and consolidation on positions Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 12 +- .../services/position.creator.transaction.ts | 67 ++++- .../position.creator.transaction.spec.ts | 244 +++++++++--------- 3 files changed, 190 insertions(+), 133 deletions(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 6885f73c5..152647d77 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -34,14 +34,14 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => TransactionModel) + @Query(() => [TransactionModel]) async createFarmPositionSingleToken( @AuthUser() user: UserAuthResult, @Args('farmAddress') farmAddress: string, @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createFarmPositionSingleToken( user.address, farmAddress, @@ -57,14 +57,14 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => TransactionModel) + @Query(() => [TransactionModel]) async createDualFarmPositionSingleToken( @AuthUser() user: UserAuthResult, @Args('dualFarmAddress') dualFarmAddress: string, @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createDualFarmPositionSingleToken( user.address, dualFarmAddress, @@ -80,14 +80,14 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => TransactionModel) + @Query(() => [TransactionModel]) async createStakingPositionSingleToken( @AuthUser() user: UserAuthResult, @Args('stakingAddress') stakingAddress: string, @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createStakingPositionSingleToken( user.address, stakingAddress, diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index b8b476641..9edc0fb91 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -20,6 +20,7 @@ import { AutoRouterTransactionService } from 'src/modules/auto-router/services/a import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; import { PairService } from 'src/modules/pair/services/pair.service'; import { TokenService } from 'src/modules/tokens/services/token.service'; +import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.transactions.service'; @Injectable() export class PositionCreatorTransactionService { @@ -33,6 +34,7 @@ export class PositionCreatorTransactionService { private readonly stakingAbi: StakingAbiService, private readonly stakingProxyAbi: StakingProxyAbiService, private readonly tokenService: TokenService, + private readonly wrapTransaction: WrapTransactionsService, private readonly mxProxy: MXProxyService, ) {} @@ -84,14 +86,25 @@ export class PositionCreatorTransactionService { farmAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + ): Promise { const [pairAddress, farmTokenID, uniqueTokensIDs] = await Promise.all([ this.farmAbiV2.pairContractAddress(farmAddress), this.farmAbiV2.farmTokenID(farmAddress), this.tokenService.getUniqueTokenIDs(false), ]); - if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { + const transactions = []; + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length > 1 + ) { + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); + } else if ( + !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && + payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier + ) { throw new Error('Invalid ESDT token payment'); } @@ -110,7 +123,7 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); - return contract.methodsExplicit + const transaction = contract.methodsExplicit .createFarmPosFromSingleToken([ new AddressValue(Address.fromBech32(farmAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), @@ -131,6 +144,9 @@ export class PositionCreatorTransactionService { .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); + + transactions.push(transaction); + return transactions; } async createDualFarmPositionSingleToken( @@ -138,7 +154,7 @@ export class PositionCreatorTransactionService { stakingProxyAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + ): Promise { const [pairAddress, dualYieldTokenID, uniqueTokensIDs] = await Promise.all([ this.stakingProxyAbi.pairAddress(stakingProxyAddress), @@ -146,7 +162,18 @@ export class PositionCreatorTransactionService { this.tokenService.getUniqueTokenIDs(false), ]); - if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { + const transactions = []; + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length > 1 + ) { + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); + } else if ( + !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && + payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier + ) { throw new Error('Invalid ESDT token payment'); } @@ -165,7 +192,7 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); - return contract.methodsExplicit + const transaction = contract.methodsExplicit .createMetastakingPosFromSingleToken([ new AddressValue(Address.fromBech32(stakingProxyAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), @@ -186,6 +213,9 @@ export class PositionCreatorTransactionService { .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); + + transactions.push(transaction); + return transactions; } async createStakingPositionSingleToken( @@ -193,7 +223,7 @@ export class PositionCreatorTransactionService { stakingAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + ): Promise { const [farmingTokenID, farmTokenID, uniqueTokensIDs] = await Promise.all([ this.stakingAbi.farmingTokenID(stakingAddress), @@ -201,7 +231,19 @@ export class PositionCreatorTransactionService { this.tokenService.getUniqueTokenIDs(false), ]); - if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { + const transactions = []; + + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length > 1 + ) { + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); + } else if ( + !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && + payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier + ) { throw new Error('Invalid ESDT token payment'); } @@ -231,7 +273,7 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoute.tokenRoute, }); - return contract.methodsExplicit + const transaction = contract.methodsExplicit .createFarmStakingPosFromSingleToken([ new AddressValue(Address.fromBech32(stakingAddress)), new BigUIntValue( @@ -257,6 +299,9 @@ export class PositionCreatorTransactionService { .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); + + transactions.push(transaction); + return transactions; } async createFarmPositionDualTokens( @@ -275,7 +320,7 @@ export class PositionCreatorTransactionService { ]); if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { - throw new Error('Invalid tokens payments'); + throw new Error('Invalid ESDT tokens payments'); } for (const payment of payments.slice(2)) { @@ -348,7 +393,7 @@ export class PositionCreatorTransactionService { ]); if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { - throw new Error('Invalid tokens payments'); + throw new Error('Invalid ESDT tokens payments'); } for (const payment of payments.slice(2)) { diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 0acd9f074..2c72ed27e 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -206,25 +206,27 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); it('should return transaction with merge farm tokens', async () => { @@ -251,25 +253,27 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); @@ -345,25 +349,27 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); it('should return transaction with merge dual farm tokens', async () => { @@ -397,25 +403,27 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); @@ -482,25 +490,27 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); it('should return transaction with merge staking tokens', async () => { @@ -525,25 +535,27 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); @@ -572,7 +584,7 @@ describe('PositionCreatorTransaction', () => { ], 0.01, ), - ).rejects.toThrowError('Invalid tokens payments'); + ).rejects.toThrowError('Invalid ESDT tokens payments'); }); it('should return error on invalid farm token merge', async () => { @@ -735,7 +747,7 @@ describe('PositionCreatorTransaction', () => { ], 0.01, ), - ).rejects.toThrowError('Invalid tokens payments'); + ).rejects.toThrowError('Invalid ESDT tokens payments'); }); it('should return error on invalid farm token merge', async () => { From 38656861888cd2405aad2078ecd6f6f368d10e5f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 12 Dec 2023 13:27:44 +0200 Subject: [PATCH 090/313] MEX-410: use EGLD for create liquidity position Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 9edc0fb91..e2d12b2c6 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -48,7 +48,10 @@ export class PositionCreatorTransactionService { false, ); - if (!uniqueTokensIDs.includes(payment.tokenIdentifier)) { + if ( + !uniqueTokensIDs.includes(payment.tokenIdentifier) || + payment.tokenIdentifier !== mxConfig.EGLDIdentifier + ) { throw new Error('Invalid ESDT token payment'); } @@ -61,24 +64,29 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); - return contract.methodsExplicit + const interaction = contract.methodsExplicit .createLpPosFromSingleToken([ new AddressValue(Address.fromBech32(pairAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), ...singleTokenPairInput.swapRouteArgs, ]) - .withSingleESDTTransfer( + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID); + + if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { + interaction.withValue(new BigNumber(payment.amount)); + } else { + interaction.withSingleESDTTransfer( TokenTransfer.fungibleFromBigInteger( payment.tokenIdentifier, new BigNumber(payment.amount), ), - ) - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); + ); + } + + return interaction.buildTransaction().toPlainObject(); } async createFarmPositionSingleToken( From 5d133f51a0bde067980743fd9c349a8253cca7ab Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 12 Dec 2023 14:59:54 +0200 Subject: [PATCH 091/313] MEX-410: fix token check Signed-off-by: Claudiu Lataretu --- .../position-creator/services/position.creator.transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index e2d12b2c6..b6623afc3 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -49,7 +49,7 @@ export class PositionCreatorTransactionService { ); if ( - !uniqueTokensIDs.includes(payment.tokenIdentifier) || + !uniqueTokensIDs.includes(payment.tokenIdentifier) && payment.tokenIdentifier !== mxConfig.EGLDIdentifier ) { throw new Error('Invalid ESDT token payment'); From d0bf2e34fa1ecf655d2ab773245e862cb7224926 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 12 Dec 2023 16:35:06 +0200 Subject: [PATCH 092/313] MEX-414: extract code snippet into own method Signed-off-by: Claudiu Lataretu --- .../services/composable.tasks.transaction.ts | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/modules/composable-tasks/services/composable.tasks.transaction.ts b/src/modules/composable-tasks/services/composable.tasks.transaction.ts index b0f2992d7..93fd40c96 100644 --- a/src/modules/composable-tasks/services/composable.tasks.transaction.ts +++ b/src/modules/composable-tasks/services/composable.tasks.transaction.ts @@ -46,26 +46,6 @@ export class ComposableTasksTransactionService { ): Promise { const contract = await this.mxPorxy.getComposableTasksSmartContract(); - const rawTasks: TypedValue[] = []; - - for (const task of tasks) { - rawTasks.push( - new EnumValue( - ComposableTaskEnumType.getEnumType(), - new EnumVariantDefinition( - task.type, - ComposableTaskEnumType.getEnumType().getVariantByName( - task.type, - ).discriminant, - ), - [], - ), - ); - rawTasks.push( - new List(new ListType(new BytesType()), task.arguments), - ); - } - let interaction = contract.methodsExplicit .composeTasks([ new Struct(EgldOrEsdtTokenPayment.getStructure(), [ @@ -79,7 +59,7 @@ export class ComposableTasksTransactionService { 'amount', ), ]), - ...rawTasks, + ...this.getRawTasks(tasks), ]) .withGasLimit(gasConfig.composableTasks.default) .withChainID(mxConfig.chainID); @@ -172,4 +152,28 @@ export class ComposableTasksTransactionService { [swapTask, unwrapTask], ); } + + private getRawTasks(tasks: ComposableTask[]): TypedValue[] { + const rawTasks: TypedValue[] = []; + + tasks.forEach((task) => { + rawTasks.push( + new EnumValue( + ComposableTaskEnumType.getEnumType(), + new EnumVariantDefinition( + task.type, + ComposableTaskEnumType.getEnumType().getVariantByName( + task.type, + ).discriminant, + ), + [], + ), + ); + rawTasks.push( + new List(new ListType(new BytesType()), task.arguments), + ); + }); + + return rawTasks; + } } From 0a25851265f5611b4a8a61fc61f8e9bd842c0a04 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 13 Dec 2023 11:52:04 +0200 Subject: [PATCH 093/313] MEX-414: fix typo Signed-off-by: Claudiu Lataretu --- .../composable-tasks/services/composable.tasks.transaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/composable-tasks/services/composable.tasks.transaction.ts b/src/modules/composable-tasks/services/composable.tasks.transaction.ts index 93fd40c96..d4c63d940 100644 --- a/src/modules/composable-tasks/services/composable.tasks.transaction.ts +++ b/src/modules/composable-tasks/services/composable.tasks.transaction.ts @@ -35,7 +35,7 @@ export type ComposableTask = { @Injectable() export class ComposableTasksTransactionService { constructor( - private readonly mxPorxy: MXProxyService, + private readonly mxProxy: MXProxyService, private readonly wrapAbi: WrapAbiService, ) {} @@ -44,7 +44,7 @@ export class ComposableTasksTransactionService { tokenOut: EgldOrEsdtTokenPayment, tasks: ComposableTask[], ): Promise { - const contract = await this.mxPorxy.getComposableTasksSmartContract(); + const contract = await this.mxProxy.getComposableTasksSmartContract(); let interaction = contract.methodsExplicit .composeTasks([ From a75fa6824dc4bccc739defb80007f378e4d2de79 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 13 Dec 2023 17:53:26 +0200 Subject: [PATCH 094/313] MEX-416: add query for user farm boosted rewards batch Signed-off-by: Claudiu Lataretu --- src/modules/farm/models/farm.model.ts | 17 +++++ src/modules/farm/v2/farm.v2.resolver.ts | 15 +++- .../farm/v2/services/farm.v2.service.ts | 70 ++++++++++++++++++- .../position.creator.module.ts | 2 + 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/modules/farm/models/farm.model.ts b/src/modules/farm/models/farm.model.ts index 3c099d579..81ca0cea1 100644 --- a/src/modules/farm/models/farm.model.ts +++ b/src/modules/farm/models/farm.model.ts @@ -37,11 +37,28 @@ export class RewardsModel { claimProgress: ClaimProgress; @Field({ nullable: true }) accumulatedRewards: string; + constructor(init?: Partial) { Object.assign(this, init); } } +@ObjectType() +export class BoostedRewardsModel { + @Field() + farmAddress: string; + @Field(() => [UserInfoByWeekModel], { nullable: true }) + boostedRewardsWeeklyInfo: UserInfoByWeekModel[]; + @Field(() => ClaimProgress, { nullable: true }) + claimProgress: ClaimProgress; + @Field({ nullable: true }) + accumulatedRewards: string; + + constructor(init?: Partial) { + Object.assign(this, init); + } +} + @ObjectType() export class ExitFarmTokensModel { @Field() diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 0c2e105cb..8445ebaf2 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -14,7 +14,7 @@ import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard' import { UserAuthResult } from 'src/modules/auth/user.auth.result'; import { AuthUser } from 'src/modules/auth/auth.user'; import { farmVersion } from 'src/utils/farm.utils'; -import { FarmVersion } from '../models/farm.model'; +import { BoostedRewardsModel, FarmVersion } from '../models/farm.model'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; @@ -167,4 +167,17 @@ export class FarmResolverV2 extends FarmResolver { } return this.farmAbi.userTotalFarmPosition(farmAddress, user.address); } + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => [BoostedRewardsModel], { nullable: true }) + async getFarmBoostedRewardsBatch( + @Args('farmsAddresses', { type: () => [String] }) + farmsAddresses: string[], + @AuthUser() user: UserAuthResult, + ): Promise { + return this.farmService.getFarmBoostedRewardsBatch( + farmsAddresses, + user.address, + ); + } } diff --git a/src/modules/farm/v2/services/farm.v2.service.ts b/src/modules/farm/v2/services/farm.v2.service.ts index ecc3390c5..8dda3aa8c 100644 --- a/src/modules/farm/v2/services/farm.v2.service.ts +++ b/src/modules/farm/v2/services/farm.v2.service.ts @@ -4,7 +4,7 @@ import { ContextGetterService } from 'src/services/context/context.getter.servic import { FarmServiceBase } from '../../base-module/services/farm.base.service'; import { FarmAbiServiceV2 } from './farm.v2.abi.service'; import { CalculateRewardsArgs } from '../../models/farm.args'; -import { RewardsModel } from '../../models/farm.model'; +import { BoostedRewardsModel, RewardsModel } from '../../models/farm.model'; import { FarmTokenAttributesModelV2 } from '../../models/farmTokenAttributes.model'; import { FarmComputeServiceV2 } from './farm.v2.compute.service'; import { FarmTokenAttributesV2 } from '@multiversx/sdk-exchange'; @@ -145,6 +145,74 @@ export class FarmServiceV2 extends FarmServiceBase { }); } + async getFarmBoostedRewardsBatch( + farmsAddresses: string[], + userAddress: string, + ): Promise { + const promises = farmsAddresses.map((address) => + this.getFarmBoostedRewards(address, userAddress), + ); + + return Promise.all(promises); + } + + async getFarmBoostedRewards( + farmAddress: string, + userAddress: string, + ): Promise { + const modelsList: UserInfoByWeekModel[] = []; + + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + farmAddress, + ); + + let lastActiveWeekUser = + await this.weeklyRewardsSplittingAbi.lastActiveWeekForUser( + farmAddress, + userAddress, + ); + if (lastActiveWeekUser === 0) { + lastActiveWeekUser = currentWeek; + } + const startWeek = Math.max( + currentWeek - constantsConfig.USER_MAX_CLAIM_WEEKS, + lastActiveWeekUser, + ); + + for (let week = startWeek; week <= currentWeek - 1; week++) { + if (week < 1) { + continue; + } + + const model = new UserInfoByWeekModel({ + scAddress: farmAddress, + userAddress: userAddress, + week: week, + }); + modelsList.push(model); + } + + const currentClaimProgress = + await this.weeklyRewardsSplittingAbi.currentClaimProgress( + farmAddress, + userAddress, + ); + + const userAccumulatedRewards = + await this.farmCompute.userAccumulatedRewards( + farmAddress, + userAddress, + currentWeek, + ); + + return new BoostedRewardsModel({ + farmAddress, + boostedRewardsWeeklyInfo: modelsList, + claimProgress: currentClaimProgress, + accumulatedRewards: userAccumulatedRewards, + }); + } + decodeFarmTokenAttributes( identifier: string, attributes: string, diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index bfd8391b2..43698c39c 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -11,6 +11,7 @@ import { StakingProxyModule } from '../staking-proxy/staking.proxy.module'; import { StakingModule } from '../staking/staking.module'; import { TokenModule } from '../tokens/token.module'; import { PositionCreatorTransactionResolver } from './position.creator.transaction.resolver'; +import { WrappingModule } from '../wrapping/wrap.module'; @Module({ imports: [ @@ -21,6 +22,7 @@ import { PositionCreatorTransactionResolver } from './position.creator.transacti StakingModule, StakingProxyModule, TokenModule, + WrappingModule, MXCommunicationModule, ], providers: [ From f531f1fa049a10df9974e058fd29dff9c12d2efd Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 13 Dec 2023 18:16:59 +0200 Subject: [PATCH 095/313] MEX-416: add query to generate claim farm boosted rewards Signed-off-by: Claudiu Lataretu --- .../farm/v2/farm.v2.transaction.resolver.ts | 14 ++++++++++++++ .../v2/services/farm.v2.transaction.service.ts | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/modules/farm/v2/farm.v2.transaction.resolver.ts b/src/modules/farm/v2/farm.v2.transaction.resolver.ts index 84f58a55f..79bc099ca 100644 --- a/src/modules/farm/v2/farm.v2.transaction.resolver.ts +++ b/src/modules/farm/v2/farm.v2.transaction.resolver.ts @@ -35,4 +35,18 @@ export class FarmTransactionResolverV2 { user.address, ); } + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => TransactionModel, { + description: 'Generate transaction to claim only boosted rewards', + }) + async claimFarmBoostedRewards( + @Args('farmAddress') farmAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.farmTransaction.claimBoostedRewards( + user.address, + farmAddress, + ); + } } diff --git a/src/modules/farm/v2/services/farm.v2.transaction.service.ts b/src/modules/farm/v2/services/farm.v2.transaction.service.ts index 7a879affd..f2b5fa145 100644 --- a/src/modules/farm/v2/services/farm.v2.transaction.service.ts +++ b/src/modules/farm/v2/services/farm.v2.transaction.service.ts @@ -132,6 +132,21 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { .toPlainObject(); } + async claimBoostedRewards( + sender: string, + farmAddress: string, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(farmAddress); + + return contract.methodsExplicit + .claimBoostedRewards() + .withSender(Address.fromString(sender)) + .withChainID(mxConfig.chainID) + .withGasLimit(gasConfig.farms[FarmVersion.V2].claimBoostedRewards) + .buildTransaction() + .toPlainObject(); + } + compoundRewards( sender: string, args: CompoundRewardsArgs, From 68cc9638fc6f205d2ceacacac69a504da870d64d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 18 Dec 2023 16:20:42 +0200 Subject: [PATCH 096/313] MEX-416: get only boosted rewards for boosted staking Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.service.ts | 65 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 16 +++++ 2 files changed, 81 insertions(+) diff --git a/src/modules/staking/services/staking.service.ts b/src/modules/staking/services/staking.service.ts index b1932b802..ac8649c37 100644 --- a/src/modules/staking/services/staking.service.ts +++ b/src/modules/staking/services/staking.service.ts @@ -26,6 +26,7 @@ import { import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { constantsConfig } from 'src/config'; +import { BoostedRewardsModel } from 'src/modules/farm/models/farm.model'; @Injectable() export class StakingService { @@ -203,6 +204,70 @@ export class StakingService { }); } + async getStakingBoostedRewardsBatch( + stakingAddresses: string[], + userAddress: string, + ): Promise { + const promises = stakingAddresses.map(async (address) => { + return await this.getStakingBoostedRewards(address, userAddress); + }); + return Promise.all(promises); + } + + async getStakingBoostedRewards( + stakingAddress: string, + userAddress: string, + ): Promise { + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + stakingAddress, + ); + const modelsList = []; + let lastActiveWeekUser = + await this.weeklyRewardsSplittingAbi.lastActiveWeekForUser( + stakingAddress, + userAddress, + ); + if (lastActiveWeekUser === 0) { + lastActiveWeekUser = currentWeek; + } + const startWeek = Math.max( + currentWeek - constantsConfig.USER_MAX_CLAIM_WEEKS, + lastActiveWeekUser, + ); + + for (let week = startWeek; week <= currentWeek - 1; week++) { + if (week < 1) { + continue; + } + const model = new UserInfoByWeekModel({ + scAddress: stakingAddress, + userAddress: userAddress, + week: week, + }); + + modelsList.push(model); + } + + const currentClaimProgress = + await this.weeklyRewardsSplittingAbi.currentClaimProgress( + stakingAddress, + userAddress, + ); + + const userAccumulatedRewards = + await this.stakingCompute.userAccumulatedRewards( + stakingAddress, + userAddress, + currentWeek, + ); + + return new BoostedRewardsModel({ + boostedRewardsWeeklyInfo: modelsList, + claimProgress: currentClaimProgress, + accumulatedRewards: userAccumulatedRewards, + }); + } + private async getUnbondigRemaingEpochs( unlockEpoch: number, ): Promise { diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index c72c77554..1fc1c5115 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -32,6 +32,7 @@ import { constantsConfig } from 'src/config'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { StakeAddressValidationPipe } from './validators/stake.address.validator'; import { BoostedYieldsFactors } from '../farm/models/farm.v2.model'; +import { BoostedRewardsModel } from '../farm/models/farm.model'; @Resolver(() => StakingModel) export class StakingResolver { @@ -267,6 +268,21 @@ export class StakingResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => [BoostedRewardsModel], { + description: 'Returns staking boosted rewards for the user', + }) + async getStakingBoostedRewardsBatch( + @Args('stakingAddresses', { type: () => [String] }) + stakingAddresses: string[], + @AuthUser() user: UserAuthResult, + ): Promise { + return this.stakingService.getStakingBoostedRewardsBatch( + stakingAddresses, + user.address, + ); + } + @UseGuards(JwtOrNativeAuthGuard) @Query(() => OptimalCompoundModel) async getOptimalCompoundFrequency( From b9b59a1c171b9d39e1c26250b6a56612561cdaa7 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 18 Dec 2023 17:34:55 +0100 Subject: [PATCH 097/313] MEX-416: add config gas for claim boosted rewards Signed-off-by: Claudiu Lataretu --- src/config/default.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 4829bada6..c0a05ea2a 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -239,7 +239,8 @@ } }, "claimRewards": 17000000 - } + }, + "claimBoostedRewards": 20000000 }, "admin": { "end_produce_rewards": 200000000, From bb93a0678ad59765520362f4433ad161b00de213 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 18 Dec 2023 19:39:43 +0100 Subject: [PATCH 098/313] MEX-417: locked token pos creator abi and config Signed-off-by: Claudiu Lataretu --- src/abis/locked-token-pos-creator.abi.json | 233 ++++++++++++++++++ src/config/default.json | 5 +- .../mx.proxy.service.ts | 8 + 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 src/abis/locked-token-pos-creator.abi.json diff --git a/src/abis/locked-token-pos-creator.abi.json b/src/abis/locked-token-pos-creator.abi.json new file mode 100644 index 000000000..b131051a0 --- /dev/null +++ b/src/abis/locked-token-pos-creator.abi.json @@ -0,0 +1,233 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.71.0-nightly", + "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de", + "commitDate": "2023-05-25", + "channel": "Nightly", + "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)" + }, + "contractCrate": { + "name": "locked-token-pos-creator", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.45.1" + } + }, + "name": "LockedTokenPosCreatorContract", + "constructor": { + "inputs": [ + { + "name": "energy_factory_adddress", + "type": "Address" + }, + { + "name": "egld_wrapper_address", + "type": "Address" + }, + { + "name": "mex_wegld_lp_pair_address", + "type": "Address" + }, + { + "name": "mex_wegld_lp_farm_address", + "type": "Address" + }, + { + "name": "proxy_dex_address", + "type": "Address" + }, + { + "name": "router_address", + "type": "Address" + } + ], + "outputs": [] + }, + "endpoints": [ + { + "name": "upgrade", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "createEnergyPosition", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "lock_epochs", + "type": "u64" + }, + { + "name": "min_amount_out", + "type": "BigUint" + }, + { + "name": "swap_operations", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "setEnergyFactoryAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "sc_address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getEnergyFactoryAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "createPairPosFromSingleToken", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "lock_epochs", + "type": "u64" + }, + { + "name": "add_liq_first_token_min_amount", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount", + "type": "BigUint" + }, + { + "name": "swap_operations", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "createPairPosFromTwoTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "add_liq_first_token_min_amount", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "createFarmPosFromSingleToken", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "lock_epochs", + "type": "u64" + }, + { + "name": "add_liq_first_token_min_amount", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount", + "type": "BigUint" + }, + { + "name": "swap_operations", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "createFarmPosFromTwoTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "add_liq_first_token_min_amount", + "type": "BigUint" + }, + { + "name": "add_liq_second_token_min_amount", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "List" + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + } + } +} diff --git a/src/config/default.json b/src/config/default.json index 4829bada6..cf55e808c 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -44,6 +44,7 @@ "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq9ej9vcnr38l69rgkc735kgv0qlu2ptrsd8ssu9rwtu", "escrow": "erd1qqqqqqqqqqqqqpgqz0wkk0j6y4h0mcxfxsg023j4x5sfgrmz0n4s4swp7a", "positionCreator": "erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6", "composableTasks": "erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh" }, "tokenProviderUSD": "WEGLD-71e90a", @@ -496,7 +497,8 @@ "vote": 50000000 }, "positionCreator": { - "singleToken": 50000000 + "singleToken": 50000000, + "energyPosition": 20000000 }, "composableTasks": { "default": 30000000 @@ -543,6 +545,7 @@ "tokenSnapshot": "./src/abis/governance-v2-merkle-proof.abi.json" }, "positionCreator": "./src/abis/auto-pos-creator.abi.json", + "lockedTokenPositionCreator": "./src/abis/locked-token-pos-creator.abi.json", "composableTasks": "./src/abis/composable-tasks.abi.json" }, "cron": { diff --git a/src/services/multiversx-communication/mx.proxy.service.ts b/src/services/multiversx-communication/mx.proxy.service.ts index fec8b6039..b927d75aa 100644 --- a/src/services/multiversx-communication/mx.proxy.service.ts +++ b/src/services/multiversx-communication/mx.proxy.service.ts @@ -231,6 +231,14 @@ export class MXProxyService { ); } + async getLockedTokenPositionCreatorContract(): Promise { + return this.getSmartContract( + scAddress.lockedTokenPositionCreator, + abiConfig.lockedTokenPositionCreator, + 'LockedTokenPosCreatorContract', + ); + } + async getComposableTasksSmartContract(): Promise { return this.getSmartContract( scAddress.composableTasks, From f2fc7fcb5cc118f34043f2f01efb3971da6e4b64 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 18 Dec 2023 19:40:35 +0100 Subject: [PATCH 099/313] MEX-417: energy position creator transaction query Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 19 +++++++ .../services/position.creator.compute.ts | 38 ++++++++++++++ .../services/position.creator.transaction.ts | 51 +++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 152647d77..58d5d7301 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -167,4 +167,23 @@ export class PositionCreatorTransactionResolver { tolerance, ); } + + @Query(() => TransactionModel) + async createEnergyPosition( + @AuthUser() user: UserAuthResult, + @Args('payment') payment: InputTokenModel, + @Args('lockEpochs') lockEpochs: number, + @Args('tolerance') tolerance: number, + ): Promise { + return this.posCreatorTransaction.createEnergyPosition( + user.address, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + lockEpochs, + tolerance, + ); + } } diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 95168b5e1..f18cc10fb 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -3,6 +3,7 @@ import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { PerformanceProfiler } from '@multiversx/sdk-nestjs-monitoring'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; +import { constantsConfig } from 'src/config'; import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; @@ -16,6 +17,11 @@ export type PositionCreatorSingleTokenPairInput = { amount1Min: BigNumber; }; +export type PositionCreatorSingleTokenInput = { + swapRouteArgs: TypedValue[]; + amountOutMin: BigNumber; +}; + @Injectable() export class PositionCreatorComputeService { constructor( @@ -121,4 +127,36 @@ export class PositionCreatorComputeService { amount1Min, }; } + + async computeSingleTokenInput( + payment: EsdtTokenPayment, + tolerance: number, + ): Promise { + const swapRoute = await this.autoRouterService.swap({ + tokenInID: payment.tokenIdentifier, + amountIn: payment.amount, + tokenOutID: constantsConfig.MEX_TOKEN_ID, + tolerance, + }); + + const amountOutMin = new BigNumber(swapRoute.amountOut) + .multipliedBy(1 - tolerance) + .integerValue(); + + const swapRouteArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoute.tokenInID, + tokenOutID: swapRoute.tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: swapRoute.pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoute.intermediaryAmounts, + tokenRoute: swapRoute.tokenRoute, + }); + + return { + swapRouteArgs, + amountOutMin, + }; + } } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index b6623afc3..484eae730 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -3,6 +3,7 @@ import { AddressValue, BigUIntValue, TokenTransfer, + U64Value, } from '@multiversx/sdk-core/out'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { Injectable } from '@nestjs/common'; @@ -505,6 +506,56 @@ export class PositionCreatorTransactionService { .toPlainObject(); } + async createEnergyPosition( + sender: string, + payment: EsdtTokenPayment, + lockEpochs: number, + tolerance: number, + ): Promise { + const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( + false, + ); + + if ( + !uniqueTokensIDs.includes(payment.tokenIdentifier) && + payment.tokenIdentifier !== mxConfig.EGLDIdentifier + ) { + throw new Error('Invalid ESDT token payment'); + } + + const singleTokenInput = + await this.posCreatorCompute.computeSingleTokenInput( + payment, + tolerance, + ); + + const contract = + await this.mxProxy.getLockedTokenPositionCreatorContract(); + + const interaction = contract.methodsExplicit + .createEnergyPosition([ + new U64Value(new BigNumber(lockEpochs)), + new BigUIntValue(singleTokenInput.amountOutMin), + ...singleTokenInput.swapRouteArgs, + ]) + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID); + + if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { + interaction.withValue(new BigNumber(payment.amount)); + } else { + interaction.withSingleESDTTransfer( + TokenTransfer.fungibleFromBigInteger( + payment.tokenIdentifier, + new BigNumber(payment.amount), + ), + ); + } + + return interaction.buildTransaction().toPlainObject(); + } + private checkTokensPayments( payments: EsdtTokenPayment[], firstTokenID: string, From 7c4d9653d98c60149b03f85340fda891bd3dac46 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 19 Dec 2023 15:50:34 +0100 Subject: [PATCH 100/313] MEX-417: integrate locked token position creator - based on lockEpochs generate transactions for locked token position creator or position creator for pair position and farm position with single token - based on token payments generate transaction for locked token position creator or position creator for farm position with dual tokens Signed-off-by: Claudiu Lataretu --- .../position.creator.module.ts | 4 + .../position.creator.transaction.resolver.ts | 8 +- .../services/position.creator.transaction.ts | 193 ++++++++++++++---- 3 files changed, 168 insertions(+), 37 deletions(-) diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index 43698c39c..b68cecadb 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -12,6 +12,8 @@ import { StakingModule } from '../staking/staking.module'; import { TokenModule } from '../tokens/token.module'; import { PositionCreatorTransactionResolver } from './position.creator.transaction.resolver'; import { WrappingModule } from '../wrapping/wrap.module'; +import { ProxyFarmModule } from '../proxy/services/proxy-farm/proxy.farm.module'; +import { EnergyModule } from '../energy/energy.module'; @Module({ imports: [ @@ -23,6 +25,8 @@ import { WrappingModule } from '../wrapping/wrap.module'; StakingProxyModule, TokenModule, WrappingModule, + ProxyFarmModule, + EnergyModule, MXCommunicationModule, ], providers: [ diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 58d5d7301..2ef136495 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -21,6 +21,7 @@ export class PositionCreatorTransactionResolver { @Args('pairAddress') pairAddress: string, @Args('payment') payment: InputTokenModel, @Args('tolerance') tolerance: number, + @Args('lockEpochs', { nullable: true }) lockEpochs: number, ): Promise { return this.posCreatorTransaction.createLiquidityPositionSingleToken( user.address, @@ -31,6 +32,7 @@ export class PositionCreatorTransactionResolver { amount: payment.amount, }), tolerance, + lockEpochs, ); } @@ -41,6 +43,7 @@ export class PositionCreatorTransactionResolver { @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, + @Args('lockEpochs', { nullable: true }) lockEpochs: number, ): Promise { return this.posCreatorTransaction.createFarmPositionSingleToken( user.address, @@ -54,6 +57,7 @@ export class PositionCreatorTransactionResolver { }), ), tolerance, + lockEpochs, ); } @@ -103,14 +107,14 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => TransactionModel) + @Query(() => [TransactionModel]) async createFarmPositionDualTokens( @AuthUser() user: UserAuthResult, @Args('farmAddress') farmAddress: string, @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createFarmPositionDualTokens( user.address, farmAddress, diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 484eae730..e9351f6b0 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -2,13 +2,14 @@ import { Address, AddressValue, BigUIntValue, + Interaction, TokenTransfer, U64Value, } from '@multiversx/sdk-core/out'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; -import { gasConfig, mxConfig } from 'src/config'; +import { gasConfig, mxConfig, scAddress } from 'src/config'; import { TransactionModel } from 'src/models/transaction.model'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; @@ -22,6 +23,9 @@ import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; import { PairService } from 'src/modules/pair/services/pair.service'; import { TokenService } from 'src/modules/tokens/services/token.service'; import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.transactions.service'; +import { ProxyFarmAbiService } from 'src/modules/proxy/services/proxy-farm/proxy.farm.abi.service'; +import { EnergyAbiService } from 'src/modules/energy/services/energy.abi.service'; +import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; @Injectable() export class PositionCreatorTransactionService { @@ -35,7 +39,10 @@ export class PositionCreatorTransactionService { private readonly stakingAbi: StakingAbiService, private readonly stakingProxyAbi: StakingProxyAbiService, private readonly tokenService: TokenService, + private readonly wrapAbi: WrapAbiService, private readonly wrapTransaction: WrapTransactionsService, + private readonly proxyFarmAbi: ProxyFarmAbiService, + private readonly energyAbi: EnergyAbiService, private readonly mxProxy: MXProxyService, ) {} @@ -44,6 +51,7 @@ export class PositionCreatorTransactionService { pairAddress: string, payment: EsdtTokenPayment, tolerance: number, + lockEpochs?: number, ): Promise { const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( false, @@ -63,23 +71,41 @@ export class PositionCreatorTransactionService { tolerance, ); - const contract = await this.mxProxy.getPostitionCreatorContract(); + const contract = lockEpochs + ? await this.mxProxy.getLockedTokenPositionCreatorContract() + : await this.mxProxy.getPostitionCreatorContract(); + + let interaction: Interaction; + + if (lockEpochs) { + interaction = contract.methodsExplicit + .createPairPosFromSingleToken([ + new U64Value(new BigNumber(lockEpochs)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]) + .withGasLimit(gasConfig.positionCreator.singleToken); + } else { + interaction = contract.methodsExplicit + .createLpPosFromSingleToken([ + new AddressValue(Address.fromBech32(pairAddress)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]) + .withGasLimit(gasConfig.positionCreator.singleToken); + } - const interaction = contract.methodsExplicit - .createLpPosFromSingleToken([ - new AddressValue(Address.fromBech32(pairAddress)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, - ]) + interaction = interaction .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID); if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { - interaction.withValue(new BigNumber(payment.amount)); + interaction = interaction.withValue(new BigNumber(payment.amount)); } else { - interaction.withSingleESDTTransfer( + interaction = interaction.withSingleESDTTransfer( TokenTransfer.fungibleFromBigInteger( payment.tokenIdentifier, new BigNumber(payment.amount), @@ -95,6 +121,7 @@ export class PositionCreatorTransactionService { farmAddress: string, payments: EsdtTokenPayment[], tolerance: number, + lockEpochs?: number, ): Promise { const [pairAddress, farmTokenID, uniqueTokensIDs] = await Promise.all([ this.farmAbiV2.pairContractAddress(farmAddress), @@ -130,15 +157,26 @@ export class PositionCreatorTransactionService { tolerance, ); - const contract = await this.mxProxy.getPostitionCreatorContract(); + const contract = lockEpochs + ? await this.mxProxy.getLockedTokenPositionCreatorContract() + : await this.mxProxy.getPostitionCreatorContract(); + + const endpointArgs = lockEpochs + ? [ + new U64Value(new BigNumber(lockEpochs)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ] + : [ + new AddressValue(Address.fromBech32(farmAddress)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]; const transaction = contract.methodsExplicit - .createFarmPosFromSingleToken([ - new AddressValue(Address.fromBech32(farmAddress)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, - ]) + .createFarmPosFromSingleToken(endpointArgs) .withMultiESDTNFTTransfer( payments.map((payment) => TokenTransfer.metaEsdtFromBigInteger( @@ -318,24 +356,40 @@ export class PositionCreatorTransactionService { farmAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + ): Promise { const pairAddress = await this.farmAbiV2.pairContractAddress( farmAddress, ); - const [firstTokenID, secondTokenID, farmTokenID] = await Promise.all([ + const [ + firstTokenID, + secondTokenID, + farmTokenID, + wrappedFarmTokenID, + xmexTokenID, + ] = await Promise.all([ this.pairAbi.firstTokenID(pairAddress), this.pairAbi.secondTokenID(pairAddress), this.farmAbiV2.farmTokenID(farmAddress), + this.proxyFarmAbi.wrappedFarmTokenID(scAddress.proxyDexAddress.v2), + this.energyAbi.lockedTokenID(), ]); - if (!this.checkTokensPayments(payments, firstTokenID, secondTokenID)) { - throw new Error('Invalid ESDT tokens payments'); + const transactions = []; + + if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { + payments[0].tokenIdentifier = + await this.wrapAbi.wrappedEgldTokenID(); + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); } - for (const payment of payments.slice(2)) { - if (payment.tokenIdentifier !== farmTokenID) { - throw new Error('Invalid farm token payment'); - } + if (payments[1].tokenIdentifier === mxConfig.EGLDIdentifier) { + payments[1].tokenIdentifier = + await this.wrapAbi.wrappedEgldTokenID(); + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[1].amount), + ); } const [firstPayment, secondPayment] = @@ -343,6 +397,46 @@ export class PositionCreatorTransactionService { ? [payments[0], payments[1]] : [payments[1], payments[0]]; + const isLockedToken = + firstPayment.tokenIdentifier === xmexTokenID || + secondPayment.tokenIdentifier === xmexTokenID; + + if ( + isLockedToken && + !this.checkLockedTokenPayments( + [firstPayment, secondPayment], + firstTokenID, + xmexTokenID, + ) + ) { + throw new Error('Invalid Locked tokens payments'); + } + + if ( + !isLockedToken && + !this.checkTokensPayments( + [firstPayment, secondPayment], + firstTokenID, + secondTokenID, + ) + ) { + throw new Error('Invalid ESDT tokens payments'); + } + + if (!isLockedToken) { + for (const payment of payments.slice(2)) { + if (payment.tokenIdentifier !== farmTokenID) { + throw new Error('Invalid farm token payment'); + } + } + } else { + for (const payment of payments.slice(2)) { + if (payment.tokenIdentifier !== wrappedFarmTokenID) { + throw new Error('Invalid wrapped farm token payment'); + } + } + } + const amount0Min = new BigNumber(firstPayment.amount) .multipliedBy(1 - tolerance) .integerValue(); @@ -350,22 +444,28 @@ export class PositionCreatorTransactionService { .multipliedBy(1 - tolerance) .integerValue(); - const contract = await this.mxProxy.getPostitionCreatorContract(); + const endpointArgs = isLockedToken + ? [new BigUIntValue(amount0Min), new BigUIntValue(amount1Min)] + : [ + new AddressValue(Address.fromBech32(farmAddress)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + ]; - return contract.methodsExplicit - .createFarmPosFromTwoTokens([ - new AddressValue(Address.fromBech32(farmAddress)), - new BigUIntValue(amount0Min), - new BigUIntValue(amount1Min), - ]) + const contract = isLockedToken + ? await this.mxProxy.getLockedTokenPositionCreatorContract() + : await this.mxProxy.getPostitionCreatorContract(); + + const transaction = contract.methodsExplicit + .createFarmPosFromTwoTokens(endpointArgs) .withMultiESDTNFTTransfer([ TokenTransfer.fungibleFromBigInteger( firstPayment.tokenIdentifier, new BigNumber(firstPayment.amount), ), - TokenTransfer.fungibleFromBigInteger( + TokenTransfer.metaEsdtFromBigInteger( secondPayment.tokenIdentifier, - + secondPayment.tokenNonce, new BigNumber(secondPayment.amount), ), ...payments @@ -383,6 +483,10 @@ export class PositionCreatorTransactionService { .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); + + transactions.push(transaction); + + return transactions; } async createDualFarmPositionDualTokens( @@ -568,4 +672,23 @@ export class PositionCreatorTransactionService { payments[0].tokenIdentifier === secondTokenID) ); } + + private checkLockedTokenPayments( + payments: EsdtTokenPayment[], + firstTokenID: string, + lockedTokenID: string, + ): boolean { + if (payments[0].tokenNonce > 0 || payments[1].tokenNonce < 1) { + return false; + } + + if ( + payments[0].tokenIdentifier !== firstTokenID || + payments[1].tokenIdentifier !== lockedTokenID + ) { + return false; + } + + return true; + } } From b0e5a5df6c477557a72f521f7cfdb8443a4aade0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 20 Dec 2023 12:09:19 +0100 Subject: [PATCH 101/313] MEX-417: add unit tests for energy farm position creator dual tokens Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 2 +- .../position.creator.transaction.spec.ts | 362 ++++++++++++++++-- 2 files changed, 324 insertions(+), 40 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index e9351f6b0..2275ccd04 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -409,7 +409,7 @@ export class PositionCreatorTransactionService { xmexTokenID, ) ) { - throw new Error('Invalid Locked tokens payments'); + throw new Error('Invalid locked tokens payments'); } if ( diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 7af292c7a..b6f89dab6 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -31,6 +31,8 @@ import { encodeTransactionData } from 'src/helpers/helpers'; import exp from 'constants'; import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; +import { ProxyFarmAbiServiceProvider } from 'src/modules/proxy/mocks/proxy.abi.service.mock'; +import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -65,6 +67,8 @@ describe('PositionCreatorTransaction', () => { TokenServiceProvider, RemoteConfigGetterServiceProvider, ComposableTasksTransactionService, + ProxyFarmAbiServiceProvider, + EnergyAbiServiceProvider, MXProxyServiceProvider, ConfigService, ApiConfigService, @@ -625,7 +629,7 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionDualTokens( + const transactions = await service.createFarmPositionDualTokens( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -645,32 +649,34 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); it('should return transaction no merge farm tokens', async () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionDualTokens( + const transactions = await service.createFarmPositionDualTokens( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -695,25 +701,303 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: 50000000, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + + it('should return transactions with egld wrap', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transactions = await service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transactions).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 4200000, + data: encodeTransactionData('wrapEgld'), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + }); + + describe('Create farm position dual token with locked token', () => { + it('should return error on invalid locked token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + + expect( + service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'ELKMEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + ).rejects.toThrowError('Invalid locked tokens payments'); + + expect( + service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'ELKMEX-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid locked tokens payments'); + + expect( + service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'ELKMEX-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid locked tokens payments'); + }); + + it('should return error on wrapped farm token', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + + expect( + service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'ELKMEX-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'LKFARM-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ), + ).rejects.toThrowError('Invalid wrapped farm token payment'); + }); + + it('should return transaction without consolidate', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transactions = await service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'ELKMEX-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + + it('should return transaction with consolidate', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transactions = await service.createFarmPositionDualTokens( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'ELKMEX-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'LKFARM-123456', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@LKFARM-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); From fa1250a275057857e88861a7878c29cda48b79a1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 20 Dec 2023 12:22:15 +0100 Subject: [PATCH 102/313] MEX-417: add unit tests for energy position creator transactions Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index b6f89dab6..c9931c034 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -1244,4 +1244,101 @@ describe('PositionCreatorTransaction', () => { }); }); }); + + describe('Energy position creator', () => { + it('should return error on invalid ESDT payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + + expect( + service.createEnergyPosition( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-abcdef', + tokenNonce: 0, + amount: '1000000000000000000', + }), + 1440, + 0.01, + ), + ).rejects.toThrowError('Invalid ESDT token payment'); + }); + + it('should return transaction with ESDT payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + + const transaction = await service.createEnergyPosition( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '1000000000000000000', + }), + 1440, + 0.01, + ); + + expect(transaction).toEqual({ + chainID: 'T', + data: encodeTransactionData( + 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', + ), + gasLimit: 50000000, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + receiverUsername: undefined, + sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + senderUsername: undefined, + signature: undefined, + value: '0', + version: 1, + }); + }); + + it('should return transaction with EGLD payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + + const transaction = await service.createEnergyPosition( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '1000000000000000000', + }), + 1440, + 0.01, + ); + + expect(transaction).toEqual({ + chainID: 'T', + data: encodeTransactionData( + 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', + ), + gasLimit: 50000000, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + receiverUsername: undefined, + sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + senderUsername: undefined, + signature: undefined, + value: '1000000000000000000', + version: 1, + }); + }); + }); }); From 77301973ca2e167456d3fa3b2dbbe3f048d133d2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 21 Dec 2023 18:14:36 +0200 Subject: [PATCH 103/313] MEX-417: Update farm position single token to use EGLD payment Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 2275ccd04..1ca60f0d0 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -175,9 +175,18 @@ export class PositionCreatorTransactionService { ...singleTokenPairInput.swapRouteArgs, ]; - const transaction = contract.methodsExplicit + let interaction = contract.methodsExplicit .createFarmPosFromSingleToken(endpointArgs) - .withMultiESDTNFTTransfer( + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID); + + if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { + interaction = interaction.withValue( + new BigNumber(payments[0].amount), + ); + } else { + interaction = interaction.withMultiESDTNFTTransfer( payments.map((payment) => TokenTransfer.metaEsdtFromBigInteger( payment.tokenIdentifier, @@ -185,14 +194,10 @@ export class PositionCreatorTransactionService { new BigNumber(payment.amount), ), ), - ) - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); + ); + } - transactions.push(transaction); + transactions.push(interaction.buildTransaction().toPlainObject()); return transactions; } From a7444d646d357989afc88b68f8b7434b4c1309be Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 21 Dec 2023 18:15:17 +0200 Subject: [PATCH 104/313] MEX-417: add unit tests for energy position creator single token Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index c9931c034..7acd213f0 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -143,6 +143,90 @@ describe('PositionCreatorTransaction', () => { }); }); + describe('Create liquidity position locked token', () => { + it('should return transaction with ESDT payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = + await service.createLiquidityPositionSingleToken( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + 1440, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + + it('should return transaction with EGLD payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = + await service.createLiquidityPositionSingleToken( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + 1440, + ); + + expect(transaction).toEqual({ + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `createPairPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }); + }); + }); + describe('Create farm position single token', () => { it('should return error on ESDT token', async () => { const service = module.get( @@ -283,6 +367,95 @@ describe('PositionCreatorTransaction', () => { }); }); + describe('Create farm position single token with locked token', () => { + it('should return transaction with ESDT payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionSingleToken( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + 1440, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + + it('should return transaction with EGLD payment', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionSingleToken( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + 1440, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `createFarmPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + }); + describe('Create dual farm position single token', () => { it('should return error on ESDT token', async () => { const service = module.get( From 2e001af0589f063eeacc707b0f285628712ed640 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Jan 2024 16:35:20 +0200 Subject: [PATCH 105/313] MEX-417: fix interaction assignment in if statement Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 1ca60f0d0..19f906d57 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -641,7 +641,7 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getLockedTokenPositionCreatorContract(); - const interaction = contract.methodsExplicit + let interaction = contract.methodsExplicit .createEnergyPosition([ new U64Value(new BigNumber(lockEpochs)), new BigUIntValue(singleTokenInput.amountOutMin), @@ -652,9 +652,9 @@ export class PositionCreatorTransactionService { .withChainID(mxConfig.chainID); if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { - interaction.withValue(new BigNumber(payment.amount)); + interaction = interaction.withValue(new BigNumber(payment.amount)); } else { - interaction.withSingleESDTTransfer( + interaction = interaction.withSingleESDTTransfer( TokenTransfer.fungibleFromBigInteger( payment.tokenIdentifier, new BigNumber(payment.amount), From deaa4ef1d1560f6c3525b8539b8d73b719eb2fc7 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Jan 2024 19:04:29 +0200 Subject: [PATCH 106/313] MEX-416: add missing field for staking BoostedRewardsModel Signed-off-by: Claudiu Lataretu --- src/modules/staking/services/staking.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/staking/services/staking.service.ts b/src/modules/staking/services/staking.service.ts index ac8649c37..ffc49f1c1 100644 --- a/src/modules/staking/services/staking.service.ts +++ b/src/modules/staking/services/staking.service.ts @@ -262,6 +262,7 @@ export class StakingService { ); return new BoostedRewardsModel({ + farmAddress: stakingAddress, boostedRewardsWeeklyInfo: modelsList, claimProgress: currentClaimProgress, accumulatedRewards: userAccumulatedRewards, From e5a5526771a5437343b565b674805f6e2f336a73 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 9 Jan 2024 18:26:11 +0200 Subject: [PATCH 107/313] MEX-418: add claim boosted rewards transaction Signed-off-by: Claudiu Lataretu --- src/abis/farm-staking.abi.json | 28 +++++++++++++++---- .../services/staking.transactions.service.ts | 16 +++++++++++ .../staking.transactions.service.spec.ts | 27 ++++++++++++++++++ src/modules/staking/staking.resolver.ts | 12 ++++++++ 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/abis/farm-staking.abi.json b/src/abis/farm-staking.abi.json index 95aeb73a3..90ede4779 100644 --- a/src/abis/farm-staking.abi.json +++ b/src/abis/farm-staking.abi.json @@ -1,20 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.73.0-nightly", - "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", - "commitDate": "2023-07-15", + "version": "1.76.0-nightly", + "commitHash": "d86d65bbc19b928387f68427fcc3a0da498d8a19", + "commitDate": "2023-12-10", "channel": "Nightly", - "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" + "short": "rustc 1.76.0-nightly (d86d65bbc 2023-12-10)" }, "contractCrate": { "name": "farm-staking", "version": "0.0.0", - "gitVersion": "v1.6.0-1527-g9534cc41" + "gitVersion": "v1.6.0-1579-ge4b95afa" }, "framework": { "name": "multiversx-sc", - "version": "0.43.3" + "version": "0.45.2" } }, "name": "FarmStaking", @@ -215,6 +215,21 @@ ], "outputs": [] }, + { + "name": "getAllowExternalClaimRewards", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, { "name": "getFarmingTokenId", "mutability": "readonly", @@ -1143,6 +1158,7 @@ ] } ], + "esdtAttributes": [], "hasCallback": true, "types": { "BoostedYieldsFactors": { diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index 3bac4fdb8..a7e5538fa 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -182,6 +182,22 @@ export class StakingTransactionService { .toPlainObject(); } + async claimBoostedRewards( + sender: string, + stakeAddress: string, + ): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + stakeAddress, + ); + return contract.methodsExplicit + .claimBoostedRewards() + .withSender(Address.fromString(sender)) + .withGasLimit(gasConfig.stake.claimBoostedRewards) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + async migrateTotalStakingPosition( stakingAddress: string, userAddress: string, diff --git a/src/modules/staking/specs/staking.transactions.service.spec.ts b/src/modules/staking/specs/staking.transactions.service.spec.ts index 321dcbd89..a7e575f2b 100644 --- a/src/modules/staking/specs/staking.transactions.service.spec.ts +++ b/src/modules/staking/specs/staking.transactions.service.spec.ts @@ -210,6 +210,33 @@ describe('StakingTransactionService', () => { }); }); + it('should get claim boosted rewards transaction', async () => { + const service = module.get( + StakingTransactionService, + ); + const transaction = await service.claimBoostedRewards( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: gasConfig.stake.claimBoostedRewards, + data: encodeTransactionData('claimBoostedRewards'), + chainID: 'T', + version: 1, + options: undefined, + signature: undefined, + guardian: undefined, + guardianSignature: undefined, + }); + }); + it('should get total staking migrate transaction', async () => { const service = module.get( StakingTransactionService, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 1fc1c5115..b49a48256 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -559,6 +559,18 @@ export class StakingResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => TransactionModel) + async claimStakingBoostedRewards( + @Args('stakeAddress') stakeAddress: string, + @AuthUser() user: UserAuthResult, + ): Promise { + return this.stakingTransactionService.claimBoostedRewards( + user.address, + stakeAddress, + ); + } + @UseGuards(JwtOrNativeAdminGuard) @Query(() => TransactionModel) async topUpRewards( From d08119f97e6b2ccc03edd6a45f4b66b22b5724cc Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 10 Jan 2024 11:42:00 +0200 Subject: [PATCH 108/313] MEX-418: add gas limit for claim staking boosted rewards Signed-off-by: Claudiu Lataretu --- src/config/default.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/default.json b/src/config/default.json index 57c335650..93e2887cd 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -392,6 +392,7 @@ "claimRewards": 17000000, "claimRewardsWithNewValue": 17000000, "compoundRewards": 17000000, + "claimBoostedRewards": 20000000, "mergeTokens": 20000000, "admin": { "setState": 20000000, From ebfea31edac2b13a29d87c4985d3039a1c1f65c7 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 11 Jan 2024 15:28:07 +0200 Subject: [PATCH 109/313] testnet: added new testnet addresses Signed-off-by: Claudiu Lataretu --- src/config/testnet.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config/testnet.json b/src/config/testnet.json index 1bece1f52..e26337986 100644 --- a/src/config/testnet.json +++ b/src/config/testnet.json @@ -27,7 +27,10 @@ "energyUpdate": "erd1qqqqqqqqqqqqqpgq37e5r67hvtrkyhs6yadwvwtk3rxk792e0n4s066pa5", "tokenUnstake": "erd1qqqqqqqqqqqqqpgqyxus98ytge6yt2ptesjxa5dzjfuz60dgexks3zee0k", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqs9c75ev7kzttp33gq0868qcwynv2ajgt0n4s9k628r", - "escrow": "erd1qqqqqqqqqqqqqpgqcsmrv0hh6fvmhe4e7q4resj7rkeqxp7p0n4s0qfapx" + "escrow": "erd1qqqqqqqqqqqqqpgqcsmrv0hh6fvmhe4e7q4resj7rkeqxp7p0n4s0qfapx", + "positionCreator": "erd1qqqqqqqqqqqqqpgqa7hv0nahgsl8tz0psat46x0tchm0wuyc0n4s6q28ad", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgq3q0rzdp8zlu8nmkr4cs6nhx78x3p080u0n4s34yj8g", + "composableTasks": "erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh" }, "governance": { "oldEnergy": {}, From 910f657f867b1f3c4d3110390dda56a9e1588903 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 12 Jan 2024 18:50:19 +0200 Subject: [PATCH 110/313] testnet: update gas limits Signed-off-by: Claudiu Lataretu --- src/config/default.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 93e2887cd..ad7b5d4ad 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -216,8 +216,8 @@ }, "v2": { "enterFarm": { - "default": 13500000, - "withTokenMerge": 15000000 + "default": 15000000, + "withTokenMerge": 17500000 }, "unlockedRewards": { "exitFarm": { @@ -365,7 +365,7 @@ "buybackAndBurn": 48000000 } }, - "claimRewards": 27500000 + "claimRewards": 28500000 }, "customRewards": { "exitFarm": { @@ -377,7 +377,8 @@ } }, "claimRewards": 20700000 - } + }, + "increaseEnergy": 20000000 }, "defaultMergeWFMT": 20000000 } @@ -389,7 +390,7 @@ }, "unstakeFarm": 20000000, "unbondFarm": 7500000, - "claimRewards": 17000000, + "claimRewards": 18000000, "claimRewardsWithNewValue": 17000000, "compoundRewards": 17000000, "claimBoostedRewards": 20000000, From 4036bdf197f835946ce4dbee84d9857a9fe4a59a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 12 Jan 2024 18:57:01 +0200 Subject: [PATCH 111/313] testnet: fix unit tests Signed-off-by: Claudiu Lataretu --- src/modules/staking/specs/staking.transactions.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/staking/specs/staking.transactions.service.spec.ts b/src/modules/staking/specs/staking.transactions.service.spec.ts index a7e575f2b..77228352c 100644 --- a/src/modules/staking/specs/staking.transactions.service.spec.ts +++ b/src/modules/staking/specs/staking.transactions.service.spec.ts @@ -273,7 +273,7 @@ describe('StakingTransactionService', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 17000000, + gasLimit: gasConfig.stake.claimRewards, data: encodeTransactionData( 'ESDTNFTTransfer@STAKETOK-1111@01@1000000000000000000@erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu@claimRewards', ), From 6f40415d22cfb11c9d6c3c08695622fc8f7bcaeb Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 18 Jan 2024 14:32:05 +0200 Subject: [PATCH 112/313] config: add missing gas configs Signed-off-by: Claudiu Lataretu --- src/config/default.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index bcb6401e4..a1fb54ec1 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -240,7 +240,8 @@ } }, "claimRewards": 30000000 - } + }, + "claimBoostedRewards": 20000000 }, "admin": { "end_produce_rewards": 200000000, @@ -390,6 +391,7 @@ "unstakeFarm": 9500000, "unbondFarm": 8000000, "claimRewards": 10000000, + "claimBoostedRewards": 20000000, "claimRewardsWithNewValue": 7500000, "compoundRewards": 10000000, "mergeTokens": 20000000, From b369c6286f596ade9c3d8591c9ee118ba7a48f71 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 18 Jan 2024 18:25:19 +0200 Subject: [PATCH 113/313] MEX-410: fix staking position creator single token with EGLD Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 19f906d57..8ae66b8da 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -325,8 +325,8 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoute.tokenRoute, }); - const transaction = contract.methodsExplicit - .createFarmStakingPosFromSingleToken([ + let interaction = + contract.methodsExplicit.createFarmStakingPosFromSingleToken([ new AddressValue(Address.fromBech32(stakingAddress)), new BigUIntValue( new BigNumber( @@ -336,8 +336,14 @@ export class PositionCreatorTransactionService { ), ), ...multiSwapArgs, - ]) - .withMultiESDTNFTTransfer( + ]); + + if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { + interaction = interaction.withValue( + new BigNumber(payments[0].amount), + ); + } else { + interaction = interaction.withMultiESDTNFTTransfer( payments.map((payment) => TokenTransfer.metaEsdtFromBigInteger( payment.tokenIdentifier, @@ -345,7 +351,10 @@ export class PositionCreatorTransactionService { new BigNumber(payment.amount), ), ), - ) + ); + } + + const transaction = interaction .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID) From c03bbf7335558b3a8aab5829c74b8610466b3a4a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 19 Jan 2024 11:22:04 +0200 Subject: [PATCH 114/313] MEX-410: fix position creator for staking single token Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 149 +++++++++++------- 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 8ae66b8da..e54e06939 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -276,28 +276,20 @@ export class PositionCreatorTransactionService { payments: EsdtTokenPayment[], tolerance: number, ): Promise { - const [farmingTokenID, farmTokenID, uniqueTokensIDs] = - await Promise.all([ - this.stakingAbi.farmingTokenID(stakingAddress), - this.stakingAbi.farmTokenID(stakingAddress), - this.tokenService.getUniqueTokenIDs(false), - ]); + const [ + farmingTokenID, + farmTokenID, + uniqueTokensIDs, + wrappedEgldTokenID, + ] = await Promise.all([ + this.stakingAbi.farmingTokenID(stakingAddress), + this.stakingAbi.farmTokenID(stakingAddress), + this.tokenService.getUniqueTokenIDs(false), + this.wrapAbi.wrappedEgldTokenID(), + ]); const transactions = []; - - if ( - payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && - payments.length > 1 - ) { - transactions.push( - await this.wrapTransaction.wrapEgld(sender, payments[0].amount), - ); - } else if ( - !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && - payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier - ) { - throw new Error('Invalid ESDT token payment'); - } + const contract = await this.mxProxy.getPostitionCreatorContract(); for (const payment of payments.slice(1)) { if (payment.tokenIdentifier !== farmTokenID) { @@ -305,6 +297,60 @@ export class PositionCreatorTransactionService { } } + if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { + if (payments.length > 1) { + payments[0].tokenIdentifier = wrappedEgldTokenID; + transactions.push( + await this.wrapTransaction.wrapEgld( + sender, + payments[0].amount, + ), + ); + transactions.push( + contract.methodsExplicit + .createFarmStakingPosFromSingleToken([ + new AddressValue( + Address.fromBech32(stakingAddress), + ), + new BigUIntValue(new BigNumber(payments[0].amount)), + ]) + .withMultiESDTNFTTransfer( + payments.map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), + ), + ) + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(), + ); + return transactions; + } else { + return [ + contract.methodsExplicit + .createFarmStakingPosFromSingleToken([ + new AddressValue( + Address.fromBech32(stakingAddress), + ), + new BigUIntValue(new BigNumber(payments[0].amount)), + ]) + .withValue(new BigNumber(payments[0].amount)) + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(), + ]; + } + } else if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { + throw new Error('Invalid ESDT token payment'); + } + const swapRoute = await this.autoRouterService.swap({ tokenInID: payments[0].tokenIdentifier, amountIn: payments[0].amount, @@ -312,8 +358,6 @@ export class PositionCreatorTransactionService { tolerance, }); - const contract = await this.mxProxy.getPostitionCreatorContract(); - const multiSwapArgs = this.autoRouterTransaction.multiPairFixedInputSwaps({ tokenInID: swapRoute.tokenInID, @@ -325,43 +369,34 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoute.tokenRoute, }); - let interaction = - contract.methodsExplicit.createFarmStakingPosFromSingleToken([ - new AddressValue(Address.fromBech32(stakingAddress)), - new BigUIntValue( - new BigNumber( - swapRoute.intermediaryAmounts[ - swapRoute.intermediaryAmounts.length - 1 - ], + transactions.push( + contract.methodsExplicit + .createFarmStakingPosFromSingleToken([ + new AddressValue(Address.fromBech32(stakingAddress)), + new BigUIntValue( + new BigNumber( + swapRoute.intermediaryAmounts[ + swapRoute.intermediaryAmounts.length - 1 + ], + ), ), - ), - ...multiSwapArgs, - ]); - - if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { - interaction = interaction.withValue( - new BigNumber(payments[0].amount), - ); - } else { - interaction = interaction.withMultiESDTNFTTransfer( - payments.map((payment) => - TokenTransfer.metaEsdtFromBigInteger( - payment.tokenIdentifier, - payment.tokenNonce, - new BigNumber(payment.amount), + ...multiSwapArgs, + ]) + .withMultiESDTNFTTransfer( + payments.map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), + ), ), - ), - ); - } - - const transaction = interaction - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); - - transactions.push(transaction); + ) + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(), + ); return transactions; } From 9e75d3aa0bffc2c862a9ee393029b29a27d7d324 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 19 Jan 2024 13:37:16 +0200 Subject: [PATCH 115/313] MEX-410: fix position creator single token with EGLD value Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 238 ++++++------ .../position.creator.transaction.spec.ts | 345 +++++++++++++++++- 2 files changed, 465 insertions(+), 118 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index e54e06939..78698fca6 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -123,21 +123,15 @@ export class PositionCreatorTransactionService { tolerance: number, lockEpochs?: number, ): Promise { - const [pairAddress, farmTokenID, uniqueTokensIDs] = await Promise.all([ - this.farmAbiV2.pairContractAddress(farmAddress), - this.farmAbiV2.farmTokenID(farmAddress), - this.tokenService.getUniqueTokenIDs(false), - ]); + const [pairAddress, farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = + await Promise.all([ + this.farmAbiV2.pairContractAddress(farmAddress), + this.farmAbiV2.farmTokenID(farmAddress), + this.tokenService.getUniqueTokenIDs(false), + this.wrapAbi.wrappedEgldTokenID(), + ]); - const transactions = []; if ( - payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && - payments.length > 1 - ) { - transactions.push( - await this.wrapTransaction.wrapEgld(sender, payments[0].amount), - ); - } else if ( !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier ) { @@ -150,6 +144,16 @@ export class PositionCreatorTransactionService { } } + const transactions = []; + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length > 1 + ) { + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); + } + const singleTokenPairInput = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, @@ -181,11 +185,18 @@ export class PositionCreatorTransactionService { .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID); - if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length === 1 + ) { interaction = interaction.withValue( new BigNumber(payments[0].amount), ); } else { + payments[0].tokenIdentifier = + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier + ? wrappedEgldTokenID + : payments[0].tokenIdentifier; interaction = interaction.withMultiESDTNFTTransfer( payments.map((payment) => TokenTransfer.metaEsdtFromBigInteger( @@ -207,22 +218,19 @@ export class PositionCreatorTransactionService { payments: EsdtTokenPayment[], tolerance: number, ): Promise { - const [pairAddress, dualYieldTokenID, uniqueTokensIDs] = - await Promise.all([ - this.stakingProxyAbi.pairAddress(stakingProxyAddress), - this.stakingProxyAbi.dualYieldTokenID(stakingProxyAddress), - this.tokenService.getUniqueTokenIDs(false), - ]); + const [ + pairAddress, + dualYieldTokenID, + uniqueTokensIDs, + wrappedEgldTokenID, + ] = await Promise.all([ + this.stakingProxyAbi.pairAddress(stakingProxyAddress), + this.stakingProxyAbi.dualYieldTokenID(stakingProxyAddress), + this.tokenService.getUniqueTokenIDs(false), + this.wrapAbi.wrappedEgldTokenID(), + ]); - const transactions = []; if ( - payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && - payments.length > 1 - ) { - transactions.push( - await this.wrapTransaction.wrapEgld(sender, payments[0].amount), - ); - } else if ( !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier ) { @@ -235,6 +243,16 @@ export class PositionCreatorTransactionService { } } + const transactions = []; + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length > 1 + ) { + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); + } + const singleTokenPairInput = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, @@ -244,14 +262,31 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); - const transaction = contract.methodsExplicit + let interaction = contract.methodsExplicit .createMetastakingPosFromSingleToken([ new AddressValue(Address.fromBech32(stakingProxyAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), ...singleTokenPairInput.swapRouteArgs, ]) - .withMultiESDTNFTTransfer( + + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID); + + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length === 1 + ) { + interaction = interaction.withValue( + new BigNumber(payments[0].amount), + ); + } else { + payments[0].tokenIdentifier = + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier + ? wrappedEgldTokenID + : payments[0].tokenIdentifier; + interaction = interaction.withMultiESDTNFTTransfer( payments.map((payment) => TokenTransfer.metaEsdtFromBigInteger( payment.tokenIdentifier, @@ -259,14 +294,10 @@ export class PositionCreatorTransactionService { new BigNumber(payment.amount), ), ), - ) - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(); + ); + } - transactions.push(transaction); + transactions.push(interaction.buildTransaction().toPlainObject()); return transactions; } @@ -288,8 +319,12 @@ export class PositionCreatorTransactionService { this.wrapAbi.wrappedEgldTokenID(), ]); - const transactions = []; - const contract = await this.mxProxy.getPostitionCreatorContract(); + if ( + !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && + payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier + ) { + throw new Error('Invalid ESDT token payment'); + } for (const payment of payments.slice(1)) { if (payment.tokenIdentifier !== farmTokenID) { @@ -297,58 +332,14 @@ export class PositionCreatorTransactionService { } } - if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { - if (payments.length > 1) { - payments[0].tokenIdentifier = wrappedEgldTokenID; - transactions.push( - await this.wrapTransaction.wrapEgld( - sender, - payments[0].amount, - ), - ); - transactions.push( - contract.methodsExplicit - .createFarmStakingPosFromSingleToken([ - new AddressValue( - Address.fromBech32(stakingAddress), - ), - new BigUIntValue(new BigNumber(payments[0].amount)), - ]) - .withMultiESDTNFTTransfer( - payments.map((payment) => - TokenTransfer.metaEsdtFromBigInteger( - payment.tokenIdentifier, - payment.tokenNonce, - new BigNumber(payment.amount), - ), - ), - ) - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - return transactions; - } else { - return [ - contract.methodsExplicit - .createFarmStakingPosFromSingleToken([ - new AddressValue( - Address.fromBech32(stakingAddress), - ), - new BigUIntValue(new BigNumber(payments[0].amount)), - ]) - .withValue(new BigNumber(payments[0].amount)) - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ]; - } - } else if (!uniqueTokensIDs.includes(payments[0].tokenIdentifier)) { - throw new Error('Invalid ESDT token payment'); + const transactions = []; + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length > 1 + ) { + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); } const swapRoute = await this.autoRouterService.swap({ @@ -369,34 +360,47 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoute.tokenRoute, }); - transactions.push( - contract.methodsExplicit - .createFarmStakingPosFromSingleToken([ - new AddressValue(Address.fromBech32(stakingAddress)), - new BigUIntValue( - new BigNumber( - swapRoute.intermediaryAmounts[ - swapRoute.intermediaryAmounts.length - 1 - ], - ), + const contract = await this.mxProxy.getPostitionCreatorContract(); + let interaction = contract.methodsExplicit + .createFarmStakingPosFromSingleToken([ + new AddressValue(Address.fromBech32(stakingAddress)), + new BigUIntValue( + new BigNumber( + swapRoute.intermediaryAmounts[ + swapRoute.intermediaryAmounts.length - 1 + ], ), - ...multiSwapArgs, - ]) - .withMultiESDTNFTTransfer( - payments.map((payment) => - TokenTransfer.metaEsdtFromBigInteger( - payment.tokenIdentifier, - payment.tokenNonce, - new BigNumber(payment.amount), - ), + ), + ...multiSwapArgs, + ]) + .withSender(Address.fromBech32(sender)) + .withGasLimit(gasConfig.positionCreator.singleToken) + .withChainID(mxConfig.chainID); + + if ( + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && + payments.length === 1 + ) { + interaction = interaction.withValue( + new BigNumber(payments[0].amount), + ); + } else { + payments[0].tokenIdentifier = + payments[0].tokenIdentifier === mxConfig.EGLDIdentifier + ? wrappedEgldTokenID + : payments[0].tokenIdentifier; + interaction = interaction.withMultiESDTNFTTransfer( + payments.map((payment) => + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenIdentifier, + payment.tokenNonce, + new BigNumber(payment.amount), ), - ) - .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); + ), + ); + } + + transactions.push(interaction.buildTransaction().toPlainObject()); return transactions; } diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 7acd213f0..629e11d7f 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -28,11 +28,12 @@ import { RemoteConfigGetterServiceProvider } from 'src/modules/remote-config/moc import { Address } from '@multiversx/sdk-core/out'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { encodeTransactionData } from 'src/helpers/helpers'; -import exp from 'constants'; import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; import { ProxyFarmAbiServiceProvider } from 'src/modules/proxy/mocks/proxy.abi.service.mock'; import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; +import { scAddress } from 'src/config'; +import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -277,6 +278,49 @@ describe('PositionCreatorTransaction', () => { ).rejects.toThrowError('Invalid farm token payment'); }); + it('should return transaction with EGLD and no merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionSingleToken( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should return transaction no merge farm tokens', async () => { const service = module.get( PositionCreatorTransactionService, @@ -319,6 +363,71 @@ describe('PositionCreatorTransaction', () => { ]); }); + it('should return transaction with EGLD and merge farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const transaction = await service.createFarmPositionSingleToken( + Address.Zero().bech32(), + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000021', + ).bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXFL-abcdef', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 4200000, + data: encodeTransactionData('wrapEgld'), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should return transaction with merge farm tokens', async () => { const service = module.get( PositionCreatorTransactionService, @@ -502,6 +611,55 @@ describe('PositionCreatorTransaction', () => { ).rejects.toThrowError('Invalid dual yield token payment'); }); + it('should return transaction with EGLD no merge dual farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: scAddress.positionCreator, + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should return transaction no merge dual farm tokens', async () => { const service = module.get( PositionCreatorTransactionService, @@ -551,6 +709,78 @@ describe('PositionCreatorTransaction', () => { ]); }); + it('should return transaction with EGLD and merge dual farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-1234', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 4200000, + data: encodeTransactionData('wrapEgld'), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should return transaction with merge dual farm tokens', async () => { const service = module.get( PositionCreatorTransactionService, @@ -652,6 +882,51 @@ describe('PositionCreatorTransaction', () => { ).rejects.toThrowError('Invalid staking token payment'); }); + it('should return transaction with EGLD no merge staking tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingAbi = module.get(StakingAbiService); + jest.spyOn(stakingAbi, 'farmingTokenID').mockResolvedValue( + 'MEX-123456', + ); + + const transaction = await service.createStakingPositionSingleToken( + Address.Zero().bech32(), + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: scAddress.positionCreator, + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should return transaction no merge staking tokens', async () => { const service = module.get( PositionCreatorTransactionService, @@ -692,6 +967,74 @@ describe('PositionCreatorTransaction', () => { ]); }); + it('should return transaction with EGLD and merge staking tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingAbi = module.get(StakingAbiService); + jest.spyOn(stakingAbi, 'farmingTokenID').mockResolvedValue( + 'MEX-123456', + ); + + const transaction = await service.createStakingPositionSingleToken( + Address.Zero().bech32(), + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'STAKETOK-1111', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 4200000, + data: encodeTransactionData('wrapEgld'), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + it('should return transaction with merge staking tokens', async () => { const service = module.get( PositionCreatorTransactionService, From 6099d15b384b54d189304e20d7cc2659a1d39829 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 19 Jan 2024 16:02:44 +0200 Subject: [PATCH 116/313] MEX-372: return object with total boosted position for user Signed-off-by: Claudiu Lataretu --- src/modules/farm/models/farm.model.ts | 12 ++++++ src/modules/farm/v2/farm.v2.resolver.ts | 41 ++++++++++++++----- src/modules/staking/staking.resolver.ts | 35 ++++++++++++---- .../validators/stake.address.validator.ts | 20 +++++---- 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/modules/farm/models/farm.model.ts b/src/modules/farm/models/farm.model.ts index 81ca0cea1..84b23d351 100644 --- a/src/modules/farm/models/farm.model.ts +++ b/src/modules/farm/models/farm.model.ts @@ -59,6 +59,18 @@ export class BoostedRewardsModel { } } +@ObjectType() +export class UserTotalBoostedPosition { + @Field() + address: string; + @Field() + boostedTokensAmount: string; + + constructor(init?: Partial) { + Object.assign(this, init); + } +} + @ObjectType() export class ExitFarmTokensModel { @Field() diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 8445ebaf2..1af0773ff 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -14,7 +14,11 @@ import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard' import { UserAuthResult } from 'src/modules/auth/user.auth.result'; import { AuthUser } from 'src/modules/auth/auth.user'; import { farmVersion } from 'src/utils/farm.utils'; -import { BoostedRewardsModel, FarmVersion } from '../models/farm.model'; +import { + BoostedRewardsModel, + FarmVersion, + UserTotalBoostedPosition, +} from '../models/farm.model'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; @@ -151,21 +155,36 @@ export class FarmResolverV2 extends FarmResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => String, { + @Query(() => [UserTotalBoostedPosition], { description: 'Returns the total farm position of the user in the farm', }) async userTotalFarmPosition( - @Args('farmAddress') farmAddress: string, + @Args('farmsAddresses', { type: () => [String] }) + farmsAddresses: string[], @AuthUser() user: UserAuthResult, - ): Promise { - if (farmVersion(farmAddress) !== FarmVersion.V2) { - throw new GraphQLError('Farm version is not supported', { - extensions: { - code: ApolloServerErrorCode.BAD_USER_INPUT, - }, + ): Promise { + farmsAddresses.forEach((farmAddress) => { + if (farmVersion(farmAddress) !== FarmVersion.V2) { + throw new GraphQLError('Farm version is not supported', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } + }); + + const positions = await Promise.all( + farmsAddresses.map((farmAddress) => + this.farmAbi.userTotalFarmPosition(farmAddress, user.address), + ), + ); + + return farmsAddresses.map((farmAddress, index) => { + return new UserTotalBoostedPosition({ + address: farmAddress, + boostedTokensAmount: positions[index], }); - } - return this.farmAbi.userTotalFarmPosition(farmAddress, user.address); + }); } @UseGuards(JwtOrNativeAuthGuard) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index b49a48256..577276fef 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -32,7 +32,10 @@ import { constantsConfig } from 'src/config'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { StakeAddressValidationPipe } from './validators/stake.address.validator'; import { BoostedYieldsFactors } from '../farm/models/farm.v2.model'; -import { BoostedRewardsModel } from '../farm/models/farm.model'; +import { + BoostedRewardsModel, + UserTotalBoostedPosition, +} from '../farm/models/farm.model'; @Resolver(() => StakingModel) export class StakingResolver { @@ -298,18 +301,34 @@ export class StakingResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => String, { + @Query(() => [UserTotalBoostedPosition], { description: 'Returns the total staked position of the user in the staking contract', }) async userTotalStakePosition( - @Args('stakeAddress', StakeAddressValidationPipe) stakeAddress: string, + @Args( + 'stakeAddresses', + { type: () => [String] }, + StakeAddressValidationPipe, + ) + stakeAddresses: string[], @AuthUser() user: UserAuthResult, - ): Promise { - return this.stakingAbi.userTotalStakePosition( - stakeAddress, - user.address, - ); + ): Promise { + const positions = await Promise.all( + stakeAddresses.map((stakeAddress) => + this.stakingAbi.userTotalStakePosition( + stakeAddress, + user.address, + ), + ), + ); + + return stakeAddresses.map((stakeAddress, index) => { + return new UserTotalBoostedPosition({ + address: stakeAddress, + boostedTokensAmount: positions[index], + }); + }); } @Query(() => [StakingModel]) diff --git a/src/modules/staking/validators/stake.address.validator.ts b/src/modules/staking/validators/stake.address.validator.ts index 857c1c856..e94cfb01d 100644 --- a/src/modules/staking/validators/stake.address.validator.ts +++ b/src/modules/staking/validators/stake.address.validator.ts @@ -7,16 +7,20 @@ import { Address } from '@multiversx/sdk-core/out'; export class StakeAddressValidationPipe implements PipeTransform { constructor(private readonly remoteConfig: RemoteConfigGetterService) {} - async transform(value: string, metadata: ArgumentMetadata) { + async transform(value: string | string[], metadata: ArgumentMetadata) { let address: Address; - try { - address = Address.fromBech32(value); - } catch (error) { - throw new UserInputError('Invalid address'); - } + const values = Array.isArray(value) ? value : [value]; const stakingAddresses = await this.remoteConfig.getStakingAddresses(); - if (!stakingAddresses.includes(address.bech32())) { - throw new UserInputError('Invalid staking address'); + + for (const entry of values) { + try { + address = Address.fromBech32(entry); + } catch (error) { + throw new UserInputError('Invalid address'); + } + if (!stakingAddresses.includes(address.bech32())) { + throw new UserInputError('Invalid staking address'); + } } return value; From 388d07cee5a5a25b78522ec402123e87df4501e1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 22 Jan 2024 17:21:44 +0200 Subject: [PATCH 117/313] MEX-410: create dual yield position from lp token Signed-off-by: Claudiu Lataretu --- .../services/position.creator.compute.ts | 11 +++- .../services/position.creator.transaction.ts | 4 +- .../position.creator.transaction.spec.ts | 54 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index f18cc10fb..7c45afaf3 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -59,11 +59,20 @@ export class PositionCreatorComputeService { const acceptedPairedTokensIDs = await this.routerAbi.commonTokensForUserPairs(); - const [firstTokenID, secondTokenID] = await Promise.all([ + const [firstTokenID, secondTokenID, lpTokenID] = await Promise.all([ this.pairAbi.firstTokenID(pairAddress), this.pairAbi.secondTokenID(pairAddress), + this.pairAbi.lpTokenID(pairAddress), ]); + if (payment.tokenIdentifier === lpTokenID) { + return { + swapRouteArgs: [], + amount0Min: new BigNumber(0), + amount1Min: new BigNumber(0), + }; + } + const swapToTokenID = acceptedPairedTokensIDs.includes(firstTokenID) ? firstTokenID : secondTokenID; diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 78698fca6..1468dd46b 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -230,8 +230,11 @@ export class PositionCreatorTransactionService { this.wrapAbi.wrappedEgldTokenID(), ]); + const lpTokenID = await this.pairAbi.lpTokenID(pairAddress); + if ( !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && + payments[0].tokenIdentifier !== lpTokenID && payments[0].tokenIdentifier !== mxConfig.EGLDIdentifier ) { throw new Error('Invalid ESDT token payment'); @@ -269,7 +272,6 @@ export class PositionCreatorTransactionService { new BigUIntValue(singleTokenPairInput.amount1Min), ...singleTokenPairInput.swapRouteArgs, ]) - .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.singleToken) .withChainID(mxConfig.chainID); diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 629e11d7f..e6d7a1ebf 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -834,6 +834,60 @@ describe('PositionCreatorTransaction', () => { }, ]); }); + + it('should return transaction with LP token and merge dual farm tokens', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transaction = await service.createDualFarmPositionSingleToken( + Address.Zero().bech32(), + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLDMEXLP-abcdef', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-1234', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transaction).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 50000000, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@EGLDMEXLP-abcdef@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@@', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); }); describe('Create staking position single token', () => { From 58e4e4a46e57483db0a58fce0c620fea1ab99a1b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 29 Jan 2024 14:24:56 +0200 Subject: [PATCH 118/313] MEX-397: XMEXFARM increase energy query Signed-off-by: Claudiu Lataretu --- .../proxy/proxy.transaction.resolver.ts | 69 +++++++++++++++---- .../proxy.farm.transactions.service.ts | 28 ++++++++ 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/src/modules/proxy/proxy.transaction.resolver.ts b/src/modules/proxy/proxy.transaction.resolver.ts index eaf45a213..ef75de8de 100644 --- a/src/modules/proxy/proxy.transaction.resolver.ts +++ b/src/modules/proxy/proxy.transaction.resolver.ts @@ -216,6 +216,7 @@ export class ProxyTransactionResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) @Query(() => TransactionModel) async increaseProxyPairTokenEnergy( @Args('payment') payment: InputTokenModel, @@ -224,22 +225,38 @@ export class ProxyTransactionResolver { ): Promise { let proxyAddress: string; try { - proxyAddress = await this.proxyService.getProxyAddressByToken( - payment.tokenID, + proxyAddress = await this.validateProxyIncreaseEnergy( + payment, + lockEpochs, ); + } catch (error) { + throw new GraphQLError(error.message, { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } + return this.transactionsProxyPairService.increaseProxyPairTokenEnergy( + user.address, + proxyAddress, + payment, + lockEpochs, + ); + } - if (proxyAddress !== scAddress.proxyDexAddress.v2) { - throw new Error('Wrapped lp token is not supported'); - } - - const lockOptions = await this.energyAbi.lockOptions(); - if ( - lockOptions.find( - (option) => option.lockEpochs === lockEpochs, - ) === undefined - ) { - throw new Error('Invalid lock epochs!'); - } + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => TransactionModel) + async increaseProxyFarmTokenEnergy( + @Args('payment') payment: InputTokenModel, + @Args('lockEpochs') lockEpochs: number, + @AuthUser() user: UserAuthResult, + ): Promise { + let proxyAddress: string; + try { + proxyAddress = await this.validateProxyIncreaseEnergy( + payment, + lockEpochs, + ); } catch (error) { throw new GraphQLError(error.message, { extensions: { @@ -247,6 +264,7 @@ export class ProxyTransactionResolver { }, }); } + return this.transactionsProxyPairService.increaseProxyPairTokenEnergy( user.address, proxyAddress, @@ -254,4 +272,27 @@ export class ProxyTransactionResolver { lockEpochs, ); } + + private async validateProxyIncreaseEnergy( + payment: InputTokenModel, + lockEpochs: number, + ): Promise { + const proxyAddress = await this.proxyService.getProxyAddressByToken( + payment.tokenID, + ); + + if (proxyAddress !== scAddress.proxyDexAddress.v2) { + throw new Error('Wrapped lp token is not supported'); + } + + const lockOptions = await this.energyAbi.lockOptions(); + if ( + lockOptions.find((option) => option.lockEpochs === lockEpochs) === + undefined + ) { + throw new Error('Invalid lock epochs!'); + } + + return proxyAddress; + } } diff --git a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts index a69e78324..b4547e739 100644 --- a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts +++ b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts @@ -4,6 +4,7 @@ import { BigUIntValue, BytesValue, TypedValue, + U64Value, } from '@multiversx/sdk-core/out/smartcontracts/typesystem'; import { Address, Interaction, TokenTransfer } from '@multiversx/sdk-core'; import { TransactionModel } from '../../../../models/transaction.model'; @@ -330,6 +331,33 @@ export class ProxyFarmTransactionsService { return Promise.all(promises); } + async increaseProxyFarmTokenEnergy( + sender: string, + proxyAddress: string, + payment: InputTokenModel, + lockEpochs: number, + ): Promise { + const contract = await this.mxProxy.getProxyDexSmartContract( + proxyAddress, + ); + return contract.methodsExplicit + .increaseProxyFarmTokenEnergy([ + new U64Value(new BigNumber(lockEpochs)), + ]) + .withSingleESDTNFTTransfer( + TokenTransfer.metaEsdtFromBigInteger( + payment.tokenID, + payment.nonce, + new BigNumber(payment.amount), + ), + ) + .withSender(Address.fromString(sender)) + .withGasLimit(gasConfig.proxy.pairs.increaseEnergy) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); + } + private async getExitFarmProxyGasLimit( args: ExitFarmProxyArgs, ): Promise { From 86dc0ac9a31df07a027291f2d9829de1d22c6ac5 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 7 Feb 2024 14:50:15 +0200 Subject: [PATCH 119/313] merge: fixes after merge Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/services/farm.v2.transaction.service.ts | 4 +++- .../specs/position.creator.transaction.spec.ts | 2 ++ .../services/proxy-farm/proxy.farm.transactions.service.ts | 4 +++- src/modules/staking/services/staking.transactions.service.ts | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.transaction.service.ts b/src/modules/farm/v2/services/farm.v2.transaction.service.ts index f2b5fa145..d29bec774 100644 --- a/src/modules/farm/v2/services/farm.v2.transaction.service.ts +++ b/src/modules/farm/v2/services/farm.v2.transaction.service.ts @@ -18,6 +18,7 @@ import { FarmAbiServiceV2 } from './farm.v2.abi.service'; import { PairService } from 'src/modules/pair/services/pair.service'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { ContextGetterService } from 'src/services/context/context.getter.service'; @Injectable() export class FarmTransactionServiceV2 extends TransactionsFarmService { @@ -27,6 +28,7 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { protected readonly pairService: PairService, protected readonly pairAbi: PairAbiService, private readonly mxApi: MXApiService, + private readonly contextGetter: ContextGetterService, ) { super(mxProxy, farmAbi, pairService, pairAbi); } @@ -168,7 +170,7 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { return []; } - const userNfts = await this.mxApi.getNftsForUser( + const userNfts = await this.contextGetter.getNftsForUser( userAddress, 0, userNftsCount, diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index e6d7a1ebf..f4fb2a766 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -34,6 +34,7 @@ import { ProxyFarmAbiServiceProvider } from 'src/modules/proxy/mocks/proxy.abi.s import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; import { scAddress } from 'src/config'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; +import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -74,6 +75,7 @@ describe('PositionCreatorTransaction', () => { ConfigService, ApiConfigService, ContextGetterServiceProvider, + MXApiServiceProvider, ], }).compile(); }); diff --git a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts index b4547e739..54f3200d6 100644 --- a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts +++ b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts @@ -34,6 +34,7 @@ import { WrappedFarmTokenAttributesV2, } from '@multiversx/sdk-exchange'; import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; +import { ContextGetterService } from 'src/services/context/context.getter.service'; @Injectable() export class ProxyFarmTransactionsService { @@ -45,6 +46,7 @@ export class ProxyFarmTransactionsService { private readonly pairService: PairService, private readonly pairAbi: PairAbiService, private readonly proxyFarmAbi: ProxyFarmAbiService, + private readonly contextGetter: ContextGetterService, ) {} async enterFarmProxy( @@ -283,7 +285,7 @@ export class ProxyFarmTransactionsService { proxyAddress, ); const userNftsCount = await this.mxApi.getNftsCountForUser(sender); - const userNfts = await this.mxApi.getNftsForUser( + const userNfts = await this.contextGetter.getNftsForUser( sender, 0, userNftsCount, diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index a7e5538fa..dabb0e039 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -16,6 +16,7 @@ import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.s import { StakingAbiService } from './staking.abi.service'; import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { ContextGetterService } from 'src/services/context/context.getter.service'; @Injectable() export class StakingTransactionService { @@ -23,6 +24,7 @@ export class StakingTransactionService { private readonly stakingAbi: StakingAbiService, private readonly mxProxy: MXProxyService, private readonly mxApi: MXApiService, + private readonly contextGetter: ContextGetterService, ) {} @ErrorLoggerAsync() @@ -210,7 +212,7 @@ export class StakingTransactionService { ], ); - const userNfts = await this.mxApi.getNftsForUser( + const userNfts = await this.contextGetter.getNftsForUser( userAddress, 0, userNftsCount, From 8ae63d705a4dcdbd5c0d9c05e98afcf718c9fa5d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 15 Feb 2024 15:05:31 +0200 Subject: [PATCH 120/313] MEX-418: update gas limits Signed-off-by: Claudiu Lataretu --- src/config/default.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index b1f3147c3..4161fcdf4 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -385,8 +385,8 @@ }, "stake": { "stakeFarm": { - "default": 9000000, - "withTokenMerge": 12000000 + "default": 20000000, + "withTokenMerge": 23000000 }, "unstakeFarm": 9500000, "unbondFarm": 8000000, @@ -413,7 +413,7 @@ }, "stakeProxy": { "stakeFarmTokens": { - "default": 26000000, + "default": 27000000, "withTokenMerge": 36000000 }, "claimDualYield": 55000000, From 401449548aae50b8b5802ff421a3683ae004029d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 15 Feb 2024 15:41:33 +0200 Subject: [PATCH 121/313] MEX-418: fix handler for claim multi rewards event on staking contracts Signed-off-by: Claudiu Lataretu --- .../weeklyRewardsSplitting.handler.service.ts | 20 +++++++++++++++++-- .../services/staking.setter.service.ts | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/modules/rabbitmq/handlers/weeklyRewardsSplitting.handler.service.ts b/src/modules/rabbitmq/handlers/weeklyRewardsSplitting.handler.service.ts index 81d48323a..3695197ed 100644 --- a/src/modules/rabbitmq/handlers/weeklyRewardsSplitting.handler.service.ts +++ b/src/modules/rabbitmq/handlers/weeklyRewardsSplitting.handler.service.ts @@ -20,6 +20,8 @@ import { UserEnergyComputeService } from 'src/modules/user/services/userEnergy/u import { EnergyAbiService } from 'src/modules/energy/services/energy.abi.service'; import { WeeklyRewardsSplittingSetterService } from 'src/submodules/weekly-rewards-splitting/services/weekly.rewarrds.splitting.setter.service'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; +import { StakingService } from 'src/modules/staking/services/staking.service'; +import { StakingSetterService } from 'src/modules/staking/services/staking.setter.service'; @Injectable() export class WeeklyRewardsSplittingHandlerService { @@ -31,6 +33,8 @@ export class WeeklyRewardsSplittingHandlerService { private readonly userEnergySetter: UserEnergySetterService, private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, private readonly weeklyRewardsSplittingSetter: WeeklyRewardsSplittingSetterService, + private readonly stakingService: StakingService, + private readonly stakingSetter: StakingSetterService, @Inject(PUB_SUB) private pubSub: RedisPubSub, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} @@ -134,8 +138,10 @@ export class WeeklyRewardsSplittingHandlerService { } return token; }); + const setter = await this.getSetter(event.address); + const keys = await Promise.all([ - this.getSetter(event.address).userRewardsForWeek( + setter.userRewardsForWeek( event.address, topics.caller.bech32(), topics.currentWeek, @@ -154,10 +160,20 @@ export class WeeklyRewardsSplittingHandlerService { }); } - private getSetter(address: string) { + private async getSetter(address: string) { if (address === scAddress.feesCollector) { return this.feesCollectorSetter; } + + const stakingAddresses = await this.stakingService.getFarmsStaking(); + + if ( + stakingAddresses.find((staking) => staking.address === address) !== + undefined + ) { + return this.stakingSetter; + } + return this.farmSetter.useSetter(address) as FarmSetterServiceV2; } diff --git a/src/modules/staking/services/staking.setter.service.ts b/src/modules/staking/services/staking.setter.service.ts index c2ffbedc5..407ccdd51 100644 --- a/src/modules/staking/services/staking.setter.service.ts +++ b/src/modules/staking/services/staking.setter.service.ts @@ -4,6 +4,7 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { GenericSetterService } from 'src/services/generics/generic.setter.service'; import { Logger } from 'winston'; +import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; @Injectable() export class StakingSetterService extends GenericSetterService { @@ -193,4 +194,23 @@ export class StakingSetterService extends GenericSetterService { CacheTtlInfo.ContractInfo.localTtl, ); } + + async userRewardsForWeek( + scAddress: string, + userAddress: string, + week: number, + value: EsdtTokenPayment[], + ): Promise { + return await this.setData( + this.getCacheKey( + 'userRewardsForWeek', + scAddress, + userAddress, + week, + ), + value, + CacheTtlInfo.ContractBalance.remoteTtl, + CacheTtlInfo.ContractBalance.localTtl, + ); + } } From b2e694614c232d9a9f81b039ef346c3c959cb347 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Feb 2024 16:33:07 +0200 Subject: [PATCH 122/313] MEX-437: update gas limits for farm v2 transactions Signed-off-by: Claudiu Lataretu --- src/config/default.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 4161fcdf4..eca687e9f 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -216,8 +216,8 @@ }, "v2": { "enterFarm": { - "default": 15000000, - "withTokenMerge": 16500000 + "default": 16500000, + "withTokenMerge": 18000000 }, "unlockedRewards": { "exitFarm": { From 286be0064f5ef5fd7b352f109a51708c31e8014b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Feb 2024 16:33:52 +0200 Subject: [PATCH 123/313] MEX-437: remove exit amount argument from farm transaction Signed-off-by: Claudiu Lataretu --- src/modules/farm/models/farm.args.ts | 7 ++++++- .../farm/v2/services/farm.v2.transaction.service.ts | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/farm/models/farm.args.ts b/src/modules/farm/models/farm.args.ts index 476986f44..479ec1d21 100644 --- a/src/modules/farm/models/farm.args.ts +++ b/src/modules/farm/models/farm.args.ts @@ -58,7 +58,12 @@ export class SftFarmInteractionArgs { export class ExitFarmArgs extends SftFarmInteractionArgs { @Field(() => Boolean, { nullable: true }) withPenalty = false; - @Field({ nullable: true }) + @Field({ + nullable: true, + deprecationReason: + 'Exit farm no longer require this value;' + + 'field is deprecated and will be removed on next release;', + }) exitAmount?: string; } diff --git a/src/modules/farm/v2/services/farm.v2.transaction.service.ts b/src/modules/farm/v2/services/farm.v2.transaction.service.ts index d29bec774..93dc84eb4 100644 --- a/src/modules/farm/v2/services/farm.v2.transaction.service.ts +++ b/src/modules/farm/v2/services/farm.v2.transaction.service.ts @@ -1,4 +1,4 @@ -import { Address, BigUIntValue, TokenTransfer } from '@multiversx/sdk-core'; +import { Address, TokenTransfer } from '@multiversx/sdk-core'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; import { mxConfig, gasConfig } from 'src/config'; @@ -86,7 +86,7 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { ); return contract.methodsExplicit - .exitFarm([new BigUIntValue(new BigNumber(args.exitAmount))]) + .exitFarm() .withSingleESDTNFTTransfer( TokenTransfer.metaEsdtFromBigInteger( args.farmTokenID, From c8cb632b2bb6e709f198da2d868eb870fe27337d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Feb 2024 14:27:02 +0200 Subject: [PATCH 124/313] MEX-410: update gas limit for position creator Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index eca687e9f..46b343526 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -500,7 +500,7 @@ "vote": 50000000 }, "positionCreator": { - "singleToken": 50000000, + "singleToken": 70000000, "energyPosition": 20000000 }, "composableTasks": { From 6165b0f929d9329a2ea5f8cea1a0ff6c0eb58599 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Feb 2024 15:54:05 +0200 Subject: [PATCH 125/313] MEX-410: fix unit tests on position creator Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index f4fb2a766..b52414536 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -32,7 +32,7 @@ import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staki import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; import { ProxyFarmAbiServiceProvider } from 'src/modules/proxy/mocks/proxy.abi.service.mock'; import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; -import { scAddress } from 'src/config'; +import { gasConfig, scAddress } from 'src/config'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; @@ -132,7 +132,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -175,7 +175,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -216,7 +216,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `createPairPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, ), @@ -309,7 +309,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, ), @@ -351,7 +351,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -416,7 +416,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, ), @@ -463,7 +463,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -508,7 +508,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -552,7 +552,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( `createFarmPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, ), @@ -648,7 +648,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', ), @@ -697,7 +697,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -769,7 +769,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', ), @@ -823,7 +823,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -877,7 +877,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@EGLDMEXLP-abcdef@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@@', ), @@ -969,7 +969,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', ), @@ -1009,7 +1009,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -1077,7 +1077,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', ), @@ -1122,7 +1122,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -1230,7 +1230,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', ), @@ -1282,7 +1282,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', ), @@ -1352,7 +1352,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', ), @@ -1506,7 +1506,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', ), @@ -1558,7 +1558,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@LKFARM-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', ), @@ -1686,7 +1686,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', ), @@ -1743,7 +1743,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', ), @@ -1803,7 +1803,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, data: encodeTransactionData( 'ESDTNFTTransfer@EGLDMEXFL-abcdef@01@100000000000000000000@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@exitFarmPos@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000000', ), @@ -1858,7 +1858,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -1896,7 +1896,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: 50000000, + gasLimit: gasConfig.positionCreator.singleToken, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From fa979f5f40f0f028414b406d14b19f7c64698024 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Feb 2024 18:22:59 +0200 Subject: [PATCH 126/313] MEX-418: update gas limit for unstake staking position Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 46b343526..26d3b42dc 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -388,7 +388,7 @@ "default": 20000000, "withTokenMerge": 23000000 }, - "unstakeFarm": 9500000, + "unstakeFarm": 18500000, "unbondFarm": 8000000, "claimRewards": 10000000, "claimBoostedRewards": 20000000, From 8c52e7e5c928492f789a1ca6121a6a0ae3378720 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Feb 2024 17:43:07 +0200 Subject: [PATCH 127/313] MEX-437: remove exit amount argument from exit farm proxy transaction Signed-off-by: Claudiu Lataretu --- src/modules/proxy/models/proxy-farm.args.ts | 9 +++++++-- .../proxy-farm/proxy.farm.transactions.service.ts | 11 ----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/modules/proxy/models/proxy-farm.args.ts b/src/modules/proxy/models/proxy-farm.args.ts index 4fe32b12c..504a60353 100644 --- a/src/modules/proxy/models/proxy-farm.args.ts +++ b/src/modules/proxy/models/proxy-farm.args.ts @@ -18,8 +18,13 @@ export class ExitFarmProxyArgs { @Field(() => Int) wrappedFarmTokenNonce: number; @Field() amount: string; - @Field({ nullable: true }) - exitAmount: string; + @Field({ + nullable: true, + deprecationReason: + 'Exit farm no longer require this value;' + + 'field is deprecated and will be removed on next release;', + }) + exitAmount?: string; @Field(() => Boolean, { nullable: true }) lockRewards = false; @Field(() => Boolean, { nullable: true }) diff --git a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts index 54f3200d6..f126a16c3 100644 --- a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts +++ b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts @@ -96,14 +96,6 @@ export class ProxyFarmTransactionsService { proxyAddress: string, args: ExitFarmProxyArgs, ): Promise { - const version = proxyVersion(proxyAddress); - if ( - version === 'v2' && - !args.exitAmount && - !new BigNumber(args.exitAmount).isPositive() - ) { - throw new Error('Invalid exit amount'); - } const contract = await this.mxProxy.getProxyDexSmartContract( proxyAddress, ); @@ -111,9 +103,6 @@ export class ProxyFarmTransactionsService { const endpointArgs: TypedValue[] = [ BytesValue.fromHex(new Address(args.farmAddress).hex()), ]; - if (version === 'v2') { - endpointArgs.push(new BigUIntValue(new BigNumber(args.exitAmount))); - } const gasLimit = await this.getExitFarmProxyGasLimit(args); From c4fff4c27c025f40242b0270fe925797d7fc72a4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 27 Feb 2024 15:57:49 +0200 Subject: [PATCH 128/313] MEX-437: remove exit amount from staking proxy unstake farm tokens Signed-off-by: Claudiu Lataretu --- .../services/staking.proxy.transactions.service.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts index 8916843e0..8e47277c4 100644 --- a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts +++ b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts @@ -7,11 +7,9 @@ import { ruleOfThree } from 'src/helpers/helpers'; import { InputTokenModel } from 'src/models/inputToken.model'; import { TransactionModel } from 'src/models/transaction.model'; import { FarmFactoryService } from 'src/modules/farm/farm.factory'; -import { FarmVersion } from 'src/modules/farm/models/farm.model'; import { PairService } from 'src/modules/pair/services/pair.service'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; -import { farmVersion } from 'src/utils/farm.utils'; import { generateLogMessage } from 'src/utils/generate-log-message'; import { tokenIdentifier } from 'src/utils/token.converters'; import { Logger } from 'winston'; @@ -181,10 +179,6 @@ export class StakingProxyTransactionService { new BigUIntValue(amount1Min), ]; - if (farmVersion(farmAddress) === FarmVersion.V2) { - endpointArgs.push(new BigUIntValue(liquidityPositionAmount)); - } - return contract.methodsExplicit .unstakeFarmTokens(endpointArgs) .withSingleESDTNFTTransfer( From 0eb002e79f37a1e5c9b0e6ddd128250f5d8d71ef Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 27 Feb 2024 16:11:50 +0200 Subject: [PATCH 129/313] MEX-437: update gas limit for create position single token Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 26d3b42dc..b5da89b86 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -500,7 +500,7 @@ "vote": 50000000 }, "positionCreator": { - "singleToken": 70000000, + "singleToken": 85000000, "energyPosition": 20000000 }, "composableTasks": { From beac717630e4797285d68bb917b296ac536125ae Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 27 Feb 2024 17:02:03 +0200 Subject: [PATCH 130/313] MEX-397: fix increase energy for wrapped farm tokens Signed-off-by: Claudiu Lataretu --- src/modules/proxy/proxy.transaction.resolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/proxy/proxy.transaction.resolver.ts b/src/modules/proxy/proxy.transaction.resolver.ts index ef75de8de..31e2faa28 100644 --- a/src/modules/proxy/proxy.transaction.resolver.ts +++ b/src/modules/proxy/proxy.transaction.resolver.ts @@ -265,7 +265,7 @@ export class ProxyTransactionResolver { }); } - return this.transactionsProxyPairService.increaseProxyPairTokenEnergy( + return this.transactionsProxyFarmService.increaseProxyFarmTokenEnergy( user.address, proxyAddress, payment, @@ -282,7 +282,7 @@ export class ProxyTransactionResolver { ); if (proxyAddress !== scAddress.proxyDexAddress.v2) { - throw new Error('Wrapped lp token is not supported'); + throw new Error('Wrapped token is not supported'); } const lockOptions = await this.energyAbi.lockOptions(); From b5b3e4008a51300e36dae36e1f3cf514cd9e2fb9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 28 Feb 2024 14:47:09 +0200 Subject: [PATCH 131/313] MEX-443: split position creator gas config into granular values Signed-off-by: Claudiu Lataretu --- src/config/default.json | 12 ++++- .../services/position.creator.transaction.ts | 44 ++++++++++--------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index b5da89b86..3478e57d2 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -500,7 +500,17 @@ "vote": 50000000 }, "positionCreator": { - "singleToken": 85000000, + "singleToken": { + "liquidityPosition": 57500000, + "farmPosition": 73000000, + "dualFarmPosition": 115000000, + "stakingPosition": 55000000 + }, + "dualTokens": { + "farmPosition": 30000000, + "dualFarmPosition": 67000000, + "exitFarm": 50000000 + }, "energyPosition": 20000000 }, "composableTasks": { diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 1468dd46b..a68752584 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -78,28 +78,28 @@ export class PositionCreatorTransactionService { let interaction: Interaction; if (lockEpochs) { - interaction = contract.methodsExplicit - .createPairPosFromSingleToken([ + interaction = contract.methodsExplicit.createPairPosFromSingleToken( + [ new U64Value(new BigNumber(lockEpochs)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), ...singleTokenPairInput.swapRouteArgs, - ]) - .withGasLimit(gasConfig.positionCreator.singleToken); + ], + ); } else { - interaction = contract.methodsExplicit - .createLpPosFromSingleToken([ - new AddressValue(Address.fromBech32(pairAddress)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, - ]) - .withGasLimit(gasConfig.positionCreator.singleToken); + interaction = contract.methodsExplicit.createLpPosFromSingleToken([ + new AddressValue(Address.fromBech32(pairAddress)), + new BigUIntValue(singleTokenPairInput.amount0Min), + new BigUIntValue(singleTokenPairInput.amount1Min), + ...singleTokenPairInput.swapRouteArgs, + ]); } interaction = interaction .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit( + gasConfig.positionCreator.singleToken.liquidityPosition, + ) .withChainID(mxConfig.chainID); if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { @@ -182,8 +182,8 @@ export class PositionCreatorTransactionService { let interaction = contract.methodsExplicit .createFarmPosFromSingleToken(endpointArgs) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) - .withChainID(mxConfig.chainID); + .withChainID(mxConfig.chainID) + .withGasLimit(gasConfig.positionCreator.singleToken.farmPosition); if ( payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && @@ -273,7 +273,9 @@ export class PositionCreatorTransactionService { ...singleTokenPairInput.swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit( + gasConfig.positionCreator.singleToken.dualFarmPosition, + ) .withChainID(mxConfig.chainID); if ( @@ -376,7 +378,7 @@ export class PositionCreatorTransactionService { ...multiSwapArgs, ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit(gasConfig.positionCreator.singleToken.stakingPosition) .withChainID(mxConfig.chainID); if ( @@ -534,7 +536,7 @@ export class PositionCreatorTransactionService { ), ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit(gasConfig.positionCreator.dualTokens.farmPosition) .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); @@ -611,7 +613,7 @@ export class PositionCreatorTransactionService { ), ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit(gasConfig.positionCreator.dualTokens.dualFarmPosition) .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); @@ -659,7 +661,7 @@ export class PositionCreatorTransactionService { ), ) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit(gasConfig.positionCreator.dualTokens.exitFarm) .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); @@ -698,7 +700,7 @@ export class PositionCreatorTransactionService { ...singleTokenInput.swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken) + .withGasLimit(gasConfig.positionCreator.energyPosition) .withChainID(mxConfig.chainID); if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { From 799000cf014c0c05b822802a3732864db5496014 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 28 Feb 2024 14:54:25 +0200 Subject: [PATCH 132/313] MEX-443: fix gas limit for position creator unit tests Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 74 ++++++++++++------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index b52414536..359018154 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -132,7 +132,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.liquidityPosition, data: encodeTransactionData( `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -175,7 +176,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.liquidityPosition, data: encodeTransactionData( `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -216,7 +218,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.liquidityPosition, data: encodeTransactionData( `createPairPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, ), @@ -309,7 +312,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( `createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, ), @@ -351,7 +355,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -416,7 +421,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, ), @@ -463,7 +469,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -508,7 +515,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -552,7 +560,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( `createFarmPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, ), @@ -648,7 +657,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( 'createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', ), @@ -697,7 +707,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -769,7 +780,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', ), @@ -823,7 +835,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -877,7 +890,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@EGLDMEXLP-abcdef@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@@', ), @@ -969,7 +983,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.stakingPosition, data: encodeTransactionData( 'createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', ), @@ -1009,7 +1024,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.stakingPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -1077,7 +1093,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.stakingPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', ), @@ -1122,7 +1139,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: + gasConfig.positionCreator.singleToken.stakingPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -1230,7 +1248,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', ), @@ -1282,7 +1300,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', ), @@ -1352,7 +1370,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000', ), @@ -1506,7 +1524,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', ), @@ -1558,7 +1576,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@LKFARM-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', ), @@ -1686,7 +1704,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.dualFarmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', ), @@ -1743,7 +1761,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.dualFarmPosition, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', ), @@ -1803,7 +1821,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.dualTokens.exitFarm, data: encodeTransactionData( 'ESDTNFTTransfer@EGLDMEXFL-abcdef@01@100000000000000000000@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@exitFarmPos@0000000000000000000000000000000000000000000000000000000000000021@99000000000000000000@99000000000000000000000', ), @@ -1858,7 +1876,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.energyPosition, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -1896,7 +1914,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: gasConfig.positionCreator.singleToken, + gasLimit: gasConfig.positionCreator.energyPosition, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From a4b519470f2fafdb70d6327013a78766f400de43 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 28 Feb 2024 16:06:38 +0200 Subject: [PATCH 133/313] MEX-410: accept EGLD for position creation dual farm with 2 tokens Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 4 +-- .../services/position.creator.transaction.ts | 26 +++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 2ef136495..c9d722021 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -130,14 +130,14 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => TransactionModel) + @Query(() => [TransactionModel]) async createDualFarmPositionDualTokens( @AuthUser() user: UserAuthResult, @Args('dualFarmAddress') dualFarmAddress: string, @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createDualFarmPositionDualTokens( user.address, dualFarmAddress, diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index a68752584..217969cf8 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -551,10 +551,29 @@ export class PositionCreatorTransactionService { stakingProxyAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + ): Promise { const pairAddress = await this.stakingProxyAbi.pairAddress( stakingProxyAddress, ); + + const transactions = []; + + if (payments[0].tokenIdentifier === mxConfig.EGLDIdentifier) { + payments[0].tokenIdentifier = + await this.wrapAbi.wrappedEgldTokenID(); + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[0].amount), + ); + } + + if (payments[1].tokenIdentifier === mxConfig.EGLDIdentifier) { + payments[1].tokenIdentifier = + await this.wrapAbi.wrappedEgldTokenID(); + transactions.push( + await this.wrapTransaction.wrapEgld(sender, payments[1].amount), + ); + } + const [firstTokenID, secondTokenID, dualYieldTokenID] = await Promise.all([ this.pairAbi.firstTokenID(pairAddress), @@ -586,7 +605,7 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); - return contract.methodsExplicit + const interaction = contract.methodsExplicit .createMetastakingPosFromTwoTokens([ new AddressValue(Address.fromBech32(stakingProxyAddress)), new BigUIntValue(amount0Min), @@ -617,6 +636,9 @@ export class PositionCreatorTransactionService { .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); + + transactions.push(interaction); + return transactions; } async exitFarmPositionDualTokens( From da458168977d579134b8f8ea25dad4a677142cb2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 28 Feb 2024 16:14:07 +0200 Subject: [PATCH 134/313] MEX-410: Add unit test for position creator dual farm with dual tokens EGLD Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 164 +++++++++++++----- 1 file changed, 124 insertions(+), 40 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 359018154..2f5ebbf20 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -1678,7 +1678,7 @@ describe('PositionCreatorTransaction', () => { ).bech32(), ); - const transaction = await service.createDualFarmPositionDualTokens( + const transactions = await service.createDualFarmPositionDualTokens( Address.Zero().bech32(), Address.Zero().bech32(), [ @@ -1696,25 +1696,106 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.dualTokens.dualFarmPosition, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: + gasConfig.positionCreator.dualTokens.dualFarmPosition, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); + }); + + it('should return transaction with egld wrap', async () => { + const service = module.get( + PositionCreatorTransactionService, + ); + const stakingProxyAbi = module.get( + StakingProxyAbiService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + ); + + const transactions = await service.createDualFarmPositionDualTokens( + Address.Zero().bech32(), + Address.Zero().bech32(), + [ + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'MEX-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + new EsdtTokenPayment({ + tokenIdentifier: 'METASTAKE-1234', + tokenNonce: 1, + amount: '100000000000000000000', + }), + ], + 0.01, + ); + + expect(transactions).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + sender: '', + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: 4200000, + data: encodeTransactionData('wrapEgld'), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: + gasConfig.positionCreator.dualTokens.dualFarmPosition, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); it('should return transaction no merge farm tokens', async () => { @@ -1730,7 +1811,7 @@ describe('PositionCreatorTransaction', () => { ).bech32(), ); - const transaction = await service.createDualFarmPositionDualTokens( + const transactions = await service.createDualFarmPositionDualTokens( Address.Zero().bech32(), Address.Zero().bech32(), [ @@ -1753,25 +1834,28 @@ describe('PositionCreatorTransaction', () => { 0.01, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: Address.Zero().bech32(), - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.dualTokens.dualFarmPosition, - data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: + gasConfig.positionCreator.dualTokens.dualFarmPosition, + data: encodeTransactionData( + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); From d550c9ce337b2ce9b008ee10e14932b8707bbfb1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 1 Mar 2024 17:45:58 +0200 Subject: [PATCH 135/313] MEX-443: use specific gas limit for farm poisiton dual tokens with locked token Signed-off-by: Claudiu Lataretu --- src/config/default.json | 3 ++- .../services/position.creator.transaction.ts | 6 +++++- .../specs/position.creator.transaction.spec.ts | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 3478e57d2..29cba9181 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -508,10 +508,11 @@ }, "dualTokens": { "farmPosition": 30000000, + "farmPositionProxy": 50000000, "dualFarmPosition": 67000000, "exitFarm": 50000000 }, - "energyPosition": 20000000 + "energyPosition": 30000000 }, "composableTasks": { "default": 30000000 diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 217969cf8..b106ca0c5 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -509,6 +509,10 @@ export class PositionCreatorTransactionService { new BigUIntValue(amount1Min), ]; + const gasLimit = isLockedToken + ? gasConfig.positionCreator.dualTokens.farmPositionProxy + : gasConfig.positionCreator.dualTokens.farmPosition; + const contract = isLockedToken ? await this.mxProxy.getLockedTokenPositionCreatorContract() : await this.mxProxy.getPostitionCreatorContract(); @@ -536,7 +540,7 @@ export class PositionCreatorTransactionService { ), ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.dualTokens.farmPosition) + .withGasLimit(gasLimit) .withChainID(mxConfig.chainID) .buildTransaction() .toPlainObject(); diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 2f5ebbf20..ca18e7106 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -1524,7 +1524,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, + gasLimit: + gasConfig.positionCreator.dualTokens.farmPositionProxy, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', ), @@ -1576,7 +1577,8 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.positionCreator.dualTokens.farmPosition, + gasLimit: + gasConfig.positionCreator.dualTokens.farmPositionProxy, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@ELKMEX-123456@01@100000000000000000000@LKFARM-123456@01@100000000000000000000@createFarmPosFromTwoTokens@99000000000000000000@99000000000000000000', ), From 1e5e857de439e3b7781046a68ff44fc54fc3985e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 1 Mar 2024 18:26:28 +0200 Subject: [PATCH 136/313] MEX-443: update gas limit for energy position Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 29cba9181..64917bc82 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -512,7 +512,7 @@ "dualFarmPosition": 67000000, "exitFarm": 50000000 }, - "energyPosition": 30000000 + "energyPosition": 45000000 }, "composableTasks": { "default": 30000000 From 040b9c04e11bd6b0f55505db55f6bf4f0b4fab9b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Mar 2024 12:23:55 +0200 Subject: [PATCH 137/313] MEX-443: update gas limits for compose tasks Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 64917bc82..91788e002 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -515,7 +515,7 @@ "energyPosition": 45000000 }, "composableTasks": { - "default": 30000000 + "default": 35000000 }, "lockedAssetCreate": 5000000, "wrapeGLD": 4200000, From 02312362f45b65ea47683419bc9c80b7a4397f60 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Mar 2024 12:45:50 +0200 Subject: [PATCH 138/313] MEX-443: fix pair transactions unit test Signed-off-by: Claudiu Lataretu --- src/modules/pair/specs/pair.transactions.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pair/specs/pair.transactions.service.spec.ts b/src/modules/pair/specs/pair.transactions.service.spec.ts index 62c1146f2..31fdaaf9b 100644 --- a/src/modules/pair/specs/pair.transactions.service.spec.ts +++ b/src/modules/pair/specs/pair.transactions.service.spec.ts @@ -461,7 +461,7 @@ describe('TransactionPairService', () => { expect(transactions).toEqual({ chainID: 'T', data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwNEBAQDAyQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMTA0', - gasLimit: 30000000, + gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From 20195d8757cef37885b0eb51a1ea4b24b2e6a985 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Mar 2024 12:57:27 +0200 Subject: [PATCH 139/313] MEX-443: fix composable tasks unit tests Signed-off-by: Claudiu Lataretu --- src/modules/auto-router/specs/auto-router.service.spec.ts | 2 +- .../specs/composable.tasks.transaction.spec.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/auto-router/specs/auto-router.service.spec.ts b/src/modules/auto-router/specs/auto-router.service.spec.ts index 14eb94f58..05fcead05 100644 --- a/src/modules/auto-router/specs/auto-router.service.spec.ts +++ b/src/modules/auto-router/specs/auto-router.service.spec.ts @@ -280,7 +280,7 @@ describe('AutoRouterService', () => { receiverUsername: undefined, senderUsername: undefined, gasPrice: 1000000000, - gasLimit: 30000000, + gasLimit: gasConfig.composableTasks.default, data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNzExNzJhY2UwMjZiMGM0QEBAMDJAMDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDcxMTcyYWNlMDI2YjBjNA==', chainID: 'T', version: 1, diff --git a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts index 40996c560..03d63c918 100644 --- a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts +++ b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts @@ -10,6 +10,7 @@ import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { EgldOrEsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; import { ComposableTaskType } from '../models/composable.tasks.model'; import { Address } from '@multiversx/sdk-core/out'; +import { gasConfig } from 'src/config'; describe('Composable Tasks Transaction', () => { let module: TestingModule; @@ -68,7 +69,7 @@ describe('Composable Tasks Transaction', () => { expect(transaction).toEqual({ chainID: 'T', data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MGRlMGI2YjNhNzY0MDAwMEBA', - gasLimit: 30000000, + gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -98,7 +99,7 @@ describe('Composable Tasks Transaction', () => { expect(transaction).toEqual({ chainID: 'T', data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAxMzEyZDAwQEBAMDJAMDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDQwMTMxMmQwMA==', - gasLimit: 30000000, + gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -132,7 +133,7 @@ describe('Composable Tasks Transaction', () => { expect(transaction).toEqual({ chainID: 'T', data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDEzMTJkMDBANjM2ZjZkNzA2ZjczNjU1NDYxNzM2YjczQDAwMDAwMDA0NDU0NzRjNDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAyQDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAxQA==', - gasLimit: 30000000, + gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From ca280a1ee2455e35b8b410bd65f051b5434b5271 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 4 Mar 2024 16:19:31 +0200 Subject: [PATCH 140/313] MEX-443: Update gas limits for farm single token position Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 91788e002..c2661bca9 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -502,7 +502,7 @@ "positionCreator": { "singleToken": { "liquidityPosition": 57500000, - "farmPosition": 73000000, + "farmPosition": 87000000, "dualFarmPosition": 115000000, "stakingPosition": 55000000 }, From 5ff567478d004b26fafb3bc0fa54fbdf7794060e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 6 Mar 2024 14:18:28 +0200 Subject: [PATCH 141/313] MEX-410: fix compute liquidity amounts Signed-off-by: Claudiu Lataretu --- .../services/position.creator.compute.ts | 27 ++++++++++++++----- .../position.creator.transaction.spec.ts | 26 +++++++++--------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 7c45afaf3..75ab33f6d 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -97,7 +97,7 @@ export class PositionCreatorComputeService { .minus(halfPayment) .toFixed(); - const [amount0, amount1] = await Promise.all([ + let [amount0, amount1] = await Promise.all([ await this.computeSwap( pairAddress, swapRoute.tokenOutID, @@ -112,12 +112,25 @@ export class PositionCreatorComputeService { ), ]); - const amount0Min = new BigNumber(amount0) - .multipliedBy(1 - tolerance) - .integerValue(); - const amount1Min = new BigNumber(amount1) - .multipliedBy(1 - tolerance) - .integerValue(); + amount0 = + swapToTokenID === firstTokenID + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + secondTokenID, + amount1.toFixed(), + ) + : amount0; + amount1 = + swapToTokenID === secondTokenID + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + firstTokenID, + amount0.toFixed(), + ) + : amount1; + + const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); + const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); const swapRouteArgs = this.autoRouterTransaction.multiPairFixedInputSwaps({ diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 2f5ebbf20..d4f9fe87c 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -135,7 +135,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.liquidityPosition, data: encodeTransactionData( - `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), chainID: 'T', version: 1, @@ -179,7 +179,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.liquidityPosition, data: encodeTransactionData( - `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), chainID: 'T', version: 1, @@ -221,7 +221,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.liquidityPosition, data: encodeTransactionData( - `createPairPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, + `createPairPosFromSingleToken@1440@47008144020574367766@47008144020574367766823`, ), chainID: 'T', version: 1, @@ -315,7 +315,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( - `createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, + `createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@47008144020574367766@47008144020574367766823`, ), chainID: 'T', version: 1, @@ -358,7 +358,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( - `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), chainID: 'T', version: 1, @@ -424,7 +424,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( - `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@49500000000000000000@47008144020574367766823`, + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@47008144020574367766@47008144020574367766823`, ), chainID: 'T', version: 1, @@ -472,7 +472,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( - `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), chainID: 'T', version: 1, @@ -518,7 +518,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( - `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@1440@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@1440@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), chainID: 'T', version: 1, @@ -563,7 +563,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.farmPosition, data: encodeTransactionData( - `createFarmPosFromSingleToken@1440@49500000000000000000@47008144020574367766823`, + `createFarmPosFromSingleToken@1440@47008144020574367766@47008144020574367766823`, ), chainID: 'T', version: 1, @@ -660,7 +660,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', + 'createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@47008144020574367766@47008144020574367766823', ), chainID: 'T', version: 1, @@ -710,7 +710,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), chainID: 'T', version: 1, @@ -783,7 +783,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@49500000000000000000@47008144020574367766823', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@47008144020574367766@47008144020574367766823', ), chainID: 'T', version: 1, @@ -838,7 +838,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@494999999950351053163@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), chainID: 'T', version: 1, From b5855cd31402959e6916872dad999411a893a128 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 8 Mar 2024 17:39:32 +0200 Subject: [PATCH 142/313] MEX-453: compute optimal energy for staking position Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 2 + .../services/staking.compute.service.ts | 61 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 13 ++++ 3 files changed, 76 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 9261732d0..019b9a43b 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -52,6 +52,8 @@ export class StakingModel { description: 'Factors used to compute boosted rewards', }) boostedYieldsFactors: BoostedYieldsFactors; + @Field({ description: 'Optimal energy for staking position' }) + optimalEnergyPerStaking: string; @Field({ description: 'Timekeeping for boosted rewards' }) time: WeekTimekeepingModel; @Field(() => [GlobalInfoByWeekModel], { diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 634f8759f..506c50217 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -619,6 +619,67 @@ export class StakingComputeService { return payments; } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async optimalEnergyPerStaking( + scAddress: string, + week: number, + ): Promise { + return await this.computeOptimalEnergyPerStaking(scAddress, week); + } + + // + // The boosted rewards is min(MAX_REWARDS, COMPUTED_REWARDS) + // + // USER_FARM_AMOUNT + // MAX_REWARDS = u * ------------------ + // FARM_SUPPLY + // + // A USER_FARM_AMOUNT B USER_ENERGY_AMOUNT + // COMPUTED_REWARDS = ----- * ------------------ + ----- * -------------------- + // A + B FARM_SUPPLY A + B TOTAL_ENERGY + // + // + // the optimal ratio is the ration when MAX_REWARDS = COMPUTED_REWARDS, which gives the following formula: + // + // USER_ENERGY u * (A + B) - A TOTAL_ENERGY + // ------------------ = ----------------- * -------------- + // USER_FARM_AMOUNT B FARM_SUPPLY + // + async computeOptimalEnergyPerStaking( + stakingAddress: string, + week: number, + ): Promise { + const [factors, farmSupply, energySupply] = await Promise.all([ + this.stakingAbi.boostedYieldsFactors(stakingAddress), + this.stakingAbi.farmTokenSupply(stakingAddress), + this.weeklyRewardsSplittingAbi.totalEnergyForWeek( + stakingAddress, + week, + ), + ]); + + const u = factors.maxRewardsFactor; + const A = factors.userRewardsFarm; + const B = factors.userRewardsEnergy; + + const optimisationConstant = new BigNumber(u) + .multipliedBy(new BigNumber(A).plus(B)) + .minus(A) + .dividedBy(B); + return optimisationConstant + .multipliedBy(energySupply) + .dividedBy(farmSupply) + .integerValue() + .toFixed(); + } + async computeBlocksInWeek( scAddress: string, week: number, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 577276fef..da6f2ce07 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -195,6 +195,19 @@ export class StakingResolver { return this.stakingAbi.boostedYieldsFactors(parent.address); } + @ResolveField() + async optimalEnergyPerStaking( + @Parent() parent: StakingModel, + ): Promise { + const currentWeek = await this.weekTimekeepingAbi.currentWeek( + parent.address, + ); + return this.stakingCompute.optimalEnergyPerStaking( + parent.address, + currentWeek, + ); + } + @ResolveField() async accumulatedRewardsForWeek( @Parent() parent: StakingModel, From bf6a30b7dc141efd076833405fc01a40d6ff96c1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 12 Mar 2024 18:48:39 +0200 Subject: [PATCH 143/313] MEX-376: accept array of addresses for migrate transactions Signed-off-by: Claudiu Lataretu --- .../farm/v2/farm.v2.transaction.resolver.ts | 26 ++++++++++++------- src/modules/staking/staking.resolver.ts | 12 ++++++--- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/modules/farm/v2/farm.v2.transaction.resolver.ts b/src/modules/farm/v2/farm.v2.transaction.resolver.ts index 79bc099ca..cb7bc43e9 100644 --- a/src/modules/farm/v2/farm.v2.transaction.resolver.ts +++ b/src/modules/farm/v2/farm.v2.transaction.resolver.ts @@ -20,20 +20,26 @@ export class FarmTransactionResolverV2 { 'Generate transactions to initialize the total farm positions for a user', }) async migrateTotalFarmPositions( - @Args('farmAddress') farmAddress: string, + @Args('farmsAddresses', { type: () => [String] }) + farmsAddresses: string[], @AuthUser() user: UserAuthResult, ): Promise { - if (farmVersion(farmAddress) !== FarmVersion.V2) { - throw new GraphQLError('Farm version is not supported', { - extensions: { - code: ApolloServerErrorCode.BAD_USER_INPUT, - }, - }); + for (const farmAddress of farmsAddresses) { + if (farmVersion(farmAddress) !== FarmVersion.V2) { + throw new GraphQLError('Farm version is not supported', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } } - return this.farmTransaction.migrateTotalFarmPosition( - farmAddress, - user.address, + const promises = farmsAddresses.map((farmAddress) => + this.farmTransaction.migrateTotalFarmPosition( + farmAddress, + user.address, + ), ); + return (await Promise.all(promises)).flat(); } @UseGuards(JwtOrNativeAuthGuard) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index da6f2ce07..289a93fc6 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -378,13 +378,17 @@ export class StakingResolver { @UseGuards(JwtOrNativeAuthGuard) @Query(() => [TransactionModel]) async migrateTotalStakingPosition( - @Args('stakeAddress') stakeAddress: string, + @Args('stakeAddresses', { type: () => [String] }) + stakeAddresses: string[], @AuthUser() user: UserAuthResult, ): Promise { - return this.stakingTransactionService.migrateTotalStakingPosition( - stakeAddress, - user.address, + const promises = stakeAddresses.map((stakeAddress) => + this.stakingTransactionService.migrateTotalStakingPosition( + stakeAddress, + user.address, + ), ); + return (await Promise.all(promises)).flat(); } @UseGuards(JwtOrNativeAdminGuard) From 77d091c323eb9ab255f8952b26928dfdea412593 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 14 Mar 2024 11:55:51 +0200 Subject: [PATCH 144/313] MEX-408: update dual yiled migration endpoint to accept array of addresses Signed-off-by: Claudiu Lataretu --- .../staking.proxy.transactions.service.ts | 26 +++++++------- .../staking.proxy.transaction.service.spec.ts | 35 ++++++++++--------- .../staking-proxy/staking.proxy.resolver.ts | 22 ++++++++---- .../staking.proxy.address.validator.ts | 24 +++++++------ 4 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts index a31414f8f..14c6fe361 100644 --- a/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts +++ b/src/modules/staking-proxy/services/staking.proxy.transactions.service.ts @@ -22,6 +22,7 @@ import { StakingProxyService } from './staking.proxy.service'; import { StakingProxyAbiService } from './staking.proxy.abi.service'; import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; +import { ContextGetterService } from 'src/services/context/context.getter.service'; @Injectable() export class StakingProxyTransactionService { @@ -34,6 +35,7 @@ export class StakingProxyTransactionService { private readonly stakingAbi: StakingAbiService, private readonly mxProxy: MXProxyService, private readonly apiService: MXApiService, + private readonly contextGetter: ContextGetterService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} @@ -199,10 +201,10 @@ export class StakingProxyTransactionService { .toPlainObject(); } - async migrateDualYieldTokens( + async migrateTotalDualFarmTokenPosition( proxyStakeAddress: string, userAddress: string, - ): Promise { + ): Promise { const [dualYieldTokenID, farmAddress, stakingAddress, userNftsCount] = await Promise.all([ this.stakeProxyAbi.dualYieldTokenID(proxyStakeAddress), @@ -211,7 +213,7 @@ export class StakingProxyTransactionService { this.apiService.getNftsCountForUser(userAddress), ]); - const userNfts = await this.apiService.getNftsForUser( + const userNfts = await this.contextGetter.getNftsForUser( userAddress, 0, userNftsCount, @@ -220,7 +222,7 @@ export class StakingProxyTransactionService { ); if (userNfts.length === 0) { - return undefined; + return []; } const [farmMigrationNonce, stakingMigrationNonce] = await Promise.all([ @@ -255,14 +257,14 @@ export class StakingProxyTransactionService { }, ); - if (payments.length > 0) { - return this.claimDualYield(userAddress, { - proxyStakingAddress: proxyStakeAddress, - payments: payments, - }); - } - - return undefined; + return Promise.all( + payments.map((payment) => + this.claimDualYield(userAddress, { + proxyStakingAddress: proxyStakeAddress, + payments: [payment], + }), + ), + ); } private async validateInputTokens( diff --git a/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts b/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts index 643168350..ad5770ebe 100644 --- a/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts +++ b/src/modules/staking-proxy/specs/staking.proxy.transaction.service.spec.ts @@ -248,7 +248,7 @@ describe('StakingProxyTransactionService', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: 50000000, + gasLimit: gasConfig.stakeProxy.claimDualYield, data: encodeTransactionData( `MultiESDTNFTTransfer@0000000000000000000000000000000000000000000000000000000000000000@01@METASTAKE-123456@01@1000000000000000000@claimDualYield`, ), @@ -281,12 +281,13 @@ describe('StakingProxyTransactionService', () => { }, ]); - const transaction = await service.migrateDualYieldTokens( - Address.Zero().bech32(), - Address.Zero().bech32(), - ); + const transactions = + await service.migrateTotalDualFarmTokenPosition( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); - expect(transaction).toEqual(expectedTransactionStub); + expect(transactions).toEqual([expectedTransactionStub]); }); it('migrate on lp farm token', async () => { @@ -310,12 +311,13 @@ describe('StakingProxyTransactionService', () => { }, ]); - const transaction = await service.migrateDualYieldTokens( - Address.Zero().bech32(), - Address.Zero().bech32(), - ); + const transactions = + await service.migrateTotalDualFarmTokenPosition( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); - expect(transaction).toEqual(expectedTransactionStub); + expect(transactions).toEqual([expectedTransactionStub]); }); it('migrate on both tokens', async () => { @@ -339,12 +341,13 @@ describe('StakingProxyTransactionService', () => { }, ]); - const transaction = await service.migrateDualYieldTokens( - Address.Zero().bech32(), - Address.Zero().bech32(), - ); + const transactions = + await service.migrateTotalDualFarmTokenPosition( + Address.Zero().bech32(), + Address.Zero().bech32(), + ); - expect(transaction).toEqual(expectedTransactionStub); + expect(transactions).toEqual([expectedTransactionStub]); }); }); }); diff --git a/src/modules/staking-proxy/staking.proxy.resolver.ts b/src/modules/staking-proxy/staking.proxy.resolver.ts index 558592369..137fe40ae 100644 --- a/src/modules/staking-proxy/staking.proxy.resolver.ts +++ b/src/modules/staking-proxy/staking.proxy.resolver.ts @@ -146,14 +146,22 @@ export class StakingProxyResolver { description: 'Update staking / farm positions for total farm position from dual yield token', }) - async migrateDualYieldTokens( - @Args('proxyStakingAddress', StakingProxyAddressValidationPipe) - proxyStakingAddress: string, + async migrateTotalDualFarmTokenPosition( + @Args( + 'dualFarmsAddresses', + { type: () => [String] }, + StakingProxyAddressValidationPipe, + ) + dualFarmsAddresses: string[], @AuthUser() user: UserAuthResult, - ): Promise { - return this.stakingProxyTransaction.migrateDualYieldTokens( - proxyStakingAddress, - user.address, + ): Promise { + const promises = dualFarmsAddresses.map((address) => + this.stakingProxyTransaction.migrateTotalDualFarmTokenPosition( + address, + user.address, + ), ); + + return (await Promise.all(promises)).flat(); } } diff --git a/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts b/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts index d3d0392a8..f728a174e 100644 --- a/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts +++ b/src/modules/staking-proxy/validators/staking.proxy.address.validator.ts @@ -7,17 +7,19 @@ import { Address } from '@multiversx/sdk-core/out'; export class StakingProxyAddressValidationPipe implements PipeTransform { constructor(private readonly remoteConfig: RemoteConfigGetterService) {} - async transform(value: string, metadata: ArgumentMetadata) { - let address: Address; - try { - address = Address.fromBech32(value); - } catch (error) { - throw new UserInputError('Invalid address'); - } - const stakingProxyAddresses = - await this.remoteConfig.getStakingProxyAddresses(); - if (!stakingProxyAddresses.includes(address.bech32())) { - throw new UserInputError('Invalid staking proxy address'); + async transform(value: string[], metadata: ArgumentMetadata) { + for (const item of value) { + let address: Address; + try { + address = Address.fromBech32(item); + } catch (error) { + throw new UserInputError('Invalid address'); + } + const stakingProxyAddresses = + await this.remoteConfig.getStakingProxyAddresses(); + if (!stakingProxyAddresses.includes(address.bech32())) { + throw new UserInputError('Invalid staking proxy address'); + } } return value; From 2299056d2dfa02c66646d1748e4e471d3e76a7e8 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 14 Mar 2024 12:21:38 +0200 Subject: [PATCH 145/313] MEX-408: fix position creator transactions unit tests Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 766f90608..f350b5c16 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -745,7 +745,7 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), new EsdtTokenPayment({ - tokenIdentifier: 'METASTAKE-1234', + tokenIdentifier: 'METASTAKE-123456', tokenNonce: 1, amount: '100000000000000000000', }), @@ -783,7 +783,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@47008144020574367766@47008144020574367766823', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@47008144020574367766@47008144020574367766823', ), chainID: 'T', version: 1, @@ -818,7 +818,7 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), new EsdtTokenPayment({ - tokenIdentifier: 'METASTAKE-1234', + tokenIdentifier: 'METASTAKE-123456', tokenNonce: 1, amount: '100000000000000000000', }), @@ -838,7 +838,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), chainID: 'T', version: 1, @@ -873,7 +873,7 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), new EsdtTokenPayment({ - tokenIdentifier: 'METASTAKE-1234', + tokenIdentifier: 'METASTAKE-123456', tokenNonce: 1, amount: '100000000000000000000', }), @@ -893,7 +893,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.singleToken.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@EGLDMEXLP-abcdef@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@@', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@EGLDMEXLP-abcdef@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@@', ), chainID: 'T', version: 1, @@ -1750,7 +1750,7 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), new EsdtTokenPayment({ - tokenIdentifier: 'METASTAKE-1234', + tokenIdentifier: 'METASTAKE-123456', tokenNonce: 1, amount: '100000000000000000000', }), @@ -1788,7 +1788,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.dualTokens.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', ), chainID: 'T', version: 1, @@ -1828,7 +1828,7 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), new EsdtTokenPayment({ - tokenIdentifier: 'METASTAKE-1234', + tokenIdentifier: 'METASTAKE-123456', tokenNonce: 1, amount: '100000000000000000000', }), @@ -1848,7 +1848,7 @@ describe('PositionCreatorTransaction', () => { gasLimit: gasConfig.positionCreator.dualTokens.dualFarmPosition, data: encodeTransactionData( - 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-1234@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', + 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@03@WEGLD-123456@@100000000000000000000@MEX-123456@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromTwoTokens@0000000000000000000000000000000000000000000000000000000000000000@99000000000000000000@99000000000000000000', ), chainID: 'T', version: 1, From 7c2276acb56aae4d683d9a7a9d558a13fffc4dde Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 1 Apr 2024 17:20:47 +0300 Subject: [PATCH 146/313] SERVICES-2247: new devnet setup for xExchange v3 Signed-off-by: Claudiu Lataretu --- src/config/devnet.json | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/config/devnet.json b/src/config/devnet.json index effe0ea5b..d4cafc147 100644 --- a/src/config/devnet.json +++ b/src/config/devnet.json @@ -9,7 +9,7 @@ "distributionAddress": "erd1qqqqqqqqqqqqqpgqyg62a3tswuzun2wzp8a83sjkc709wwjt2jpssphddm", "proxyDexAddress": { "v1": "erd1qqqqqqqqqqqqqpgqj45ksc9zln3ss9r46qn0uxvmavyqxm2e0n4smptzsg", - "v2": "erd1qqqqqqqqqqqqqpgqxtujvf7w6rag29f39f7yrqsuv09m232r0n4sf7qs79" + "v2": "erd1qqqqqqqqqqqqqpgqp8c763u69ctvshmjtp06qzzcwmleqcpu0n4s2e893l" }, "lockedAssetAddress": "erd1qqqqqqqqqqqqqpgqxsy4k8fkqvllcvyrwhf80t97kyd32qfx0n4stue7cn", "metabondingStakingAddress": "erd1qqqqqqqqqqqqqpgq4jpq0azcwlmcdluzv5ra8ev4vts8ht090n4suseqfa", @@ -23,12 +23,15 @@ }, "priceDiscovery": [ ], - "simpleLockEnergy": "erd1qqqqqqqqqqqqqpgqs7qnuvj2t0m40xmgrrh88wc7qdx286c60n4spvrzzk", - "feesCollector": "erd1qqqqqqqqqqqqqpgqw88ux2l44eufvwz2uhvduhq03g8pxc4j0n4s0frzjz", + "simpleLockEnergy": "erd1qqqqqqqqqqqqqpgqfsdhdyyuyxlflc43axlvderg84ry7cfr0n4sf0nw6v", + "feesCollector": "erd1qqqqqqqqqqqqqpgquq94exc0fs6x8tvzyzsmj2v643vmclct0n4shkztah", "energyUpdate": "erd1qqqqqqqqqqqqqpgqz2ctz77j9we0r99mnhehv24he3pxmsmq0n4sntf4n7", - "tokenUnstake": "erd1qqqqqqqqqqqqqpgqu6s4e5mndgf37psxw9mdlmjavp2er3f00n4snwyk3q", - "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqasr2asq07e6ur274eecx3vd0pnej2vxs0n4sqqyp52", - "escrow": "erd1qqqqqqqqqqqqqpgqnx4t9kwy5n9atuumvnrluv5yrwmpxzx60n4sj497ms" + "tokenUnstake": "erd1qqqqqqqqqqqqqpgqnp7xfy65s6q9y3ud9x7rmc69f07789wq0n4s535697", + "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgqn27jupfvzms4ret4cft0p29jx6s5ch2f0n4sfps3as", + "escrow": "erd1qqqqqqqqqqqqqpgqjtuw2ss3cgdsa99z6ggtclzplt4v3f0c0n4satu8aw", + "positionCreator": "erd1qqqqqqqqqqqqqpgqysngv4llcx056rqjjj7m9d0aum2yjphe0n4stlxu35", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqj60zvv9xp32afuer0kjwejs7qneqsp520n4slk5tnf", + "composableTasks": "erd1qqqqqqqqqqqqqpgqepsrmke5dwvf8098dv5xyzhtlhsdmgp60n4sgk7gu2" }, "governance": { "oldEnergy": { @@ -60,13 +63,9 @@ "unlockedRewards": [ ], "lockedRewards": [ - "erd1qqqqqqqqqqqqqpgq3chrzjdg3gu40zt6kes6w62cyz8tes6k0n4ssk3hj4", - "erd1qqqqqqqqqqqqqpgqalcjd05y3uazuw79k4vdnheq825x8e400n4s09ymcr", - "erd1qqqqqqqqqqqqqpgqyd7uaxe9sctfxsev0u3mntekxqlu6ntt0n4s8a6thz", - "erd1qqqqqqqqqqqqqpgqtmg6xzef8k6z4885qhehgvu4zlf3g5zf0n4snencta", - "erd1qqqqqqqqqqqqqpgq7mq58gjqzawtzy9y5v2rawpawx03a6ny0n4sv5mzr6", - "erd1qqqqqqqqqqqqqpgqauyawadp2eex5kd30z5ehrrprgksjpyp0n4s00cy97", - "erd1qqqqqqqqqqqqqpgqlsq7r58fn05emy66456hunfvlm03vzh00n4s5pqfqc" + "erd1qqqqqqqqqqqqqpgqqjpfmappkpzq6kdju3mkqepk3d2kcymu0n4srd45sa", + "erd1qqqqqqqqqqqqqpgqtukp628k04vg93peq8k3ngrh0k2rs3nm0n4sjkcz95", + "erd1qqqqqqqqqqqqqpgql7rysqgxxzhykg3j6vccnx4003z2mcl80n4ste3jh8" ] } }, From 7c971b090770682ed186079da98fabdf2cf867d3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 4 Apr 2024 12:43:18 +0300 Subject: [PATCH 147/313] MEX-414: update swap composable task to use specific swap endpoint Signed-off-by: Claudiu Lataretu --- src/abis/composable-tasks.abi.json | 34 +++++++++++-------- .../services/composable.tasks.transaction.ts | 8 +++-- .../services/pair.transactions.service.ts | 6 ++-- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/abis/composable-tasks.abi.json b/src/abis/composable-tasks.abi.json index c62ebddd4..70b76c9f2 100644 --- a/src/abis/composable-tasks.abi.json +++ b/src/abis/composable-tasks.abi.json @@ -1,11 +1,11 @@ { "buildInfo": { "rustc": { - "version": "1.71.0-nightly", - "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de", - "commitDate": "2023-05-25", + "version": "1.76.0-nightly", + "commitHash": "d86d65bbc19b928387f68427fcc3a0da498d8a19", + "commitDate": "2023-12-10", "channel": "Nightly", - "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)" + "short": "rustc 1.76.0-nightly (d86d65bbc 2023-12-10)" }, "contractCrate": { "name": "composable-tasks", @@ -13,7 +13,7 @@ }, "framework": { "name": "multiversx-sc", - "version": "0.44.0" + "version": "0.46.1" } }, "name": "ComposableTasksContract", @@ -22,6 +22,12 @@ "outputs": [] }, "endpoints": [ + { + "name": "upgrade", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, { "name": "composeTasks", "mutability": "mutable", @@ -30,7 +36,7 @@ ], "inputs": [ { - "name": "expected_token_out", + "name": "min_expected_token_out", "type": "EgldOrEsdtTokenPayment" }, { @@ -54,7 +60,7 @@ "outputs": [] }, { - "name": "setrouterAddr", + "name": "setRouterAddr", "onlyOwner": true, "mutability": "mutable", "inputs": [ @@ -66,9 +72,8 @@ "outputs": [] }, { - "name": "setPairAddrForTokens", - "onlyOwner": true, - "mutability": "mutable", + "name": "getPair", + "mutability": "readonly", "inputs": [ { "name": "first_token_id", @@ -77,16 +82,15 @@ { "name": "second_token_id", "type": "TokenIdentifier" - }, + } + ], + "outputs": [ { - "name": "new_addr", "type": "Address" } - ], - "outputs": [] + ] } ], - "events": [], "esdtAttributes": [], "hasCallback": false, "types": { diff --git a/src/modules/composable-tasks/services/composable.tasks.transaction.ts b/src/modules/composable-tasks/services/composable.tasks.transaction.ts index d4c63d940..33f5291da 100644 --- a/src/modules/composable-tasks/services/composable.tasks.transaction.ts +++ b/src/modules/composable-tasks/services/composable.tasks.transaction.ts @@ -83,10 +83,11 @@ export class ComposableTasksTransactionService { return interaction.buildTransaction().toPlainObject(); } - async wrapEgldAndSwapFixedInputTransaction( + async wrapEgldAndSwapTransaction( value: string, tokenOutID: string, tokenOutAmountMin: string, + swapEndpoint: string, ): Promise { const wrapTask: ComposableTask = { type: ComposableTaskType.WRAP_EGLD, @@ -96,6 +97,7 @@ export class ComposableTasksTransactionService { const swapTask: ComposableTask = { type: ComposableTaskType.SWAP, arguments: [ + new BytesValue(Buffer.from(swapEndpoint, 'utf-8')), new BytesValue(Buffer.from(tokenOutID, 'utf-8')), new BytesValue( Buffer.from( @@ -120,15 +122,17 @@ export class ComposableTasksTransactionService { ); } - async swapFixedInputAndUnwrapEgldTransaction( + async swapAndUnwrapEgldTransaction( payment: EsdtTokenPayment, minimumValue: string, + swapEndpoint: string, ): Promise { const wrappedEgldTokenID = await this.wrapAbi.wrappedEgldTokenID(); const swapTask: ComposableTask = { type: ComposableTaskType.SWAP, arguments: [ + new BytesValue(Buffer.from(swapEndpoint, 'utf-8')), new BytesValue(Buffer.from(wrappedEgldTokenID, 'utf-8')), new BytesValue( Buffer.from( diff --git a/src/modules/pair/services/pair.transactions.service.ts b/src/modules/pair/services/pair.transactions.service.ts index 9ad582dc3..ef102dd83 100644 --- a/src/modules/pair/services/pair.transactions.service.ts +++ b/src/modules/pair/services/pair.transactions.service.ts @@ -289,21 +289,23 @@ export class PairTransactionService { .integerValue(); if (args.tokenInID === mxConfig.EGLDIdentifier) { - return this.composableTasksTransaction.wrapEgldAndSwapFixedInputTransaction( + return this.composableTasksTransaction.wrapEgldAndSwapTransaction( args.amountIn, args.tokenOutID, amountOutMin.toFixed(), + 'swapTokensFixedInput', ); } if (args.tokenOutID === mxConfig.EGLDIdentifier) { - return this.composableTasksTransaction.swapFixedInputAndUnwrapEgldTransaction( + return this.composableTasksTransaction.swapAndUnwrapEgldTransaction( new EsdtTokenPayment({ tokenIdentifier: args.tokenInID, tokenNonce: 0, amount: args.amountIn, }), amountOutMin.toFixed(), + 'swapTokensFixedInput', ); } From 60bcb8e97cc842d3d4fd4852a5a79e69b8f904e4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 4 Apr 2024 12:44:34 +0300 Subject: [PATCH 148/313] MEX-414: add swap fixed output composable task Signed-off-by: Claudiu Lataretu --- .../services/auto-router.service.ts | 23 ++-- src/modules/pair/pair.resolver.ts | 4 +- .../services/pair.transactions.service.ts | 127 ++++++------------ 3 files changed, 56 insertions(+), 98 deletions(-) diff --git a/src/modules/auto-router/services/auto-router.service.ts b/src/modules/auto-router/services/auto-router.service.ts index 06aab0843..c7c675031 100644 --- a/src/modules/auto-router/services/auto-router.service.ts +++ b/src/modules/auto-router/services/auto-router.service.ts @@ -469,16 +469,19 @@ export class AutoRouterService { return [transaction]; } - return await this.pairTransactionService.swapTokensFixedOutput( - sender, - { - pairAddress: parent.pairs[0].address, - tokenInID: parent.tokenInID, - tokenOutID: parent.tokenOutID, - amountIn: parent.amountIn, - amountOut: parent.amountOut, - }, - ); + const transaction = + await this.pairTransactionService.swapTokensFixedOutput( + sender, + { + pairAddress: parent.pairs[0].address, + tokenInID: parent.tokenInID, + tokenOutID: parent.tokenOutID, + amountIn: parent.amountIn, + amountOut: parent.amountOut, + }, + ); + + return [transaction]; } if ( diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 7d9151a7b..2cfa76897 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -329,11 +329,11 @@ export class PairResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => [TransactionModel]) + @Query(() => TransactionModel) async swapTokensFixedOutput( @Args() args: SwapTokensFixedOutputArgs, @AuthUser() user: UserAuthResult, - ): Promise { + ): Promise { return this.transactionService.swapTokensFixedOutput( user.address, args, diff --git a/src/modules/pair/services/pair.transactions.service.ts b/src/modules/pair/services/pair.transactions.service.ts index ef102dd83..5f32ad75f 100644 --- a/src/modules/pair/services/pair.transactions.service.ts +++ b/src/modules/pair/services/pair.transactions.service.ts @@ -342,7 +342,7 @@ export class PairTransactionService { async swapTokensFixedOutput( sender: string, args: SwapTokensFixedOutputArgs, - ): Promise { + ): Promise { await this.validateTokens(args.pairAddress, [ new InputTokenModel({ tokenID: args.tokenInID, @@ -354,100 +354,55 @@ export class PairTransactionService { }), ]); - const transactions: TransactionModel[] = []; - let endpointArgs: TypedValue[]; - const [wrappedTokenID, contract, trustedSwapPairs] = await Promise.all([ - this.wrapAbi.wrappedEgldTokenID(), + const amountIn = new BigNumber(args.amountIn); + const amountOut = new BigNumber(args.amountOut); + + if (args.tokenInID === mxConfig.EGLDIdentifier) { + return this.composableTasksTransaction.wrapEgldAndSwapTransaction( + args.amountIn, + args.tokenOutID, + args.amountOut, + 'swapTokensFixedOutput', + ); + } + + if (args.tokenOutID === mxConfig.EGLDIdentifier) { + return this.composableTasksTransaction.swapAndUnwrapEgldTransaction( + new EsdtTokenPayment({ + tokenIdentifier: args.tokenInID, + tokenNonce: 0, + amount: args.amountIn, + }), + args.amountOut, + 'swapTokensFixedOutput', + ); + } + + const [contract, trustedSwapPairs] = await Promise.all([ this.mxProxy.getPairSmartContract(args.pairAddress), this.pairAbi.trustedSwapPairs(args.pairAddress), ]); - const amountIn = new BigNumber(args.amountIn); - const amountOut = new BigNumber(args.amountOut); - const gasLimit = trustedSwapPairs.length === 0 ? gasConfig.pairs.swapTokensFixedOutput.default : gasConfig.pairs.swapTokensFixedOutput.withFeeSwap; - switch (mxConfig.EGLDIdentifier) { - case args.tokenInID: - transactions.push( - await this.wrapTransaction.wrapEgld( - sender, - amountIn.toString(), - ), - ); - - endpointArgs = [ - BytesValue.fromUTF8(args.tokenOutID), - new BigUIntValue(amountOut), - ]; - - transactions.push( - contract.methodsExplicit - .swapTokensFixedOutput(endpointArgs) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - wrappedTokenID, - new BigNumber(amountIn), - ), - ) - .withGasLimit(gasLimit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - break; - case args.tokenOutID: - endpointArgs = [ - BytesValue.fromUTF8(wrappedTokenID), - new BigUIntValue(amountOut), - ]; - transactions.push( - contract.methodsExplicit - .swapTokensFixedOutput(endpointArgs) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - args.tokenInID, - new BigNumber(amountIn), - ), - ) - .withGasLimit(gasLimit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - transactions.push( - await this.wrapTransaction.unwrapEgld( - sender, - args.amountOut, - ), - ); - break; - default: - endpointArgs = [ - BytesValue.fromUTF8(args.tokenOutID), - new BigUIntValue(amountOut), - ]; - - transactions.push( - contract.methodsExplicit - .swapTokensFixedOutput(endpointArgs) - .withSingleESDTTransfer( - TokenTransfer.fungibleFromBigInteger( - args.tokenInID, - new BigNumber(amountIn), - ), - ) - .withGasLimit(gasLimit) - .withChainID(mxConfig.chainID) - .buildTransaction() - .toPlainObject(), - ); - break; - } - return transactions; + return contract.methodsExplicit + .swapTokensFixedOutput([ + BytesValue.fromUTF8(args.tokenOutID), + new BigUIntValue(amountOut), + ]) + .withSingleESDTTransfer( + TokenTransfer.fungibleFromBigInteger( + args.tokenInID, + new BigNumber(amountIn), + ), + ) + .withGasLimit(gasLimit) + .withChainID(mxConfig.chainID) + .buildTransaction() + .toPlainObject(); } async validateTokens( From ca50a43cb755849749d171eac9e7c7581eff33e9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 4 Apr 2024 12:55:40 +0300 Subject: [PATCH 149/313] MEX-414: fix composable tasks unit tests to use endpoint argument on swap Signed-off-by: Claudiu Lataretu --- .../composable.tasks.transaction.spec.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts index 03d63c918..ec480aced 100644 --- a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts +++ b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts @@ -11,6 +11,7 @@ import { EgldOrEsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; import { ComposableTaskType } from '../models/composable.tasks.model'; import { Address } from '@multiversx/sdk-core/out'; import { gasConfig } from 'src/config'; +import { encodeTransactionData } from 'src/helpers/helpers'; describe('Composable Tasks Transaction', () => { let module: TestingModule; @@ -90,15 +91,17 @@ describe('Composable Tasks Transaction', () => { ComposableTasksTransactionService, ); - const transaction = await service.wrapEgldAndSwapFixedInputTransaction( + const transaction = await service.wrapEgldAndSwapTransaction( '1000000000000000000', 'USDC-123456', '20000000', + 'swapTokensFixedInput', ); expect(transaction).toEqual({ chainID: 'T', - data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAxMzEyZDAwQEBAMDJAMDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDQwMTMxMmQwMA==', + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAxMzEyZDAwQEBAMDJAMDAwMDAwMTQ3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0OTZlNzA3NTc0MDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDQwMTMxMmQwMA==', + gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, @@ -120,19 +123,19 @@ describe('Composable Tasks Transaction', () => { ComposableTasksTransactionService, ); - const transaction = - await service.swapFixedInputAndUnwrapEgldTransaction( - new EsdtTokenPayment({ - tokenIdentifier: 'USDC-123456', - tokenNonce: 0, - amount: '20000000', - }), - '1000000000000000000', - ); + const transaction = await service.swapAndUnwrapEgldTransaction( + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '20000000', + }), + '1000000000000000000', + 'swapTokensFixedInput', + ); expect(transaction).toEqual({ chainID: 'T', - data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDEzMTJkMDBANjM2ZjZkNzA2ZjczNjU1NDYxNzM2YjczQDAwMDAwMDA0NDU0NzRjNDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAyQDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAxQA==', + data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDEzMTJkMDBANjM2ZjZkNzA2ZjczNjU1NDYxNzM2YjczQDAwMDAwMDA0NDU0NzRjNDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAyQDAwMDAwMDE0NzM3NzYxNzA1NDZmNmI2NTZlNzM0NjY5Nzg2NTY0NDk2ZTcwNzU3NDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAxQA==', gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, From e53343264e31a4efb9ecaefd25f9829d08a3e83d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 4 Apr 2024 13:23:15 +0300 Subject: [PATCH 150/313] MEX-414: fix pair and auto-router unit tests for composable tasks Signed-off-by: Claudiu Lataretu --- .../specs/auto-router.service.spec.ts | 2 +- .../specs/pair.transactions.service.spec.ts | 64 ++++++------------- 2 files changed, 20 insertions(+), 46 deletions(-) diff --git a/src/modules/auto-router/specs/auto-router.service.spec.ts b/src/modules/auto-router/specs/auto-router.service.spec.ts index 05fcead05..f7b6499d7 100644 --- a/src/modules/auto-router/specs/auto-router.service.spec.ts +++ b/src/modules/auto-router/specs/auto-router.service.spec.ts @@ -281,7 +281,7 @@ describe('AutoRouterService', () => { senderUsername: undefined, gasPrice: 1000000000, gasLimit: gasConfig.composableTasks.default, - data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNzExNzJhY2UwMjZiMGM0QEBAMDJAMDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDcxMTcyYWNlMDI2YjBjNA==', + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNzExNzJhY2UwMjZiMGM0QEBAMDJAMDAwMDAwMTQ3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0OTZlNzA3NTc0MDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDcxMTcyYWNlMDI2YjBjNA==', chainID: 'T', version: 1, options: undefined, diff --git a/src/modules/pair/specs/pair.transactions.service.spec.ts b/src/modules/pair/specs/pair.transactions.service.spec.ts index 31fdaaf9b..74c3825df 100644 --- a/src/modules/pair/specs/pair.transactions.service.spec.ts +++ b/src/modules/pair/specs/pair.transactions.service.spec.ts @@ -460,7 +460,7 @@ describe('TransactionPairService', () => { expect(transactions).toEqual({ chainID: 'T', - data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwNEBAQDAyQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMTA0', + data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwNEBAQDAyQDAwMDAwMDE0NzM3NzYxNzA1NDZmNmI2NTZlNzM0NjY5Nzg2NTY0NDk2ZTcwNzU3NDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMTA0', gasLimit: gasConfig.composableTasks.default, gasPrice: 1000000000, guardian: undefined, @@ -482,7 +482,7 @@ describe('TransactionPairService', () => { PairTransactionService, ); - const transactions = await service.swapTokensFixedOutput( + const transaction = await service.swapTokensFixedOutput( 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', { pairAddress: Address.fromHex( @@ -495,49 +495,23 @@ describe('TransactionPairService', () => { }, ); - expect(transactions).toEqual([ - { - nonce: 0, - value: '0', - receiver: Address.fromHex( - '0000000000000000000000000000000000000000000000000000000000000012', - ).bech32(), - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.pairs.swapTokensFixedOutput.default, - data: encodeTransactionData( - 'ESDTTransfer@MEX-123456@05@swapTokensFixedOutput@WEGLD-123456@05', - ), - chainID: mxConfig.chainID, - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }, - { - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: gasConfig.wrapeGLD, - data: encodeTransactionData( - 'ESDTTransfer@WEGLD-123456@05@unwrapEgld', - ), - chainID: mxConfig.chainID, - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }, - ]); + expect(transaction).toEqual({ + nonce: 0, + value: '0', + receiver: Address.Zero().bech32(), + sender: '', + receiverUsername: undefined, + senderUsername: undefined, + gasPrice: 1000000000, + gasLimit: gasConfig.composableTasks.default, + data: 'RVNEVFRyYW5zZmVyQDRkNDU1ODJkMzEzMjMzMzQzNTM2QDA1QDYzNmY2ZDcwNmY3MzY1NTQ2MTczNmI3M0AwMDAwMDAwNDQ1NDc0YzQ0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDVAMDJAMDAwMDAwMTU3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0Zjc1NzQ3MDc1NzQwMDAwMDAwYzU3NDU0NzRjNDQyZDMxMzIzMzM0MzUzNjAwMDAwMDAxMDVAMDFA', + chainID: mxConfig.chainID, + version: 1, + options: undefined, + signature: undefined, + guardian: undefined, + guardianSignature: undefined, + }); }); it('should validate tokens', async () => { From a6ed6ef13c541cca90feb80bdef7a2d643c6b3b9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 5 Apr 2024 16:20:13 +0300 Subject: [PATCH 151/313] MEX-466: split AutoRouteModel into base SwapRoutModel and specialized AutoRouteModel Signed-off-by: Claudiu Lataretu --- src/modules/auto-router/auto-router.module.ts | 3 +- .../auto-router/auto-router.resolver.ts | 49 ++++++++++--------- .../auto-router/models/auto-route.model.ts | 11 ++++- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/modules/auto-router/auto-router.module.ts b/src/modules/auto-router/auto-router.module.ts index 5b984568d..09a593fd2 100644 --- a/src/modules/auto-router/auto-router.module.ts +++ b/src/modules/auto-router/auto-router.module.ts @@ -9,7 +9,7 @@ import { AutoRouterComputeService } from './services/auto-router.compute.service import { WrappingModule } from '../wrapping/wrap.module'; import { RouterModule } from '../router/router.module'; import { AutoRouterTransactionService } from './services/auto-router.transactions.service'; -import { AutoRouterResolver } from './auto-router.resolver'; +import { AutoRouterResolver, SwapRouteResolver } from './auto-router.resolver'; import { PairTransactionService } from '../pair/services/pair.transactions.service'; import { RemoteConfigModule } from '../remote-config/remote-config.module'; import { TokenModule } from '../tokens/token.module'; @@ -29,6 +29,7 @@ import { ComposableTasksModule } from '../composable-tasks/composable.tasks.modu RemoteConfigModule, ], providers: [ + SwapRouteResolver, AutoRouterResolver, AutoRouterService, AutoRouterComputeService, diff --git a/src/modules/auto-router/auto-router.resolver.ts b/src/modules/auto-router/auto-router.resolver.ts index 74b476461..6c45ec07b 100644 --- a/src/modules/auto-router/auto-router.resolver.ts +++ b/src/modules/auto-router/auto-router.resolver.ts @@ -1,7 +1,7 @@ import { Resolver, Query, ResolveField, Args, Parent } from '@nestjs/graphql'; import { AutoRouterService } from '../auto-router/services/auto-router.service'; import { AutoRouterArgs } from '../auto-router/models/auto-router.args'; -import { AutoRouteModel } from './models/auto-route.model'; +import { AutoRouteModel, SwapRouteModel } from './models/auto-route.model'; import { TransactionModel } from 'src/models/transaction.model'; import { UseGuards } from '@nestjs/common'; import { AuthUser } from '../auth/auth.user'; @@ -10,10 +10,33 @@ import { JwtOrNativeAuthGuard } from '../auth/jwt.or.native.auth.guard'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; -@Resolver(() => AutoRouteModel) -export class AutoRouterResolver { - constructor(private readonly autoRouterService: AutoRouterService) {} +@Resolver(() => SwapRouteModel) +export class SwapRouteResolver { + constructor(protected readonly autoRouterService: AutoRouterService) {} + + @ResolveField(() => [String]) + fees(@Parent() parent: AutoRouteModel): string[] { + const fees = this.autoRouterService.getFeesDenom( + parent.intermediaryAmounts, + parent.tokenRoute, + parent.pairs, + ); + + return fees; + } + + @ResolveField(() => [String]) + pricesImpact(@Parent() parent: AutoRouteModel): string[] { + return this.autoRouterService.getPriceImpactPercents( + parent.intermediaryAmounts, + parent.tokenRoute, + parent.pairs, + ); + } +} +@Resolver(() => AutoRouteModel) +export class AutoRouterResolver extends SwapRouteResolver { @Query(() => AutoRouteModel) async swap(@Args() args: AutoRouterArgs): Promise { try { @@ -34,24 +57,6 @@ export class AutoRouterResolver { } } - @ResolveField(() => [String]) - fees(@Parent() parent: AutoRouteModel): string[] { - return this.autoRouterService.getFeesDenom( - parent.intermediaryAmounts, - parent.tokenRoute, - parent.pairs, - ); - } - - @ResolveField(() => [String]) - pricesImpact(@Parent() parent: AutoRouteModel): string[] { - return this.autoRouterService.getPriceImpactPercents( - parent.intermediaryAmounts, - parent.tokenRoute, - parent.pairs, - ); - } - @UseGuards(JwtOrNativeAuthGuard) @ResolveField(() => [TransactionModel]) async transactions( diff --git a/src/modules/auto-router/models/auto-route.model.ts b/src/modules/auto-router/models/auto-route.model.ts index 0619aae02..0ed98e430 100644 --- a/src/modules/auto-router/models/auto-route.model.ts +++ b/src/modules/auto-router/models/auto-route.model.ts @@ -1,8 +1,9 @@ import { ObjectType, Field } from '@nestjs/graphql'; import { TransactionModel } from 'src/models/transaction.model'; import { PairModel } from 'src/modules/pair/models/pair.model'; + @ObjectType() -export class AutoRouteModel { +export class SwapRouteModel { @Field() swapType: SWAP_TYPE; @@ -60,6 +61,13 @@ export class AutoRouteModel { @Field() tolerance: number; + constructor(init?: Partial) { + Object.assign(this, init); + } +} + +@ObjectType() +export class AutoRouteModel extends SwapRouteModel { @Field(() => [TransactionModel], { nullable: true }) transactions: TransactionModel[]; @@ -67,6 +75,7 @@ export class AutoRouteModel { noAuthTransactions: TransactionModel[]; constructor(init?: Partial) { + super(init); Object.assign(this, init); } } From 84de90849ea0155f0a20e107547d55c91743f04b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 5 Apr 2024 16:23:41 +0300 Subject: [PATCH 152/313] MEX-466: include detailed information about position creator actions Signed-off-by: Claudiu Lataretu --- .../models/position.creator.model.ts | 11 ++ .../position.creator.transaction.resolver.ts | 13 ++- .../services/position.creator.compute.ts | 109 +++++++++++++----- .../services/position.creator.transaction.ts | 76 ++++++++++-- 4 files changed, 162 insertions(+), 47 deletions(-) diff --git a/src/modules/position-creator/models/position.creator.model.ts b/src/modules/position-creator/models/position.creator.model.ts index bb01757e1..68c02519f 100644 --- a/src/modules/position-creator/models/position.creator.model.ts +++ b/src/modules/position-creator/models/position.creator.model.ts @@ -1,4 +1,6 @@ import { Field, ObjectType } from '@nestjs/graphql'; +import { TransactionModel } from 'src/models/transaction.model'; +import { SwapRouteModel } from 'src/modules/auto-router/models/auto-route.model'; @ObjectType() export class PositionCreatorModel { @@ -9,3 +11,12 @@ export class PositionCreatorModel { Object.assign(this, init); } } + +@ObjectType() +export class PositionCreatorTransactionModel { + @Field(() => [SwapRouteModel]) + swaps: SwapRouteModel[]; + + @Field(() => [TransactionModel], { nullable: true }) + transactions: TransactionModel[]; +} diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index c9d722021..4837520f3 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -7,6 +7,7 @@ import { PositionCreatorTransactionService } from './services/position.creator.t import { InputTokenModel } from 'src/models/inputToken.model'; import { UserAuthResult } from '../auth/user.auth.result'; import { AuthUser } from '../auth/auth.user'; +import { PositionCreatorTransactionModel } from './models/position.creator.model'; @Resolver() @UseGuards(JwtOrNativeAuthGuard) @@ -15,14 +16,14 @@ export class PositionCreatorTransactionResolver { private readonly posCreatorTransaction: PositionCreatorTransactionService, ) {} - @Query(() => TransactionModel) + @Query(() => PositionCreatorTransactionModel) async createPositionSingleToken( @AuthUser() user: UserAuthResult, @Args('pairAddress') pairAddress: string, @Args('payment') payment: InputTokenModel, @Args('tolerance') tolerance: number, @Args('lockEpochs', { nullable: true }) lockEpochs: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createLiquidityPositionSingleToken( user.address, pairAddress, @@ -36,7 +37,7 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => [TransactionModel]) + @Query(() => PositionCreatorTransactionModel) async createFarmPositionSingleToken( @AuthUser() user: UserAuthResult, @Args('farmAddress') farmAddress: string, @@ -44,7 +45,7 @@ export class PositionCreatorTransactionResolver { payments: InputTokenModel[], @Args('tolerance') tolerance: number, @Args('lockEpochs', { nullable: true }) lockEpochs: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createFarmPositionSingleToken( user.address, farmAddress, @@ -61,14 +62,14 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => [TransactionModel]) + @Query(() => PositionCreatorTransactionModel) async createDualFarmPositionSingleToken( @AuthUser() user: UserAuthResult, @Args('dualFarmAddress') dualFarmAddress: string, @Args('payments', { type: () => [InputTokenModel] }) payments: InputTokenModel[], @Args('tolerance') tolerance: number, - ): Promise { + ): Promise { return this.posCreatorTransaction.createDualFarmPositionSingleToken( user.address, dualFarmAddress, diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 75ab33f6d..2a412d03a 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -4,15 +4,20 @@ import { PerformanceProfiler } from '@multiversx/sdk-nestjs-monitoring'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; import { constantsConfig } from 'src/config'; -import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; +import { + SWAP_TYPE, + SwapRouteModel, +} from 'src/modules/auto-router/models/auto-route.model'; import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; +import { PairModel } from 'src/modules/pair/models/pair.model'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; +import { PairComputeService } from 'src/modules/pair/services/pair.compute.service'; import { PairService } from 'src/modules/pair/services/pair.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; export type PositionCreatorSingleTokenPairInput = { - swapRouteArgs: TypedValue[]; + swapRoutes: SwapRouteModel[]; amount0Min: BigNumber; amount1Min: BigNumber; }; @@ -27,6 +32,7 @@ export class PositionCreatorComputeService { constructor( private readonly pairAbi: PairAbiService, private readonly pairService: PairService, + private readonly pairCompute: PairComputeService, private readonly routerAbi: RouterAbiService, private readonly autoRouterService: AutoRouterService, private readonly autoRouterTransaction: AutoRouterTransactionService, @@ -59,33 +65,51 @@ export class PositionCreatorComputeService { const acceptedPairedTokensIDs = await this.routerAbi.commonTokensForUserPairs(); - const [firstTokenID, secondTokenID, lpTokenID] = await Promise.all([ - this.pairAbi.firstTokenID(pairAddress), - this.pairAbi.secondTokenID(pairAddress), + const [ + firstToken, + secondToken, + lpTokenID, + firstTokenPriceUSD, + secondTokenPriceUSD, + reserves, + totalFeePercent, + ] = await Promise.all([ + this.pairService.getFirstToken(pairAddress), + this.pairService.getSecondToken(pairAddress), this.pairAbi.lpTokenID(pairAddress), + this.pairCompute.firstTokenPriceUSD(pairAddress), + this.pairCompute.secondTokenPriceUSD(pairAddress), + this.pairAbi.pairInfoMetadata(pairAddress), + this.pairAbi.totalFeePercent(pairAddress), ]); if (payment.tokenIdentifier === lpTokenID) { return { - swapRouteArgs: [], + swapRoutes: [], amount0Min: new BigNumber(0), amount1Min: new BigNumber(0), }; } - const swapToTokenID = acceptedPairedTokensIDs.includes(firstTokenID) - ? firstTokenID - : secondTokenID; + const [tokenInID, tokenOutID] = acceptedPairedTokensIDs.includes( + firstToken.identifier, + ) + ? [firstToken.identifier, secondToken.identifier] + : [secondToken.identifier, firstToken.identifier]; const profiler = new PerformanceProfiler(); + const swapRoutes = []; + const swapRoute = await this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, amountIn: payment.amount, - tokenOutID: swapToTokenID, + tokenOutID: tokenInID, tolerance, }); + swapRoutes.push(swapRoute); + profiler.stop('swap route', true); const halfPayment = new BigNumber(swapRoute.amountOut) @@ -100,31 +124,67 @@ export class PositionCreatorComputeService { let [amount0, amount1] = await Promise.all([ await this.computeSwap( pairAddress, - swapRoute.tokenOutID, - firstTokenID, + tokenInID, + tokenInID, halfPayment, ), await this.computeSwap( pairAddress, - swapRoute.tokenOutID, - secondTokenID, + tokenInID, + tokenOutID, remainingPayment, ), ]); + swapRoutes.push( + new SwapRouteModel({ + swapType: SWAP_TYPE.fixedInput, + tokenInID, + tokenOutID, + amountIn: amount0.toFixed(), + amountOut: amount1.toFixed(), + tokenRoute: [tokenInID, tokenOutID], + pairs: [ + new PairModel({ + address: pairAddress, + firstToken, + secondToken, + info: reserves, + totalFeePercent, + }), + ], + intermediaryAmounts: [amount0.toFixed(), amount1.toFixed()], + tolerance: tolerance, + tokenInExchangeRate: new BigNumber(amount1) + .dividedBy(amount0) + .toFixed(), + tokenOutExchangeRate: new BigNumber(amount0) + .dividedBy(amount1) + .toFixed(), + tokenInPriceUSD: + tokenInID === firstToken.identifier + ? firstTokenPriceUSD + : secondTokenPriceUSD, + tokenOutPriceUSD: + tokenOutID === firstToken.identifier + ? firstTokenPriceUSD + : secondTokenPriceUSD, + }), + ); + amount0 = - swapToTokenID === firstTokenID + tokenInID === firstToken.identifier ? await this.pairService.getEquivalentForLiquidity( pairAddress, - secondTokenID, + secondToken.identifier, amount1.toFixed(), ) : amount0; amount1 = - swapToTokenID === secondTokenID + tokenInID === secondToken.identifier ? await this.pairService.getEquivalentForLiquidity( pairAddress, - firstTokenID, + firstToken.identifier, amount0.toFixed(), ) : amount1; @@ -132,19 +192,8 @@ export class PositionCreatorComputeService { const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); - const swapRouteArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoute.tokenInID, - tokenOutID: swapRoute.tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoute.pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoute.intermediaryAmounts, - tokenRoute: swapRoute.tokenRoute, - }); - return { - swapRouteArgs, + swapRoutes, amount0Min, amount1Min, }; diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index b106ca0c5..5b22af61d 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -26,6 +26,7 @@ import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.tran import { ProxyFarmAbiService } from 'src/modules/proxy/services/proxy-farm/proxy.farm.abi.service'; import { EnergyAbiService } from 'src/modules/energy/services/energy.abi.service'; import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; +import { PositionCreatorTransactionModel } from '../models/position.creator.model'; @Injectable() export class PositionCreatorTransactionService { @@ -52,7 +53,7 @@ export class PositionCreatorTransactionService { payment: EsdtTokenPayment, tolerance: number, lockEpochs?: number, - ): Promise { + ): Promise { const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( false, ); @@ -71,6 +72,20 @@ export class PositionCreatorTransactionService { tolerance, ); + const swapRouteArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: singleTokenPairInput.swapRoutes[0].tokenInID, + tokenOutID: singleTokenPairInput.swapRoutes[0].tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: singleTokenPairInput.swapRoutes[0].pairs.map( + (pair) => pair.address, + ), + intermediaryAmounts: + singleTokenPairInput.swapRoutes[0].intermediaryAmounts, + tokenRoute: singleTokenPairInput.swapRoutes[0].tokenRoute, + }); + const contract = lockEpochs ? await this.mxProxy.getLockedTokenPositionCreatorContract() : await this.mxProxy.getPostitionCreatorContract(); @@ -83,7 +98,7 @@ export class PositionCreatorTransactionService { new U64Value(new BigNumber(lockEpochs)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, + ...swapRouteArgs, ], ); } else { @@ -91,7 +106,7 @@ export class PositionCreatorTransactionService { new AddressValue(Address.fromBech32(pairAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, + ...swapRouteArgs, ]); } @@ -113,7 +128,12 @@ export class PositionCreatorTransactionService { ); } - return interaction.buildTransaction().toPlainObject(); + const transaction = interaction.buildTransaction().toPlainObject(); + + return { + swaps: singleTokenPairInput.swapRoutes, + transactions: [transaction], + }; } async createFarmPositionSingleToken( @@ -122,7 +142,7 @@ export class PositionCreatorTransactionService { payments: EsdtTokenPayment[], tolerance: number, lockEpochs?: number, - ): Promise { + ): Promise { const [pairAddress, farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = await Promise.all([ this.farmAbiV2.pairContractAddress(farmAddress), @@ -161,6 +181,20 @@ export class PositionCreatorTransactionService { tolerance, ); + const swapRouteArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: singleTokenPairInput[0].tokenInID, + tokenOutID: singleTokenPairInput[0].tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: singleTokenPairInput[0].pairs.map( + (pair) => pair.address, + ), + intermediaryAmounts: + singleTokenPairInput[0].intermediaryAmounts, + tokenRoute: singleTokenPairInput[0].tokenRoute, + }); + const contract = lockEpochs ? await this.mxProxy.getLockedTokenPositionCreatorContract() : await this.mxProxy.getPostitionCreatorContract(); @@ -170,13 +204,13 @@ export class PositionCreatorTransactionService { new U64Value(new BigNumber(lockEpochs)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, + ...swapRouteArgs, ] : [ new AddressValue(Address.fromBech32(farmAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, + ...swapRouteArgs, ]; let interaction = contract.methodsExplicit @@ -209,7 +243,10 @@ export class PositionCreatorTransactionService { } transactions.push(interaction.buildTransaction().toPlainObject()); - return transactions; + return { + swaps: singleTokenPairInput.swapRoutes, + transactions, + }; } async createDualFarmPositionSingleToken( @@ -217,7 +254,7 @@ export class PositionCreatorTransactionService { stakingProxyAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + ): Promise { const [ pairAddress, dualYieldTokenID, @@ -263,6 +300,20 @@ export class PositionCreatorTransactionService { tolerance, ); + const swapRouteArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: singleTokenPairInput[0].tokenInID, + tokenOutID: singleTokenPairInput[0].tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: singleTokenPairInput[0].pairs.map( + (pair) => pair.address, + ), + intermediaryAmounts: + singleTokenPairInput[0].intermediaryAmounts, + tokenRoute: singleTokenPairInput[0].tokenRoute, + }); + const contract = await this.mxProxy.getPostitionCreatorContract(); let interaction = contract.methodsExplicit @@ -270,7 +321,7 @@ export class PositionCreatorTransactionService { new AddressValue(Address.fromBech32(stakingProxyAddress)), new BigUIntValue(singleTokenPairInput.amount0Min), new BigUIntValue(singleTokenPairInput.amount1Min), - ...singleTokenPairInput.swapRouteArgs, + ...swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) .withGasLimit( @@ -302,7 +353,10 @@ export class PositionCreatorTransactionService { } transactions.push(interaction.buildTransaction().toPlainObject()); - return transactions; + return { + swaps: singleTokenPairInput.swapRoutes, + transactions, + }; } async createStakingPositionSingleToken( From 48616f36dfe6a4b9e5982a603715e103daf1128e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 11:45:53 +0300 Subject: [PATCH 153/313] MEX-466: refactor position creator transactions - add new models for each single token transaction model - add resolvers for each single token transaction model - remove authentification from the position creator queries Signed-off-by: Claudiu Lataretu --- .../models/position.creator.model.ts | 34 ++- .../position.creator.module.ts | 10 +- .../position.creator.transaction.resolver.ts | 231 ++++++++++++++---- .../services/position.creator.compute.ts | 42 +--- .../services/position.creator.transaction.ts | 182 ++++++++------ 5 files changed, 334 insertions(+), 165 deletions(-) diff --git a/src/modules/position-creator/models/position.creator.model.ts b/src/modules/position-creator/models/position.creator.model.ts index 68c02519f..f06cae51f 100644 --- a/src/modules/position-creator/models/position.creator.model.ts +++ b/src/modules/position-creator/models/position.creator.model.ts @@ -13,10 +13,40 @@ export class PositionCreatorModel { } @ObjectType() -export class PositionCreatorTransactionModel { +export class LiquidityPositionSingleTokenModel { @Field(() => [SwapRouteModel]) swaps: SwapRouteModel[]; @Field(() => [TransactionModel], { nullable: true }) - transactions: TransactionModel[]; + transactions?: TransactionModel[]; + + constructor(init: Partial) { + Object.assign(this, init); + } +} + +@ObjectType() +export class FarmPositionSingleTokenModel { + @Field(() => [SwapRouteModel]) + swaps: SwapRouteModel[]; + + @Field(() => [TransactionModel], { nullable: true }) + transactions?: TransactionModel[]; + + constructor(init: Partial) { + Object.assign(this, init); + } +} + +@ObjectType() +export class DualFarmPositionSingleTokenModel { + @Field(() => [SwapRouteModel]) + swaps: SwapRouteModel[]; + + @Field(() => [TransactionModel], { nullable: true }) + transactions?: TransactionModel[]; + + constructor(init: Partial) { + Object.assign(this, init); + } } diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index b68cecadb..4291598cf 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -10,7 +10,12 @@ import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; import { StakingProxyModule } from '../staking-proxy/staking.proxy.module'; import { StakingModule } from '../staking/staking.module'; import { TokenModule } from '../tokens/token.module'; -import { PositionCreatorTransactionResolver } from './position.creator.transaction.resolver'; +import { + DualFarmPositionSingleTokenResolver, + FarmPositionSingleTokenResolver, + LiquidityPositionSingleTokenResolver, + PositionCreatorTransactionResolver, +} from './position.creator.transaction.resolver'; import { WrappingModule } from '../wrapping/wrap.module'; import { ProxyFarmModule } from '../proxy/services/proxy-farm/proxy.farm.module'; import { EnergyModule } from '../energy/energy.module'; @@ -34,6 +39,9 @@ import { EnergyModule } from '../energy/energy.module'; PositionCreatorTransactionService, PositionCreatorResolver, PositionCreatorTransactionResolver, + FarmPositionSingleTokenResolver, + LiquidityPositionSingleTokenResolver, + DualFarmPositionSingleTokenResolver, ], exports: [], }) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 4837520f3..e531c5b94 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -1,5 +1,5 @@ import { UseGuards } from '@nestjs/common'; -import { Args, Query, Resolver } from '@nestjs/graphql'; +import { Args, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'; import { JwtOrNativeAuthGuard } from '../auth/jwt.or.native.auth.guard'; import { TransactionModel } from 'src/models/transaction.model'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; @@ -7,83 +7,210 @@ import { PositionCreatorTransactionService } from './services/position.creator.t import { InputTokenModel } from 'src/models/inputToken.model'; import { UserAuthResult } from '../auth/user.auth.result'; import { AuthUser } from '../auth/auth.user'; -import { PositionCreatorTransactionModel } from './models/position.creator.model'; +import { + DualFarmPositionSingleTokenModel, + FarmPositionSingleTokenModel, + LiquidityPositionSingleTokenModel, +} from './models/position.creator.model'; +import { PositionCreatorComputeService } from './services/position.creator.compute'; -@Resolver() -@UseGuards(JwtOrNativeAuthGuard) -export class PositionCreatorTransactionResolver { +@Resolver(() => LiquidityPositionSingleTokenModel) +export class LiquidityPositionSingleTokenResolver { constructor( private readonly posCreatorTransaction: PositionCreatorTransactionService, ) {} - @Query(() => PositionCreatorTransactionModel) - async createPositionSingleToken( + @ResolveField(() => [TransactionModel]) + @UseGuards(JwtOrNativeAuthGuard) + async transactions( @AuthUser() user: UserAuthResult, - @Args('pairAddress') pairAddress: string, - @Args('payment') payment: InputTokenModel, - @Args('tolerance') tolerance: number, + @Parent() parent: LiquidityPositionSingleTokenModel, @Args('lockEpochs', { nullable: true }) lockEpochs: number, - ): Promise { - return this.posCreatorTransaction.createLiquidityPositionSingleToken( - user.address, - pairAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - tolerance, - lockEpochs, - ); + ): Promise { + const pairAddress = + parent.swaps[parent.swaps.length - 1].pairs[0].address; + const payment = new EsdtTokenPayment({ + tokenIdentifier: parent.swaps[0].tokenInID, + tokenNonce: 0, + amount: parent.swaps[0].amountIn, + }); + + const transactions = + await this.posCreatorTransaction.createLiquidityPositionSingleToken( + user.address, + pairAddress, + payment, + parent.swaps[parent.swaps.length - 1].tolerance, + parent.swaps, + lockEpochs, + ); + + return transactions; } +} - @Query(() => PositionCreatorTransactionModel) - async createFarmPositionSingleToken( +@Resolver(() => FarmPositionSingleTokenModel) +export class FarmPositionSingleTokenResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + ) {} + + @ResolveField(() => [TransactionModel]) + @UseGuards(JwtOrNativeAuthGuard) + async transactions( @AuthUser() user: UserAuthResult, + @Parent() parent: FarmPositionSingleTokenModel, @Args('farmAddress') farmAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, + @Args('additionalPayments', { + type: () => [InputTokenModel], + nullable: true, + }) + additionalPayments: InputTokenModel[], @Args('lockEpochs', { nullable: true }) lockEpochs: number, - ): Promise { + ): Promise { + const firstPayment = new EsdtTokenPayment({ + tokenIdentifier: parent.swaps[0].tokenInID, + tokenNonce: 0, + amount: parent.swaps[0].amountIn, + }); + return this.posCreatorTransaction.createFarmPositionSingleToken( user.address, farmAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, + [ + firstPayment, + ...additionalPayments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + ], + parent.swaps[length - 1].tolerance, + parent.swaps, lockEpochs, ); } +} - @Query(() => PositionCreatorTransactionModel) - async createDualFarmPositionSingleToken( +@Resolver(() => DualFarmPositionSingleTokenModel) +export class DualFarmPositionSingleTokenResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + ) {} + + @ResolveField(() => [TransactionModel]) + @UseGuards(JwtOrNativeAuthGuard) + async transactions( @AuthUser() user: UserAuthResult, + @Parent() parent: DualFarmPositionSingleTokenModel, @Args('dualFarmAddress') dualFarmAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], - @Args('tolerance') tolerance: number, - ): Promise { + @Args('additionalPayments', { + type: () => [InputTokenModel], + nullable: true, + }) + additionalPayments: InputTokenModel[], + ): Promise { + const firstPayment = new EsdtTokenPayment({ + tokenIdentifier: parent.swaps[0].tokenInID, + tokenNonce: 0, + amount: parent.swaps[0].amountIn, + }); + return this.posCreatorTransaction.createDualFarmPositionSingleToken( user.address, dualFarmAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), - tolerance, + [ + firstPayment, + ...additionalPayments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + ], + parent.swaps[length - 1].tolerance, + parent.swaps, ); } +} + +@Resolver() +export class PositionCreatorTransactionResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + private readonly posCreatorCompute: PositionCreatorComputeService, + ) {} + + @Query(() => LiquidityPositionSingleTokenModel) + async createPositionSingleToken( + @Args('pairAddress') pairAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + const swapRoutes = + await this.posCreatorCompute.computeSingleTokenPairInput( + pairAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + + return new LiquidityPositionSingleTokenModel({ + swaps: swapRoutes, + }); + } + + @Query(() => FarmPositionSingleTokenModel) + async createFarmPositionSingleToken( + @Args('pairAddress') pairAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + const swapRoutes = + await this.posCreatorCompute.computeSingleTokenPairInput( + pairAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + + return new FarmPositionSingleTokenModel({ + swaps: swapRoutes, + }); + } + + @Query(() => DualFarmPositionSingleTokenModel) + async createDualFarmPositionSingleToken( + @Args('pairAddress') pairAddress: string, + @Args('payment') payment: InputTokenModel, + @Args('tolerance') tolerance: number, + ): Promise { + const swapRoutes = + await this.posCreatorCompute.computeSingleTokenPairInput( + pairAddress, + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + tolerance, + ); + + return new DualFarmPositionSingleTokenModel({ + swaps: swapRoutes, + }); + } @Query(() => [TransactionModel]) async createStakingPositionSingleToken( diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 2a412d03a..6bbcb418b 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -16,12 +16,6 @@ import { PairComputeService } from 'src/modules/pair/services/pair.compute.servi import { PairService } from 'src/modules/pair/services/pair.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; -export type PositionCreatorSingleTokenPairInput = { - swapRoutes: SwapRouteModel[]; - amount0Min: BigNumber; - amount1Min: BigNumber; -}; - export type PositionCreatorSingleTokenInput = { swapRouteArgs: TypedValue[]; amountOutMin: BigNumber; @@ -61,7 +55,7 @@ export class PositionCreatorComputeService { pairAddress: string, payment: EsdtTokenPayment, tolerance: number, - ): Promise { + ): Promise { const acceptedPairedTokensIDs = await this.routerAbi.commonTokensForUserPairs(); @@ -84,11 +78,7 @@ export class PositionCreatorComputeService { ]); if (payment.tokenIdentifier === lpTokenID) { - return { - swapRoutes: [], - amount0Min: new BigNumber(0), - amount1Min: new BigNumber(0), - }; + return []; } const [tokenInID, tokenOutID] = acceptedPairedTokensIDs.includes( @@ -121,7 +111,7 @@ export class PositionCreatorComputeService { .minus(halfPayment) .toFixed(); - let [amount0, amount1] = await Promise.all([ + const [amount0, amount1] = await Promise.all([ await this.computeSwap( pairAddress, tokenInID, @@ -172,31 +162,7 @@ export class PositionCreatorComputeService { }), ); - amount0 = - tokenInID === firstToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - secondToken.identifier, - amount1.toFixed(), - ) - : amount0; - amount1 = - tokenInID === secondToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - firstToken.identifier, - amount0.toFixed(), - ) - : amount1; - - const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); - const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); - - return { - swapRoutes, - amount0Min, - amount1Min, - }; + return swapRoutes; } async computeSingleTokenInput( diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 5b22af61d..f69c35b81 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -19,14 +19,16 @@ import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.servi import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; -import { SWAP_TYPE } from 'src/modules/auto-router/models/auto-route.model'; +import { + SWAP_TYPE, + SwapRouteModel, +} from 'src/modules/auto-router/models/auto-route.model'; import { PairService } from 'src/modules/pair/services/pair.service'; import { TokenService } from 'src/modules/tokens/services/token.service'; import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.transactions.service'; import { ProxyFarmAbiService } from 'src/modules/proxy/services/proxy-farm/proxy.farm.abi.service'; import { EnergyAbiService } from 'src/modules/energy/services/energy.abi.service'; import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; -import { PositionCreatorTransactionModel } from '../models/position.creator.model'; @Injectable() export class PositionCreatorTransactionService { @@ -52,8 +54,9 @@ export class PositionCreatorTransactionService { pairAddress: string, payment: EsdtTokenPayment, tolerance: number, + swapRoutes: SwapRouteModel[], lockEpochs?: number, - ): Promise { + ): Promise { const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( false, ); @@ -65,27 +68,41 @@ export class PositionCreatorTransactionService { throw new Error('Invalid ESDT token payment'); } - const singleTokenPairInput = - await this.posCreatorCompute.computeSingleTokenPairInput( - pairAddress, - payment, - tolerance, - ); - const swapRouteArgs = this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: singleTokenPairInput.swapRoutes[0].tokenInID, - tokenOutID: singleTokenPairInput.swapRoutes[0].tokenOutID, + tokenInID: swapRoutes[0].tokenInID, + tokenOutID: swapRoutes[0].tokenOutID, swapType: SWAP_TYPE.fixedInput, tolerance, - addressRoute: singleTokenPairInput.swapRoutes[0].pairs.map( - (pair) => pair.address, - ), - intermediaryAmounts: - singleTokenPairInput.swapRoutes[0].intermediaryAmounts, - tokenRoute: singleTokenPairInput.swapRoutes[0].tokenRoute, + addressRoute: swapRoutes[0].pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoutes[0].intermediaryAmounts, + tokenRoute: swapRoutes[0].tokenRoute, }); + const amount0 = + swapRoutes[swapRoutes.length - 1].tokenInID === + swapRoutes[swapRoutes.length - 1].pairs[0].firstToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + swapRoutes[swapRoutes.length - 1].pairs[0].secondToken + .identifier, + swapRoutes[swapRoutes.length - 1].amountIn, + ) + : new BigNumber(swapRoutes[swapRoutes.length - 1].amountOut); + const amount1 = + swapRoutes[swapRoutes.length - 1].tokenInID === + swapRoutes[swapRoutes.length - 1].pairs[0].secondToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + swapRoutes[swapRoutes.length - 1].pairs[0].firstToken + .identifier, + swapRoutes[swapRoutes.length - 1].amountOut, + ) + : new BigNumber(swapRoutes[swapRoutes.length - 1].amountIn); + + const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); + const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); + const contract = lockEpochs ? await this.mxProxy.getLockedTokenPositionCreatorContract() : await this.mxProxy.getPostitionCreatorContract(); @@ -96,16 +113,16 @@ export class PositionCreatorTransactionService { interaction = contract.methodsExplicit.createPairPosFromSingleToken( [ new U64Value(new BigNumber(lockEpochs)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), ...swapRouteArgs, ], ); } else { interaction = contract.methodsExplicit.createLpPosFromSingleToken([ new AddressValue(Address.fromBech32(pairAddress)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), ...swapRouteArgs, ]); } @@ -130,10 +147,7 @@ export class PositionCreatorTransactionService { const transaction = interaction.buildTransaction().toPlainObject(); - return { - swaps: singleTokenPairInput.swapRoutes, - transactions: [transaction], - }; + return [transaction]; } async createFarmPositionSingleToken( @@ -141,8 +155,9 @@ export class PositionCreatorTransactionService { farmAddress: string, payments: EsdtTokenPayment[], tolerance: number, + swapRoutes: SwapRouteModel[], lockEpochs?: number, - ): Promise { + ): Promise { const [pairAddress, farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = await Promise.all([ this.farmAbiV2.pairContractAddress(farmAddress), @@ -174,27 +189,41 @@ export class PositionCreatorTransactionService { ); } - const singleTokenPairInput = - await this.posCreatorCompute.computeSingleTokenPairInput( - pairAddress, - payments[0], - tolerance, - ); - const swapRouteArgs = this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: singleTokenPairInput[0].tokenInID, - tokenOutID: singleTokenPairInput[0].tokenOutID, + tokenInID: swapRoutes[0].tokenInID, + tokenOutID: swapRoutes[0].tokenOutID, swapType: SWAP_TYPE.fixedInput, tolerance, - addressRoute: singleTokenPairInput[0].pairs.map( - (pair) => pair.address, - ), - intermediaryAmounts: - singleTokenPairInput[0].intermediaryAmounts, - tokenRoute: singleTokenPairInput[0].tokenRoute, + addressRoute: swapRoutes[0].pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoutes[0].intermediaryAmounts, + tokenRoute: swapRoutes[0].tokenRoute, }); + const amount0 = + swapRoutes[swapRoutes.length - 1].tokenInID === + swapRoutes[swapRoutes.length - 1].pairs[0].firstToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + swapRoutes[swapRoutes.length - 1].pairs[0].secondToken + .identifier, + swapRoutes[swapRoutes.length - 1].amountIn, + ) + : new BigNumber(swapRoutes[swapRoutes.length - 1].amountOut); + const amount1 = + swapRoutes[swapRoutes.length - 1].tokenInID === + swapRoutes[swapRoutes.length - 1].pairs[0].secondToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + swapRoutes[swapRoutes.length - 1].pairs[0].firstToken + .identifier, + swapRoutes[swapRoutes.length - 1].amountOut, + ) + : new BigNumber(swapRoutes[swapRoutes.length - 1].amountIn); + + const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); + const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); + const contract = lockEpochs ? await this.mxProxy.getLockedTokenPositionCreatorContract() : await this.mxProxy.getPostitionCreatorContract(); @@ -202,14 +231,14 @@ export class PositionCreatorTransactionService { const endpointArgs = lockEpochs ? [ new U64Value(new BigNumber(lockEpochs)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), ...swapRouteArgs, ] : [ new AddressValue(Address.fromBech32(farmAddress)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), ...swapRouteArgs, ]; @@ -243,10 +272,7 @@ export class PositionCreatorTransactionService { } transactions.push(interaction.buildTransaction().toPlainObject()); - return { - swaps: singleTokenPairInput.swapRoutes, - transactions, - }; + return transactions; } async createDualFarmPositionSingleToken( @@ -254,7 +280,8 @@ export class PositionCreatorTransactionService { stakingProxyAddress: string, payments: EsdtTokenPayment[], tolerance: number, - ): Promise { + swapRoutes: SwapRouteModel[], + ): Promise { const [ pairAddress, dualYieldTokenID, @@ -293,34 +320,48 @@ export class PositionCreatorTransactionService { ); } - const singleTokenPairInput = - await this.posCreatorCompute.computeSingleTokenPairInput( - pairAddress, - payments[0], - tolerance, - ); - const swapRouteArgs = this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: singleTokenPairInput[0].tokenInID, - tokenOutID: singleTokenPairInput[0].tokenOutID, + tokenInID: swapRoutes[0].tokenInID, + tokenOutID: swapRoutes[0].tokenOutID, swapType: SWAP_TYPE.fixedInput, tolerance, - addressRoute: singleTokenPairInput[0].pairs.map( - (pair) => pair.address, - ), - intermediaryAmounts: - singleTokenPairInput[0].intermediaryAmounts, - tokenRoute: singleTokenPairInput[0].tokenRoute, + addressRoute: swapRoutes[0].pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoutes[0].intermediaryAmounts, + tokenRoute: swapRoutes[0].tokenRoute, }); + const amount0 = + swapRoutes[swapRoutes.length - 1].tokenInID === + swapRoutes[swapRoutes.length - 1].pairs[0].firstToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + swapRoutes[swapRoutes.length - 1].pairs[0].secondToken + .identifier, + swapRoutes[swapRoutes.length - 1].amountIn, + ) + : new BigNumber(swapRoutes[swapRoutes.length - 1].amountOut); + const amount1 = + swapRoutes[swapRoutes.length - 1].tokenInID === + swapRoutes[swapRoutes.length - 1].pairs[0].secondToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pairAddress, + swapRoutes[swapRoutes.length - 1].pairs[0].firstToken + .identifier, + swapRoutes[swapRoutes.length - 1].amountOut, + ) + : new BigNumber(swapRoutes[swapRoutes.length - 1].amountIn); + + const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); + const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); + const contract = await this.mxProxy.getPostitionCreatorContract(); let interaction = contract.methodsExplicit .createMetastakingPosFromSingleToken([ new AddressValue(Address.fromBech32(stakingProxyAddress)), - new BigUIntValue(singleTokenPairInput.amount0Min), - new BigUIntValue(singleTokenPairInput.amount1Min), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), ...swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) @@ -353,10 +394,7 @@ export class PositionCreatorTransactionService { } transactions.push(interaction.buildTransaction().toPlainObject()); - return { - swaps: singleTokenPairInput.swapRoutes, - transactions, - }; + return transactions; } async createStakingPositionSingleToken( From 29634c958eb4b99f3ce02fcc9488ad93643e395a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 12:10:18 +0300 Subject: [PATCH 154/313] MEX-466: add validations for transaction addresses Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index e531c5b94..1c8c20b85 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -13,6 +13,10 @@ import { LiquidityPositionSingleTokenModel, } from './models/position.creator.model'; import { PositionCreatorComputeService } from './services/position.creator.compute'; +import { FarmAbiServiceV2 } from '../farm/v2/services/farm.v2.abi.service'; +import { StakingProxyAbiService } from '../staking-proxy/services/staking.proxy.abi.service'; +import { GraphQLError } from 'graphql'; +import { ApolloServerErrorCode } from '@apollo/server/dist/esm/errors'; @Resolver(() => LiquidityPositionSingleTokenModel) export class LiquidityPositionSingleTokenResolver { @@ -53,6 +57,7 @@ export class LiquidityPositionSingleTokenResolver { export class FarmPositionSingleTokenResolver { constructor( private readonly posCreatorTransaction: PositionCreatorTransactionService, + private readonly farmAbi: FarmAbiServiceV2, ) {} @ResolveField(() => [TransactionModel]) @@ -68,6 +73,19 @@ export class FarmPositionSingleTokenResolver { additionalPayments: InputTokenModel[], @Args('lockEpochs', { nullable: true }) lockEpochs: number, ): Promise { + const pairAddress = await this.farmAbi.pairContractAddress(farmAddress); + + if ( + pairAddress !== + parent.swaps[parent.swaps.length - 1].pairs[0].address + ) { + throw new GraphQLError('Invalid farm address', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } + const firstPayment = new EsdtTokenPayment({ tokenIdentifier: parent.swaps[0].tokenInID, tokenNonce: 0, @@ -99,6 +117,7 @@ export class FarmPositionSingleTokenResolver { export class DualFarmPositionSingleTokenResolver { constructor( private readonly posCreatorTransaction: PositionCreatorTransactionService, + private readonly stakingProxyAbi: StakingProxyAbiService, ) {} @ResolveField(() => [TransactionModel]) @@ -113,6 +132,20 @@ export class DualFarmPositionSingleTokenResolver { }) additionalPayments: InputTokenModel[], ): Promise { + const pairAddress = await this.stakingProxyAbi.pairAddress( + dualFarmAddress, + ); + if ( + pairAddress !== + parent.swaps[parent.swaps.length - 1].pairs[0].address + ) { + throw new GraphQLError('Invalid farm address', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } + const firstPayment = new EsdtTokenPayment({ tokenIdentifier: parent.swaps[0].tokenInID, tokenNonce: 0, @@ -144,6 +177,8 @@ export class PositionCreatorTransactionResolver { constructor( private readonly posCreatorTransaction: PositionCreatorTransactionService, private readonly posCreatorCompute: PositionCreatorComputeService, + private readonly farmAbi: FarmAbiServiceV2, + private readonly stakingProxyAbi: StakingProxyAbiService, ) {} @Query(() => LiquidityPositionSingleTokenModel) @@ -170,10 +205,12 @@ export class PositionCreatorTransactionResolver { @Query(() => FarmPositionSingleTokenModel) async createFarmPositionSingleToken( - @Args('pairAddress') pairAddress: string, + @Args('farmAddress') farmAddress: string, @Args('payment') payment: InputTokenModel, @Args('tolerance') tolerance: number, ): Promise { + const pairAddress = await this.farmAbi.pairContractAddress(farmAddress); + const swapRoutes = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, @@ -192,10 +229,14 @@ export class PositionCreatorTransactionResolver { @Query(() => DualFarmPositionSingleTokenModel) async createDualFarmPositionSingleToken( - @Args('pairAddress') pairAddress: string, + @Args('dualFarmAddress') dualFarmAddress: string, @Args('payment') payment: InputTokenModel, @Args('tolerance') tolerance: number, ): Promise { + const pairAddress = await this.stakingProxyAbi.pairAddress( + dualFarmAddress, + ); + const swapRoutes = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, From 2d93ebef6066ea2ffda59f03be5debd90e28f1d0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 12:11:01 +0300 Subject: [PATCH 155/313] MEX-466: extract compute amounts minimum into own method Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 115 +++++++----------- 1 file changed, 44 insertions(+), 71 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index f69c35b81..45f5993da 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -79,29 +79,10 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoutes[0].tokenRoute, }); - const amount0 = - swapRoutes[swapRoutes.length - 1].tokenInID === - swapRoutes[swapRoutes.length - 1].pairs[0].firstToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - swapRoutes[swapRoutes.length - 1].pairs[0].secondToken - .identifier, - swapRoutes[swapRoutes.length - 1].amountIn, - ) - : new BigNumber(swapRoutes[swapRoutes.length - 1].amountOut); - const amount1 = - swapRoutes[swapRoutes.length - 1].tokenInID === - swapRoutes[swapRoutes.length - 1].pairs[0].secondToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - swapRoutes[swapRoutes.length - 1].pairs[0].firstToken - .identifier, - swapRoutes[swapRoutes.length - 1].amountOut, - ) - : new BigNumber(swapRoutes[swapRoutes.length - 1].amountIn); - - const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); - const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); + const [amount0Min, amount1Min] = + await this.getMinimumAmountsForLiquidity( + swapRoutes[swapRoutes.length - 1], + ); const contract = lockEpochs ? await this.mxProxy.getLockedTokenPositionCreatorContract() @@ -158,9 +139,8 @@ export class PositionCreatorTransactionService { swapRoutes: SwapRouteModel[], lockEpochs?: number, ): Promise { - const [pairAddress, farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = + const [farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = await Promise.all([ - this.farmAbiV2.pairContractAddress(farmAddress), this.farmAbiV2.farmTokenID(farmAddress), this.tokenService.getUniqueTokenIDs(false), this.wrapAbi.wrappedEgldTokenID(), @@ -200,29 +180,10 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoutes[0].tokenRoute, }); - const amount0 = - swapRoutes[swapRoutes.length - 1].tokenInID === - swapRoutes[swapRoutes.length - 1].pairs[0].firstToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - swapRoutes[swapRoutes.length - 1].pairs[0].secondToken - .identifier, - swapRoutes[swapRoutes.length - 1].amountIn, - ) - : new BigNumber(swapRoutes[swapRoutes.length - 1].amountOut); - const amount1 = - swapRoutes[swapRoutes.length - 1].tokenInID === - swapRoutes[swapRoutes.length - 1].pairs[0].secondToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - swapRoutes[swapRoutes.length - 1].pairs[0].firstToken - .identifier, - swapRoutes[swapRoutes.length - 1].amountOut, - ) - : new BigNumber(swapRoutes[swapRoutes.length - 1].amountIn); - - const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); - const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); + const [amount0Min, amount1Min] = + await this.getMinimumAmountsForLiquidity( + swapRoutes[swapRoutes.length - 1], + ); const contract = lockEpochs ? await this.mxProxy.getLockedTokenPositionCreatorContract() @@ -331,29 +292,10 @@ export class PositionCreatorTransactionService { tokenRoute: swapRoutes[0].tokenRoute, }); - const amount0 = - swapRoutes[swapRoutes.length - 1].tokenInID === - swapRoutes[swapRoutes.length - 1].pairs[0].firstToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - swapRoutes[swapRoutes.length - 1].pairs[0].secondToken - .identifier, - swapRoutes[swapRoutes.length - 1].amountIn, - ) - : new BigNumber(swapRoutes[swapRoutes.length - 1].amountOut); - const amount1 = - swapRoutes[swapRoutes.length - 1].tokenInID === - swapRoutes[swapRoutes.length - 1].pairs[0].secondToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pairAddress, - swapRoutes[swapRoutes.length - 1].pairs[0].firstToken - .identifier, - swapRoutes[swapRoutes.length - 1].amountOut, - ) - : new BigNumber(swapRoutes[swapRoutes.length - 1].amountIn); - - const amount0Min = amount0.multipliedBy(1 - tolerance).integerValue(); - const amount1Min = amount1.multipliedBy(1 - tolerance).integerValue(); + const [amount0Min, amount1Min] = + await this.getMinimumAmountsForLiquidity( + swapRoutes[swapRoutes.length - 1], + ); const contract = await this.mxProxy.getPostitionCreatorContract(); @@ -835,6 +777,37 @@ export class PositionCreatorTransactionService { return interaction.buildTransaction().toPlainObject(); } + private async getMinimumAmountsForLiquidity( + swapRoute: SwapRouteModel, + ): Promise { + const pair = swapRoute.pairs[0]; + const amount0 = + swapRoute.tokenInID === swapRoute.pairs[0].firstToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pair.address, + pair.secondToken.identifier, + swapRoute.amountIn, + ) + : new BigNumber(swapRoute.amountOut); + const amount1 = + swapRoute.tokenInID === pair.secondToken.identifier + ? await this.pairService.getEquivalentForLiquidity( + pair.address, + pair.firstToken.identifier, + swapRoute.amountOut, + ) + : new BigNumber(swapRoute.amountIn); + + const amount0Min = amount0 + .multipliedBy(1 - swapRoute.tolerance) + .integerValue(); + const amount1Min = amount1 + .multipliedBy(1 - swapRoute.tolerance) + .integerValue(); + + return [amount0Min, amount1Min]; + } + private checkTokensPayments( payments: EsdtTokenPayment[], firstTokenID: string, From 0b82d60380b874d46feebd7425e1d0344cbbb81d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 14:14:26 +0300 Subject: [PATCH 156/313] MEX-466: fix swap information with swap on same pair Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 2 +- .../services/position.creator.compute.ts | 46 +++++++++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 1c8c20b85..fe4d21f8f 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -16,7 +16,7 @@ import { PositionCreatorComputeService } from './services/position.creator.compu import { FarmAbiServiceV2 } from '../farm/v2/services/farm.v2.abi.service'; import { StakingProxyAbiService } from '../staking-proxy/services/staking.proxy.abi.service'; import { GraphQLError } from 'graphql'; -import { ApolloServerErrorCode } from '@apollo/server/dist/esm/errors'; +import { ApolloServerErrorCode } from '@apollo/server/errors'; @Resolver(() => LiquidityPositionSingleTokenModel) export class LiquidityPositionSingleTokenResolver { diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 6bbcb418b..dcbee850d 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -81,33 +81,49 @@ export class PositionCreatorComputeService { return []; } - const [tokenInID, tokenOutID] = acceptedPairedTokensIDs.includes( - firstToken.identifier, - ) - ? [firstToken.identifier, secondToken.identifier] - : [secondToken.identifier, firstToken.identifier]; - const profiler = new PerformanceProfiler(); const swapRoutes = []; - const swapRoute = await this.autoRouterService.swap({ - tokenInID: payment.tokenIdentifier, - amountIn: payment.amount, - tokenOutID: tokenInID, - tolerance, - }); + let amountOut: BigNumber; + let tokenInID: string; + let tokenOutID: string; + + if ( + payment.tokenIdentifier !== firstToken.identifier && + payment.tokenIdentifier !== secondToken.identifier + ) { + [tokenInID, tokenOutID] = acceptedPairedTokensIDs.includes( + firstToken.identifier, + ) + ? [firstToken.identifier, secondToken.identifier] + : [secondToken.identifier, firstToken.identifier]; + + const swapRoute = await this.autoRouterService.swap({ + tokenInID: payment.tokenIdentifier, + amountIn: payment.amount, + tokenOutID: tokenInID, + tolerance, + }); - swapRoutes.push(swapRoute); + swapRoutes.push(swapRoute); + amountOut = new BigNumber(swapRoute.amountOut); + } else { + amountOut = new BigNumber(payment.amount); + [tokenInID, tokenOutID] = + payment.tokenIdentifier === firstToken.identifier + ? [firstToken.identifier, secondToken.identifier] + : [secondToken.identifier, firstToken.identifier]; + } profiler.stop('swap route', true); - const halfPayment = new BigNumber(swapRoute.amountOut) + const halfPayment = new BigNumber(amountOut) .dividedBy(2) .integerValue() .toFixed(); - const remainingPayment = new BigNumber(swapRoute.amountOut) + const remainingPayment = new BigNumber(amountOut) .minus(halfPayment) .toFixed(); From 939891bfc3c06e77d2194b4f7fddffbab579ddd0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 14:15:14 +0300 Subject: [PATCH 157/313] MEX-466: fix transaction generation when swap in same pair Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 45f5993da..ea52b5df8 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -69,15 +69,19 @@ export class PositionCreatorTransactionService { } const swapRouteArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoutes[0].tokenInID, - tokenOutID: swapRoutes[0].tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoutes[0].pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoutes[0].intermediaryAmounts, - tokenRoute: swapRoutes[0].tokenRoute, - }); + swapRoutes.length < 2 + ? [] + : this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoutes[0].tokenInID, + tokenOutID: swapRoutes[0].tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: swapRoutes[0].pairs.map( + (pair) => pair.address, + ), + intermediaryAmounts: swapRoutes[0].intermediaryAmounts, + tokenRoute: swapRoutes[0].tokenRoute, + }); const [amount0Min, amount1Min] = await this.getMinimumAmountsForLiquidity( @@ -170,15 +174,19 @@ export class PositionCreatorTransactionService { } const swapRouteArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoutes[0].tokenInID, - tokenOutID: swapRoutes[0].tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoutes[0].pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoutes[0].intermediaryAmounts, - tokenRoute: swapRoutes[0].tokenRoute, - }); + swapRoutes.length < 2 + ? [] + : this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoutes[0].tokenInID, + tokenOutID: swapRoutes[0].tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: swapRoutes[0].pairs.map( + (pair) => pair.address, + ), + intermediaryAmounts: swapRoutes[0].intermediaryAmounts, + tokenRoute: swapRoutes[0].tokenRoute, + }); const [amount0Min, amount1Min] = await this.getMinimumAmountsForLiquidity( @@ -282,15 +290,19 @@ export class PositionCreatorTransactionService { } const swapRouteArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoutes[0].tokenInID, - tokenOutID: swapRoutes[0].tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoutes[0].pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoutes[0].intermediaryAmounts, - tokenRoute: swapRoutes[0].tokenRoute, - }); + swapRoutes.length < 2 + ? [] + : this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoutes[0].tokenInID, + tokenOutID: swapRoutes[0].tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance, + addressRoute: swapRoutes[0].pairs.map( + (pair) => pair.address, + ), + intermediaryAmounts: swapRoutes[0].intermediaryAmounts, + tokenRoute: swapRoutes[0].tokenRoute, + }); const [amount0Min, amount1Min] = await this.getMinimumAmountsForLiquidity( From d56da49faa0cb03003e3911d09709b4400875427 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 16:12:35 +0300 Subject: [PATCH 158/313] MEX-466: refactor staking and energy single token position creator - added 2 new models for staking and energy single token position creator - added resolvers for staking and energy single token position creator models Signed-off-by: Claudiu Lataretu --- .../models/position.creator.model.ts | 26 ++++ .../position.creator.module.ts | 4 + .../position.creator.transaction.resolver.ts | 135 ++++++++++++++---- .../services/position.creator.compute.ts | 51 ++++--- .../services/position.creator.transaction.ts | 51 ++++--- 5 files changed, 191 insertions(+), 76 deletions(-) diff --git a/src/modules/position-creator/models/position.creator.model.ts b/src/modules/position-creator/models/position.creator.model.ts index f06cae51f..5c450d9ef 100644 --- a/src/modules/position-creator/models/position.creator.model.ts +++ b/src/modules/position-creator/models/position.creator.model.ts @@ -50,3 +50,29 @@ export class DualFarmPositionSingleTokenModel { Object.assign(this, init); } } + +@ObjectType() +export class StakingPositionSingleTokenModel { + @Field(() => [SwapRouteModel]) + swaps: SwapRouteModel[]; + + @Field(() => [TransactionModel], { nullable: true }) + transactions?: TransactionModel[]; + + constructor(init: Partial) { + Object.assign(this, init); + } +} + +@ObjectType() +export class EnergyPositionSingleTokenModel { + @Field(() => [SwapRouteModel]) + swaps: SwapRouteModel[]; + + @Field(() => [TransactionModel], { nullable: true }) + transactions?: TransactionModel[]; + + constructor(init: Partial) { + Object.assign(this, init); + } +} diff --git a/src/modules/position-creator/position.creator.module.ts b/src/modules/position-creator/position.creator.module.ts index 4291598cf..1dc3b0964 100644 --- a/src/modules/position-creator/position.creator.module.ts +++ b/src/modules/position-creator/position.creator.module.ts @@ -12,9 +12,11 @@ import { StakingModule } from '../staking/staking.module'; import { TokenModule } from '../tokens/token.module'; import { DualFarmPositionSingleTokenResolver, + EnergyPositionSingleTokenResolver, FarmPositionSingleTokenResolver, LiquidityPositionSingleTokenResolver, PositionCreatorTransactionResolver, + StakingPositionSingleTokenResolver, } from './position.creator.transaction.resolver'; import { WrappingModule } from '../wrapping/wrap.module'; import { ProxyFarmModule } from '../proxy/services/proxy-farm/proxy.farm.module'; @@ -42,6 +44,8 @@ import { EnergyModule } from '../energy/energy.module'; FarmPositionSingleTokenResolver, LiquidityPositionSingleTokenResolver, DualFarmPositionSingleTokenResolver, + StakingPositionSingleTokenResolver, + EnergyPositionSingleTokenResolver, ], exports: [], }) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index fe4d21f8f..699c9a296 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -9,14 +9,18 @@ import { UserAuthResult } from '../auth/user.auth.result'; import { AuthUser } from '../auth/auth.user'; import { DualFarmPositionSingleTokenModel, + EnergyPositionSingleTokenModel, FarmPositionSingleTokenModel, LiquidityPositionSingleTokenModel, + StakingPositionSingleTokenModel, } from './models/position.creator.model'; import { PositionCreatorComputeService } from './services/position.creator.compute'; import { FarmAbiServiceV2 } from '../farm/v2/services/farm.v2.abi.service'; import { StakingProxyAbiService } from '../staking-proxy/services/staking.proxy.abi.service'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; +import { constantsConfig } from 'src/config'; +import { StakingAbiService } from '../staking/services/staking.abi.service'; @Resolver(() => LiquidityPositionSingleTokenModel) export class LiquidityPositionSingleTokenResolver { @@ -68,7 +72,7 @@ export class FarmPositionSingleTokenResolver { @Args('farmAddress') farmAddress: string, @Args('additionalPayments', { type: () => [InputTokenModel], - nullable: true, + defaultValue: [], }) additionalPayments: InputTokenModel[], @Args('lockEpochs', { nullable: true }) lockEpochs: number, @@ -128,7 +132,7 @@ export class DualFarmPositionSingleTokenResolver { @Args('dualFarmAddress') dualFarmAddress: string, @Args('additionalPayments', { type: () => [InputTokenModel], - nullable: true, + defaultValue: [], }) additionalPayments: InputTokenModel[], ): Promise { @@ -172,6 +176,93 @@ export class DualFarmPositionSingleTokenResolver { } } +@Resolver(() => StakingPositionSingleTokenModel) +export class StakingPositionSingleTokenResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + private readonly stakingAbi: StakingAbiService, + ) {} + + @ResolveField(() => [TransactionModel]) + @UseGuards(JwtOrNativeAuthGuard) + async transactions( + @AuthUser() user: UserAuthResult, + @Parent() parent: StakingPositionSingleTokenModel, + @Args('stakingAddress') stakingAddress: string, + @Args('additionalPayments', { + type: () => [InputTokenModel], + defaultValue: [], + }) + additionalPayments: InputTokenModel[], + ): Promise { + const farmingTokenID = await this.stakingAbi.farmingTokenID( + stakingAddress, + ); + if ( + farmingTokenID !== parent.swaps[parent.swaps.length - 1].tokenOutID + ) { + throw new GraphQLError('Invalid staking address', { + extensions: { + code: ApolloServerErrorCode.BAD_USER_INPUT, + }, + }); + } + + const firstPayment = new EsdtTokenPayment({ + tokenIdentifier: parent.swaps[0].tokenInID, + tokenNonce: 0, + amount: parent.swaps[0].amountIn, + }); + + return this.posCreatorTransaction.createStakingPositionSingleToken( + user.address, + stakingAddress, + parent.swaps[0], + [ + firstPayment, + ...additionalPayments.map( + (payment) => + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), + ), + ], + parent.swaps[0].tolerance, + ); + } +} + +@Resolver(() => EnergyPositionSingleTokenModel) +export class EnergyPositionSingleTokenResolver { + constructor( + private readonly posCreatorTransaction: PositionCreatorTransactionService, + ) {} + + @ResolveField(() => [TransactionModel]) + @UseGuards(JwtOrNativeAuthGuard) + async transactions( + @AuthUser() user: UserAuthResult, + @Parent() parent: EnergyPositionSingleTokenModel, + @Args('lockEpochs') lockEpochs: number, + ): Promise { + const firstPayment = new EsdtTokenPayment({ + tokenIdentifier: parent.swaps[0].tokenInID, + tokenNonce: 0, + amount: parent.swaps[0].amountIn, + }); + + return this.posCreatorTransaction.createEnergyPosition( + user.address, + firstPayment, + parent.swaps[0], + lockEpochs, + parent.swaps[0].tolerance, + ); + } +} + @Resolver() export class PositionCreatorTransactionResolver { constructor( @@ -253,25 +344,20 @@ export class PositionCreatorTransactionResolver { }); } - @Query(() => [TransactionModel]) + @Query(() => StakingPositionSingleTokenModel) async createStakingPositionSingleToken( - @AuthUser() user: UserAuthResult, @Args('stakingAddress') stakingAddress: string, - @Args('payments', { type: () => [InputTokenModel] }) - payments: InputTokenModel[], + @Args('payment', { type: () => InputTokenModel }) + payment: InputTokenModel, @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createStakingPositionSingleToken( - user.address, + ): Promise { + return this.posCreatorCompute.computeStakingPositionSingleToken( stakingAddress, - payments.map( - (payment) => - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), - ), + new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }), tolerance, ); } @@ -341,22 +427,23 @@ export class PositionCreatorTransactionResolver { ); } - @Query(() => TransactionModel) + @Query(() => EnergyPositionSingleTokenModel) async createEnergyPosition( - @AuthUser() user: UserAuthResult, @Args('payment') payment: InputTokenModel, - @Args('lockEpochs') lockEpochs: number, @Args('tolerance') tolerance: number, - ): Promise { - return this.posCreatorTransaction.createEnergyPosition( - user.address, + ): Promise { + const swapRoute = await this.posCreatorCompute.computeSingleTokenInput( new EsdtTokenPayment({ tokenIdentifier: payment.tokenID, tokenNonce: payment.nonce, amount: payment.amount, }), - lockEpochs, + constantsConfig.MEX_TOKEN_ID, tolerance, ); + + return new EnergyPositionSingleTokenModel({ + swaps: [swapRoute], + }); } } diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index dcbee850d..0e4a95897 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -3,18 +3,18 @@ import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; import { PerformanceProfiler } from '@multiversx/sdk-nestjs-monitoring'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; -import { constantsConfig } from 'src/config'; import { SWAP_TYPE, SwapRouteModel, } from 'src/modules/auto-router/models/auto-route.model'; import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; -import { AutoRouterTransactionService } from 'src/modules/auto-router/services/auto-router.transactions.service'; import { PairModel } from 'src/modules/pair/models/pair.model'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { PairComputeService } from 'src/modules/pair/services/pair.compute.service'; import { PairService } from 'src/modules/pair/services/pair.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; +import { StakingPositionSingleTokenModel } from '../models/position.creator.model'; +import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; export type PositionCreatorSingleTokenInput = { swapRouteArgs: TypedValue[]; @@ -28,8 +28,8 @@ export class PositionCreatorComputeService { private readonly pairService: PairService, private readonly pairCompute: PairComputeService, private readonly routerAbi: RouterAbiService, + private readonly stakingAbi: StakingAbiService, private readonly autoRouterService: AutoRouterService, - private readonly autoRouterTransaction: AutoRouterTransactionService, ) {} async computeSwap( @@ -181,35 +181,34 @@ export class PositionCreatorComputeService { return swapRoutes; } + async computeStakingPositionSingleToken( + stakingAddress: string, + payment: EsdtTokenPayment, + tolerance: number, + ): Promise { + const farmingTokenID = await this.stakingAbi.farmingTokenID( + stakingAddress, + ); + const swapRoute = await this.computeSingleTokenInput( + payment, + farmingTokenID, + tolerance, + ); + return new StakingPositionSingleTokenModel({ + swaps: [swapRoute], + }); + } + async computeSingleTokenInput( payment: EsdtTokenPayment, + tokenOutID: string, tolerance: number, - ): Promise { - const swapRoute = await this.autoRouterService.swap({ + ): Promise { + return this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, amountIn: payment.amount, - tokenOutID: constantsConfig.MEX_TOKEN_ID, + tokenOutID: tokenOutID, tolerance, }); - - const amountOutMin = new BigNumber(swapRoute.amountOut) - .multipliedBy(1 - tolerance) - .integerValue(); - - const swapRouteArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoute.tokenInID, - tokenOutID: swapRoute.tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoute.pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoute.intermediaryAmounts, - tokenRoute: swapRoute.tokenRoute, - }); - - return { - swapRouteArgs, - amountOutMin, - }; } } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index ea52b5df8..2644826c6 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -354,20 +354,16 @@ export class PositionCreatorTransactionService { async createStakingPositionSingleToken( sender: string, stakingAddress: string, + swapRoute: SwapRouteModel, payments: EsdtTokenPayment[], tolerance: number, ): Promise { - const [ - farmingTokenID, - farmTokenID, - uniqueTokensIDs, - wrappedEgldTokenID, - ] = await Promise.all([ - this.stakingAbi.farmingTokenID(stakingAddress), - this.stakingAbi.farmTokenID(stakingAddress), - this.tokenService.getUniqueTokenIDs(false), - this.wrapAbi.wrappedEgldTokenID(), - ]); + const [farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = + await Promise.all([ + this.stakingAbi.farmTokenID(stakingAddress), + this.tokenService.getUniqueTokenIDs(false), + this.wrapAbi.wrappedEgldTokenID(), + ]); if ( !uniqueTokensIDs.includes(payments[0].tokenIdentifier) && @@ -392,13 +388,6 @@ export class PositionCreatorTransactionService { ); } - const swapRoute = await this.autoRouterService.swap({ - tokenInID: payments[0].tokenIdentifier, - amountIn: payments[0].amount, - tokenOutID: farmingTokenID, - tolerance, - }); - const multiSwapArgs = this.autoRouterTransaction.multiPairFixedInputSwaps({ tokenInID: swapRoute.tokenInID, @@ -742,9 +731,10 @@ export class PositionCreatorTransactionService { async createEnergyPosition( sender: string, payment: EsdtTokenPayment, + swapRoute: SwapRouteModel, lockEpochs: number, tolerance: number, - ): Promise { + ): Promise { const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( false, ); @@ -756,11 +746,20 @@ export class PositionCreatorTransactionService { throw new Error('Invalid ESDT token payment'); } - const singleTokenInput = - await this.posCreatorCompute.computeSingleTokenInput( - payment, + const amountOutMin = new BigNumber(swapRoute.amountOut) + .multipliedBy(1 - tolerance) + .integerValue(); + + const swapRouteArgs = + this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoute.tokenInID, + tokenOutID: swapRoute.tokenOutID, + swapType: SWAP_TYPE.fixedInput, tolerance, - ); + addressRoute: swapRoute.pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoute.intermediaryAmounts, + tokenRoute: swapRoute.tokenRoute, + }); const contract = await this.mxProxy.getLockedTokenPositionCreatorContract(); @@ -768,8 +767,8 @@ export class PositionCreatorTransactionService { let interaction = contract.methodsExplicit .createEnergyPosition([ new U64Value(new BigNumber(lockEpochs)), - new BigUIntValue(singleTokenInput.amountOutMin), - ...singleTokenInput.swapRouteArgs, + new BigUIntValue(amountOutMin), + ...swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) .withGasLimit(gasConfig.positionCreator.energyPosition) @@ -786,7 +785,7 @@ export class PositionCreatorTransactionService { ); } - return interaction.buildTransaction().toPlainObject(); + return [interaction.buildTransaction().toPlainObject()]; } private async getMinimumAmountsForLiquidity( From d74d798cc1623bf6f5795ba048cd8b911757fbdf Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 18:57:44 +0300 Subject: [PATCH 159/313] MEX-466: fix position creator minimum amounts for liquidity compute Signed-off-by: Claudiu Lataretu --- .../services/position.creator.transaction.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 2644826c6..3abeffd12 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -305,9 +305,11 @@ export class PositionCreatorTransactionService { }); const [amount0Min, amount1Min] = - await this.getMinimumAmountsForLiquidity( - swapRoutes[swapRoutes.length - 1], - ); + payments[0].tokenIdentifier === lpTokenID + ? [new BigNumber(0), new BigNumber(0)] + : await this.getMinimumAmountsForLiquidity( + swapRoutes[swapRoutes.length - 1], + ); const contract = await this.mxProxy.getPostitionCreatorContract(); @@ -792,22 +794,33 @@ export class PositionCreatorTransactionService { swapRoute: SwapRouteModel, ): Promise { const pair = swapRoute.pairs[0]; - const amount0 = + let [amount0, amount1] = + swapRoute.tokenInID === pair.firstToken.identifier + ? [ + new BigNumber(swapRoute.amountIn), + new BigNumber(swapRoute.amountOut), + ] + : [ + new BigNumber(swapRoute.amountOut), + new BigNumber(swapRoute.amountIn), + ]; + + amount0 = swapRoute.tokenInID === swapRoute.pairs[0].firstToken.identifier ? await this.pairService.getEquivalentForLiquidity( pair.address, pair.secondToken.identifier, - swapRoute.amountIn, + amount1.toFixed(), ) - : new BigNumber(swapRoute.amountOut); - const amount1 = + : amount0; + amount1 = swapRoute.tokenInID === pair.secondToken.identifier ? await this.pairService.getEquivalentForLiquidity( pair.address, pair.firstToken.identifier, - swapRoute.amountOut, + amount0.toFixed(), ) - : new BigNumber(swapRoute.amountIn); + : amount1; const amount0Min = amount0 .multipliedBy(1 - swapRoute.tolerance) From 5392c571c36b90620f4725b5b105fb9ddbbe52af Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 8 Apr 2024 18:58:30 +0300 Subject: [PATCH 160/313] MEX-466: update position creator unit tests to use new swap information Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.spec.ts | 585 ++++++++++++++---- 1 file changed, 466 insertions(+), 119 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index f350b5c16..bd2877071 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -32,9 +32,10 @@ import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staki import { ComposableTasksTransactionService } from 'src/modules/composable-tasks/services/composable.tasks.transaction'; import { ProxyFarmAbiServiceProvider } from 'src/modules/proxy/mocks/proxy.abi.service.mock'; import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; -import { gasConfig, scAddress } from 'src/config'; +import { constantsConfig, gasConfig, scAddress } from 'src/config'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; +import { SwapRouteModel } from 'src/modules/auto-router/models/auto-route.model'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -101,6 +102,7 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), 0.01, + [], ), ).rejects.toThrowError('Invalid ESDT token payment'); }); @@ -109,7 +111,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createLiquidityPositionSingleToken( Address.Zero().bech32(), Address.fromHex( @@ -121,29 +140,32 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), 0.01, + swapRoutes, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.liquidityPosition, - data: encodeTransactionData( - `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: + gasConfig.positionCreator.singleToken.liquidityPosition, + data: encodeTransactionData( + `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); @@ -152,7 +174,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createLiquidityPositionSingleToken( Address.Zero().bech32(), Address.fromHex( @@ -164,37 +203,57 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), 0.01, + swapRoutes, 1440, ); - expect(transaction).toEqual({ - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.liquidityPosition, - data: encodeTransactionData( - `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transactions).toEqual([ + { + nonce: 0, + value: '0', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: + gasConfig.positionCreator.singleToken.liquidityPosition, + data: encodeTransactionData( + `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); it('should return transaction with EGLD payment', async () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createLiquidityPositionSingleToken( Address.Zero().bech32(), Address.fromHex( @@ -206,30 +265,33 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), 0.01, + swapRoutes, 1440, ); - expect(transaction).toEqual({ - nonce: 0, - value: '100000000000000000000', - receiver: - 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', - sender: Address.Zero().bech32(), - senderUsername: undefined, - receiverUsername: undefined, - gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.liquidityPosition, - data: encodeTransactionData( - `createPairPosFromSingleToken@1440@47008144020574367766@47008144020574367766823`, - ), - chainID: 'T', - version: 1, - options: undefined, - guardian: undefined, - signature: undefined, - guardianSignature: undefined, - }); + expect(transactions).toEqual([ + { + nonce: 0, + value: '100000000000000000000', + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + sender: Address.Zero().bech32(), + senderUsername: undefined, + receiverUsername: undefined, + gasPrice: 1000000000, + gasLimit: + gasConfig.positionCreator.singleToken.liquidityPosition, + data: encodeTransactionData( + `createPairPosFromSingleToken@1440@47008144020574367766@47008144020574367766823`, + ), + chainID: 'T', + version: 1, + options: undefined, + guardian: undefined, + signature: undefined, + guardianSignature: undefined, + }, + ]); }); }); @@ -238,6 +300,7 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + expect( service.createFarmPositionSingleToken( Address.Zero().bech32(), @@ -252,6 +315,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + [], ), ).rejects.toThrowError('Invalid ESDT token payment'); }); @@ -260,6 +324,7 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + expect( service.createFarmPositionSingleToken( Address.Zero().bech32(), @@ -279,6 +344,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + [], ), ).rejects.toThrowError('Invalid farm token payment'); }); @@ -287,7 +353,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionSingleToken( + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createFarmPositionSingleToken( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -300,9 +383,10 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); - expect(transaction).toEqual([ + expect(transactions).toEqual([ { nonce: 0, value: '100000000000000000000', @@ -331,7 +415,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionSingleToken( + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createFarmPositionSingleToken( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -344,9 +445,10 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); - expect(transaction).toEqual([ + expect(transactions).toEqual([ { nonce: 0, value: '0', @@ -374,7 +476,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionSingleToken( + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createFarmPositionSingleToken( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -392,9 +511,10 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); - expect(transaction).toEqual([ + expect(transactions).toEqual([ { nonce: 0, value: '100000000000000000000', @@ -440,7 +560,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionSingleToken( + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createFarmPositionSingleToken( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -458,9 +595,10 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); - expect(transaction).toEqual([ + expect(transactions).toEqual([ { nonce: 0, value: '0', @@ -490,7 +628,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); - const transaction = await service.createFarmPositionSingleToken( + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + + const transactions = await service.createFarmPositionSingleToken( Address.Zero().bech32(), Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', @@ -503,10 +658,11 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, 1440, ); - expect(transaction).toEqual([ + expect(transactions).toEqual([ { nonce: 0, value: '0', @@ -534,6 +690,23 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + const transaction = await service.createFarmPositionSingleToken( Address.Zero().bech32(), Address.fromHex( @@ -547,6 +720,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, 1440, ); @@ -581,6 +755,7 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + expect( service.createDualFarmPositionSingleToken( Address.Zero().bech32(), @@ -593,6 +768,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + [], ), ).rejects.toThrowError('Invalid ESDT token payment'); }); @@ -618,6 +794,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + [], ), ).rejects.toThrowError('Invalid dual yield token payment'); }); @@ -629,12 +806,29 @@ describe('PositionCreatorTransaction', () => { const stakingProxyAbi = module.get( StakingProxyAbiService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000012', ).bech32(), ); + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + const transaction = await service.createDualFarmPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), @@ -646,6 +840,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); expect(transaction).toEqual([ @@ -679,6 +874,23 @@ describe('PositionCreatorTransaction', () => { const stakingProxyAbi = module.get( StakingProxyAbiService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000012', @@ -696,6 +908,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); expect(transaction).toEqual([ @@ -729,6 +942,23 @@ describe('PositionCreatorTransaction', () => { const stakingProxyAbi = module.get( StakingProxyAbiService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000012', @@ -751,6 +981,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); expect(transaction).toEqual([ @@ -802,6 +1033,23 @@ describe('PositionCreatorTransaction', () => { const stakingProxyAbi = module.get( StakingProxyAbiService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeSingleTokenPairInput( + Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + jest.spyOn(stakingProxyAbi, 'pairAddress').mockResolvedValue( Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000012', @@ -824,6 +1072,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + swapRoutes, ); expect(transaction).toEqual([ @@ -879,6 +1128,7 @@ describe('PositionCreatorTransaction', () => { }), ], 0.01, + [], ); expect(transaction).toEqual([ @@ -915,6 +1165,7 @@ describe('PositionCreatorTransaction', () => { service.createStakingPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), + new SwapRouteModel(), [ new EsdtTokenPayment({ tokenIdentifier: 'USDC-abcdef', @@ -935,6 +1186,7 @@ describe('PositionCreatorTransaction', () => { service.createStakingPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), + new SwapRouteModel(), [ new EsdtTokenPayment({ tokenIdentifier: 'USDC-123456', @@ -957,13 +1209,29 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const stakingAbi = module.get(StakingAbiService); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + jest.spyOn(stakingAbi, 'farmingTokenID').mockResolvedValue( 'MEX-123456', ); + const swapRoutes = + await posCreatorCompute.computeStakingPositionSingleToken( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + const transaction = await service.createStakingPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), + swapRoutes.swaps[0], [ new EsdtTokenPayment({ tokenIdentifier: 'EGLD', @@ -1002,9 +1270,24 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + const swapRoutes = + await posCreatorCompute.computeStakingPositionSingleToken( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + const transaction = await service.createStakingPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), + swapRoutes.swaps[0], [ new EsdtTokenPayment({ tokenIdentifier: 'USDC-123456', @@ -1044,13 +1327,29 @@ describe('PositionCreatorTransaction', () => { PositionCreatorTransactionService, ); const stakingAbi = module.get(StakingAbiService); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + jest.spyOn(stakingAbi, 'farmingTokenID').mockResolvedValue( 'MEX-123456', ); + const swapRoutes = + await posCreatorCompute.computeStakingPositionSingleToken( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + const transaction = await service.createStakingPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), + swapRoutes.swaps[0], [ new EsdtTokenPayment({ tokenIdentifier: 'EGLD', @@ -1112,9 +1411,25 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + + const swapRoutes = + await posCreatorCompute.computeStakingPositionSingleToken( + Address.Zero().bech32(), + new EsdtTokenPayment({ + tokenIdentifier: 'USDC-123456', + tokenNonce: 0, + amount: '100000000000000000000', + }), + 0.01, + ); + const transaction = await service.createStakingPositionSingleToken( Address.Zero().bech32(), Address.Zero().bech32(), + swapRoutes.swaps[0], [ new EsdtTokenPayment({ tokenIdentifier: 'USDC-123456', @@ -1935,6 +2250,7 @@ describe('PositionCreatorTransaction', () => { tokenNonce: 0, amount: '1000000000000000000', }), + new SwapRouteModel(), 1440, 0.01, ), @@ -1945,76 +2261,107 @@ describe('PositionCreatorTransaction', () => { const service = module.get( PositionCreatorTransactionService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); - const transaction = await service.createEnergyPosition( + const swapRoute = await posCreatorCompute.computeSingleTokenInput( + new EsdtTokenPayment({ + tokenIdentifier: 'WEGLD-123456', + tokenNonce: 0, + amount: '1000000000000000000', + }), + constantsConfig.MEX_TOKEN_ID, + 0.01, + ); + + const transactions = await service.createEnergyPosition( Address.Zero().bech32(), new EsdtTokenPayment({ tokenIdentifier: 'WEGLD-123456', tokenNonce: 0, amount: '1000000000000000000', }), + swapRoute, 1440, 0.01, ); - expect(transaction).toEqual({ - chainID: 'T', - data: encodeTransactionData( - 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', - ), - gasLimit: gasConfig.positionCreator.energyPosition, - gasPrice: 1000000000, - guardian: undefined, - guardianSignature: undefined, - nonce: 0, - options: undefined, - receiver: - 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', - receiverUsername: undefined, - sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', - senderUsername: undefined, - signature: undefined, - value: '0', - version: 1, - }); + expect(transactions).toEqual([ + { + chainID: 'T', + data: encodeTransactionData( + 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', + ), + gasLimit: gasConfig.positionCreator.energyPosition, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + receiverUsername: undefined, + sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + senderUsername: undefined, + signature: undefined, + value: '0', + version: 1, + }, + ]); }); it('should return transaction with EGLD payment', async () => { const service = module.get( PositionCreatorTransactionService, ); + const posCreatorCompute = module.get( + PositionCreatorComputeService, + ); + const swapRoute = await posCreatorCompute.computeSingleTokenInput( + new EsdtTokenPayment({ + tokenIdentifier: 'EGLD', + tokenNonce: 0, + amount: '1000000000000000000', + }), + constantsConfig.MEX_TOKEN_ID, + 0.01, + ); - const transaction = await service.createEnergyPosition( + const transactions = await service.createEnergyPosition( Address.Zero().bech32(), new EsdtTokenPayment({ tokenIdentifier: 'EGLD', tokenNonce: 0, amount: '1000000000000000000', }), + swapRoute, 1440, 0.01, ); - expect(transaction).toEqual({ - chainID: 'T', - data: encodeTransactionData( - 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', - ), - gasLimit: gasConfig.positionCreator.energyPosition, - gasPrice: 1000000000, - guardian: undefined, - guardianSignature: undefined, - nonce: 0, - options: undefined, - receiver: - 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', - receiverUsername: undefined, - sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', - senderUsername: undefined, - signature: undefined, - value: '1000000000000000000', - version: 1, - }); + expect(transactions).toEqual([ + { + chainID: 'T', + data: encodeTransactionData( + 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', + ), + gasLimit: gasConfig.positionCreator.energyPosition, + gasPrice: 1000000000, + guardian: undefined, + guardianSignature: undefined, + nonce: 0, + options: undefined, + receiver: + 'erd1qqqqqqqqqqqqqpgqh3zcutxk3wmfvevpyymaehvc3k0knyq70n4sg6qcj6', + receiverUsername: undefined, + sender: 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', + senderUsername: undefined, + signature: undefined, + value: '1000000000000000000', + version: 1, + }, + ]); }); }); }); From fe467922cf6dc6caabf6e7c9d91a341a7a6a0c66 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 9 Apr 2024 18:17:42 +0300 Subject: [PATCH 161/313] MEX-466: fix transaction for dual farm single token position Signed-off-by: Claudiu Lataretu --- .../position-creator/position.creator.transaction.resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 699c9a296..bb4a0ee32 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -170,7 +170,7 @@ export class DualFarmPositionSingleTokenResolver { }), ), ], - parent.swaps[length - 1].tolerance, + parent.swaps[parent.swaps.length - 1].tolerance, parent.swaps, ); } From 12152029bc567745754eb70c9533c294c2dc6d3b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 10 Apr 2024 19:34:30 +0300 Subject: [PATCH 162/313] MEX-466: fixes after review - removed the ternary operator for multiple lines - added new method to serialize the swap route arguments - refactored getMinimumAmountsForLiquidity method Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 5 - .../services/position.creator.transaction.ts | 156 ++++++------------ .../position.creator.transaction.spec.ts | 28 ---- 3 files changed, 53 insertions(+), 136 deletions(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index bb4a0ee32..6e669997b 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -48,7 +48,6 @@ export class LiquidityPositionSingleTokenResolver { user.address, pairAddress, payment, - parent.swaps[parent.swaps.length - 1].tolerance, parent.swaps, lockEpochs, ); @@ -110,7 +109,6 @@ export class FarmPositionSingleTokenResolver { }), ), ], - parent.swaps[length - 1].tolerance, parent.swaps, lockEpochs, ); @@ -170,7 +168,6 @@ export class DualFarmPositionSingleTokenResolver { }), ), ], - parent.swaps[parent.swaps.length - 1].tolerance, parent.swaps, ); } @@ -229,7 +226,6 @@ export class StakingPositionSingleTokenResolver { }), ), ], - parent.swaps[0].tolerance, ); } } @@ -258,7 +254,6 @@ export class EnergyPositionSingleTokenResolver { firstPayment, parent.swaps[0], lockEpochs, - parent.swaps[0].tolerance, ); } } diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 3abeffd12..c713f67c8 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -4,6 +4,7 @@ import { BigUIntValue, Interaction, TokenTransfer, + TypedValue, U64Value, } from '@multiversx/sdk-core/out'; import { EsdtTokenPayment } from '@multiversx/sdk-exchange'; @@ -13,8 +14,6 @@ import { gasConfig, mxConfig, scAddress } from 'src/config'; import { TransactionModel } from 'src/models/transaction.model'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; -import { PositionCreatorComputeService } from './position.creator.compute'; -import { AutoRouterService } from 'src/modules/auto-router/services/auto-router.service'; import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staking.proxy.abi.service'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; @@ -33,9 +32,7 @@ import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; @Injectable() export class PositionCreatorTransactionService { constructor( - private readonly autoRouterService: AutoRouterService, private readonly autoRouterTransaction: AutoRouterTransactionService, - private readonly posCreatorCompute: PositionCreatorComputeService, private readonly pairAbi: PairAbiService, private readonly pairService: PairService, private readonly farmAbiV2: FarmAbiServiceV2, @@ -53,7 +50,6 @@ export class PositionCreatorTransactionService { sender: string, pairAddress: string, payment: EsdtTokenPayment, - tolerance: number, swapRoutes: SwapRouteModel[], lockEpochs?: number, ): Promise { @@ -71,17 +67,7 @@ export class PositionCreatorTransactionService { const swapRouteArgs = swapRoutes.length < 2 ? [] - : this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoutes[0].tokenInID, - tokenOutID: swapRoutes[0].tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoutes[0].pairs.map( - (pair) => pair.address, - ), - intermediaryAmounts: swapRoutes[0].intermediaryAmounts, - tokenRoute: swapRoutes[0].tokenRoute, - }); + : this.serializeSwapRouteArgs(swapRoutes[0]); const [amount0Min, amount1Min] = await this.getMinimumAmountsForLiquidity( @@ -139,7 +125,6 @@ export class PositionCreatorTransactionService { sender: string, farmAddress: string, payments: EsdtTokenPayment[], - tolerance: number, swapRoutes: SwapRouteModel[], lockEpochs?: number, ): Promise { @@ -176,17 +161,7 @@ export class PositionCreatorTransactionService { const swapRouteArgs = swapRoutes.length < 2 ? [] - : this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoutes[0].tokenInID, - tokenOutID: swapRoutes[0].tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoutes[0].pairs.map( - (pair) => pair.address, - ), - intermediaryAmounts: swapRoutes[0].intermediaryAmounts, - tokenRoute: swapRoutes[0].tokenRoute, - }); + : this.serializeSwapRouteArgs(swapRoutes[0]); const [amount0Min, amount1Min] = await this.getMinimumAmountsForLiquidity( @@ -197,19 +172,22 @@ export class PositionCreatorTransactionService { ? await this.mxProxy.getLockedTokenPositionCreatorContract() : await this.mxProxy.getPostitionCreatorContract(); - const endpointArgs = lockEpochs - ? [ - new U64Value(new BigNumber(lockEpochs)), - new BigUIntValue(amount0Min), - new BigUIntValue(amount1Min), - ...swapRouteArgs, - ] - : [ - new AddressValue(Address.fromBech32(farmAddress)), - new BigUIntValue(amount0Min), - new BigUIntValue(amount1Min), - ...swapRouteArgs, - ]; + let endpointArgs: TypedValue[]; + if (lockEpochs) { + endpointArgs = [ + new U64Value(new BigNumber(lockEpochs)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + ...swapRouteArgs, + ]; + } else { + endpointArgs = [ + new AddressValue(Address.fromBech32(farmAddress)), + new BigUIntValue(amount0Min), + new BigUIntValue(amount1Min), + ...swapRouteArgs, + ]; + } let interaction = contract.methodsExplicit .createFarmPosFromSingleToken(endpointArgs) @@ -248,7 +226,6 @@ export class PositionCreatorTransactionService { sender: string, stakingProxyAddress: string, payments: EsdtTokenPayment[], - tolerance: number, swapRoutes: SwapRouteModel[], ): Promise { const [ @@ -292,17 +269,7 @@ export class PositionCreatorTransactionService { const swapRouteArgs = swapRoutes.length < 2 ? [] - : this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoutes[0].tokenInID, - tokenOutID: swapRoutes[0].tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoutes[0].pairs.map( - (pair) => pair.address, - ), - intermediaryAmounts: swapRoutes[0].intermediaryAmounts, - tokenRoute: swapRoutes[0].tokenRoute, - }); + : this.serializeSwapRouteArgs(swapRoutes[0]); const [amount0Min, amount1Min] = payments[0].tokenIdentifier === lpTokenID @@ -358,7 +325,6 @@ export class PositionCreatorTransactionService { stakingAddress: string, swapRoute: SwapRouteModel, payments: EsdtTokenPayment[], - tolerance: number, ): Promise { const [farmTokenID, uniqueTokensIDs, wrappedEgldTokenID] = await Promise.all([ @@ -390,16 +356,7 @@ export class PositionCreatorTransactionService { ); } - const multiSwapArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoute.tokenInID, - tokenOutID: swapRoute.tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoute.pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoute.intermediaryAmounts, - tokenRoute: swapRoute.tokenRoute, - }); + const multiSwapArgs = this.serializeSwapRouteArgs(swapRoute); const contract = await this.mxProxy.getPostitionCreatorContract(); let interaction = contract.methodsExplicit @@ -735,7 +692,6 @@ export class PositionCreatorTransactionService { payment: EsdtTokenPayment, swapRoute: SwapRouteModel, lockEpochs: number, - tolerance: number, ): Promise { const uniqueTokensIDs = await this.tokenService.getUniqueTokenIDs( false, @@ -749,19 +705,10 @@ export class PositionCreatorTransactionService { } const amountOutMin = new BigNumber(swapRoute.amountOut) - .multipliedBy(1 - tolerance) + .multipliedBy(1 - swapRoute.tolerance) .integerValue(); - const swapRouteArgs = - this.autoRouterTransaction.multiPairFixedInputSwaps({ - tokenInID: swapRoute.tokenInID, - tokenOutID: swapRoute.tokenOutID, - swapType: SWAP_TYPE.fixedInput, - tolerance, - addressRoute: swapRoute.pairs.map((pair) => pair.address), - intermediaryAmounts: swapRoute.intermediaryAmounts, - tokenRoute: swapRoute.tokenRoute, - }); + const swapRouteArgs = this.serializeSwapRouteArgs(swapRoute); const contract = await this.mxProxy.getLockedTokenPositionCreatorContract(); @@ -794,33 +741,24 @@ export class PositionCreatorTransactionService { swapRoute: SwapRouteModel, ): Promise { const pair = swapRoute.pairs[0]; - let [amount0, amount1] = - swapRoute.tokenInID === pair.firstToken.identifier - ? [ - new BigNumber(swapRoute.amountIn), - new BigNumber(swapRoute.amountOut), - ] - : [ - new BigNumber(swapRoute.amountOut), - new BigNumber(swapRoute.amountIn), - ]; - - amount0 = - swapRoute.tokenInID === swapRoute.pairs[0].firstToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pair.address, - pair.secondToken.identifier, - amount1.toFixed(), - ) - : amount0; - amount1 = - swapRoute.tokenInID === pair.secondToken.identifier - ? await this.pairService.getEquivalentForLiquidity( - pair.address, - pair.firstToken.identifier, - amount0.toFixed(), - ) - : amount1; + let amount0: BigNumber; + let amount1: BigNumber; + + if (swapRoute.tokenInID === pair.firstToken.identifier) { + amount0 = await this.pairService.getEquivalentForLiquidity( + pair.address, + pair.secondToken.identifier, + swapRoute.amountOut, + ); + amount1 = new BigNumber(swapRoute.amountOut); + } else { + amount0 = new BigNumber(swapRoute.amountOut); + amount1 = await this.pairService.getEquivalentForLiquidity( + pair.address, + pair.firstToken.identifier, + swapRoute.amountOut, + ); + } const amount0Min = amount0 .multipliedBy(1 - swapRoute.tolerance) @@ -863,4 +801,16 @@ export class PositionCreatorTransactionService { return true; } + + private serializeSwapRouteArgs(swapRoute: SwapRouteModel): TypedValue[] { + return this.autoRouterTransaction.multiPairFixedInputSwaps({ + tokenInID: swapRoute.tokenInID, + tokenOutID: swapRoute.tokenOutID, + swapType: SWAP_TYPE.fixedInput, + tolerance: swapRoute.tolerance, + addressRoute: swapRoute.pairs.map((pair) => pair.address), + intermediaryAmounts: swapRoute.intermediaryAmounts, + tokenRoute: swapRoute.tokenRoute, + }); + } } diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index bd2877071..f980eea78 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -101,7 +101,6 @@ describe('PositionCreatorTransaction', () => { tokenNonce: 0, amount: '100000000000000000000', }), - 0.01, [], ), ).rejects.toThrowError('Invalid ESDT token payment'); @@ -139,7 +138,6 @@ describe('PositionCreatorTransaction', () => { tokenNonce: 0, amount: '100000000000000000000', }), - 0.01, swapRoutes, ); @@ -202,7 +200,6 @@ describe('PositionCreatorTransaction', () => { tokenNonce: 0, amount: '100000000000000000000', }), - 0.01, swapRoutes, 1440, ); @@ -264,7 +261,6 @@ describe('PositionCreatorTransaction', () => { tokenNonce: 0, amount: '100000000000000000000', }), - 0.01, swapRoutes, 1440, ); @@ -314,7 +310,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, [], ), ).rejects.toThrowError('Invalid ESDT token payment'); @@ -343,7 +338,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, [], ), ).rejects.toThrowError('Invalid farm token payment'); @@ -382,7 +376,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -444,7 +437,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -510,7 +502,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -594,7 +585,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -657,7 +647,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, 1440, ); @@ -719,7 +708,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, 1440, ); @@ -767,7 +755,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, [], ), ).rejects.toThrowError('Invalid ESDT token payment'); @@ -793,7 +780,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, [], ), ).rejects.toThrowError('Invalid dual yield token payment'); @@ -839,7 +825,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -907,7 +892,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -980,7 +964,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -1071,7 +1054,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, swapRoutes, ); @@ -1127,7 +1109,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, [], ); @@ -1173,7 +1154,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, ), ).rejects.toThrowError('Invalid ESDT token payment'); }); @@ -1199,7 +1179,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, ), ).rejects.toThrowError('Invalid staking token payment'); }); @@ -1239,7 +1218,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, ); expect(transaction).toEqual([ @@ -1295,7 +1273,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, ); expect(transaction).toEqual([ @@ -1362,7 +1339,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, ); expect(transaction).toEqual([ @@ -1442,7 +1418,6 @@ describe('PositionCreatorTransaction', () => { amount: '100000000000000000000', }), ], - 0.01, ); expect(transaction).toEqual([ @@ -2252,7 +2227,6 @@ describe('PositionCreatorTransaction', () => { }), new SwapRouteModel(), 1440, - 0.01, ), ).rejects.toThrowError('Invalid ESDT token payment'); }); @@ -2284,7 +2258,6 @@ describe('PositionCreatorTransaction', () => { }), swapRoute, 1440, - 0.01, ); expect(transactions).toEqual([ @@ -2337,7 +2310,6 @@ describe('PositionCreatorTransaction', () => { }), swapRoute, 1440, - 0.01, ); expect(transactions).toEqual([ From 27932c15ada40c0886b4275ce13b63ce07412bde Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 12 Apr 2024 11:40:17 +0300 Subject: [PATCH 163/313] MEX-466: add denominated values for tokens rate on position creator Signed-off-by: Claudiu Lataretu --- .../services/position.creator.compute.ts | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 0e4a95897..423163140 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -15,6 +15,8 @@ import { PairService } from 'src/modules/pair/services/pair.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; import { StakingPositionSingleTokenModel } from '../models/position.creator.model'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; +import { denominateAmount } from 'src/utils/token.converters'; +import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; export type PositionCreatorSingleTokenInput = { swapRouteArgs: TypedValue[]; @@ -86,23 +88,23 @@ export class PositionCreatorComputeService { const swapRoutes = []; let amountOut: BigNumber; - let tokenInID: string; - let tokenOutID: string; + let tokenIn: EsdtToken; + let tokenOut: EsdtToken; if ( payment.tokenIdentifier !== firstToken.identifier && payment.tokenIdentifier !== secondToken.identifier ) { - [tokenInID, tokenOutID] = acceptedPairedTokensIDs.includes( + [tokenIn, tokenOut] = acceptedPairedTokensIDs.includes( firstToken.identifier, ) - ? [firstToken.identifier, secondToken.identifier] - : [secondToken.identifier, firstToken.identifier]; + ? [firstToken, secondToken] + : [secondToken, firstToken]; const swapRoute = await this.autoRouterService.swap({ tokenInID: payment.tokenIdentifier, amountIn: payment.amount, - tokenOutID: tokenInID, + tokenOutID: tokenIn.identifier, tolerance, }); @@ -110,10 +112,10 @@ export class PositionCreatorComputeService { amountOut = new BigNumber(swapRoute.amountOut); } else { amountOut = new BigNumber(payment.amount); - [tokenInID, tokenOutID] = + [tokenIn, tokenOut] = payment.tokenIdentifier === firstToken.identifier - ? [firstToken.identifier, secondToken.identifier] - : [secondToken.identifier, firstToken.identifier]; + ? [firstToken, secondToken] + : [secondToken, firstToken]; } profiler.stop('swap route', true); @@ -130,26 +132,33 @@ export class PositionCreatorComputeService { const [amount0, amount1] = await Promise.all([ await this.computeSwap( pairAddress, - tokenInID, - tokenInID, + tokenIn.identifier, + tokenIn.identifier, halfPayment, ), await this.computeSwap( pairAddress, - tokenInID, - tokenOutID, + tokenIn.identifier, + tokenOut.identifier, remainingPayment, ), ]); + const tokenInExchangeRate = new BigNumber(amount1) + .dividedBy(amount0) + .toFixed(); + const tokenOutExchangeRate = new BigNumber(amount0) + .dividedBy(amount1) + .toFixed(); + swapRoutes.push( new SwapRouteModel({ swapType: SWAP_TYPE.fixedInput, - tokenInID, - tokenOutID, + tokenInID: tokenIn.identifier, + tokenOutID: tokenOut.identifier, amountIn: amount0.toFixed(), amountOut: amount1.toFixed(), - tokenRoute: [tokenInID, tokenOutID], + tokenRoute: [tokenIn.identifier, tokenOut.identifier], pairs: [ new PairModel({ address: pairAddress, @@ -161,18 +170,22 @@ export class PositionCreatorComputeService { ], intermediaryAmounts: [amount0.toFixed(), amount1.toFixed()], tolerance: tolerance, - tokenInExchangeRate: new BigNumber(amount1) - .dividedBy(amount0) - .toFixed(), - tokenOutExchangeRate: new BigNumber(amount0) - .dividedBy(amount1) - .toFixed(), + tokenInExchangeRate: tokenInExchangeRate, + tokenOutExchangeRate: tokenOutExchangeRate, + tokenInExchangeRateDenom: denominateAmount( + tokenInExchangeRate, + tokenOut.decimals, + ).toFixed(), + tokenOutExchangeRateDenom: denominateAmount( + tokenOutExchangeRate, + tokenIn.decimals, + ).toFixed(), tokenInPriceUSD: - tokenInID === firstToken.identifier + tokenIn.identifier === firstToken.identifier ? firstTokenPriceUSD : secondTokenPriceUSD, tokenOutPriceUSD: - tokenOutID === firstToken.identifier + tokenOut.identifier === firstToken.identifier ? firstTokenPriceUSD : secondTokenPriceUSD, }), From ba3072a9cd00e9c63683db55b8ad8e3a33ee0855 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 12 Apr 2024 14:36:20 +0300 Subject: [PATCH 164/313] MEX-466: add missing swap information to position creator Signed-off-by: Claudiu Lataretu --- .../services/position.creator.compute.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 423163140..070cf979f 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -17,6 +17,7 @@ import { StakingPositionSingleTokenModel } from '../models/position.creator.mode import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; import { denominateAmount } from 'src/utils/token.converters'; import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; +import { constantsConfig } from 'src/config'; export type PositionCreatorSingleTokenInput = { swapRouteArgs: TypedValue[]; @@ -151,6 +152,12 @@ export class PositionCreatorComputeService { .dividedBy(amount1) .toFixed(); + const priceDeviationPercent = + await this.autoRouterService.getTokenPriceDeviationPercent( + [tokenIn.identifier, tokenOut.identifier], + [amount0.toFixed(), amount1.toFixed()], + ); + swapRoutes.push( new SwapRouteModel({ swapType: SWAP_TYPE.fixedInput, @@ -188,6 +195,8 @@ export class PositionCreatorComputeService { tokenOut.identifier === firstToken.identifier ? firstTokenPriceUSD : secondTokenPriceUSD, + maxPriceDeviationPercent: constantsConfig.MAX_SWAP_SPREAD, + tokensPriceDeviationPercent: priceDeviationPercent, }), ); From 785054fde388d6569ff14ba8487b435b104a6fec Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Sat, 13 Apr 2024 19:37:54 +0300 Subject: [PATCH 165/313] MEX-466: fix exchange rates for position creator swap information Signed-off-by: Claudiu Lataretu --- .../services/position.creator.compute.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 070cf979f..3d481f20f 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -145,11 +145,17 @@ export class PositionCreatorComputeService { ), ]); - const tokenInExchangeRate = new BigNumber(amount1) + const tokenInExchangeRate = new BigNumber(10) + .pow(tokenOut.decimals) + .multipliedBy(amount1) .dividedBy(amount0) + .integerValue() .toFixed(); - const tokenOutExchangeRate = new BigNumber(amount0) + const tokenOutExchangeRate = new BigNumber(10) + .pow(tokenIn.decimals) + .multipliedBy(amount0) .dividedBy(amount1) + .integerValue() .toFixed(); const priceDeviationPercent = From 02166f589dbfa5c54b987c57df4060069957d17c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Sat, 13 Apr 2024 19:41:59 +0300 Subject: [PATCH 166/313] MEX-466: fix position creator transaction payment Signed-off-by: Claudiu Lataretu --- .../models/position.creator.model.ts | 16 ++++ .../position.creator.transaction.resolver.ts | 87 ++++++++----------- .../services/position.creator.compute.ts | 20 +++-- 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/modules/position-creator/models/position.creator.model.ts b/src/modules/position-creator/models/position.creator.model.ts index 5c450d9ef..41e18d2cf 100644 --- a/src/modules/position-creator/models/position.creator.model.ts +++ b/src/modules/position-creator/models/position.creator.model.ts @@ -1,6 +1,7 @@ import { Field, ObjectType } from '@nestjs/graphql'; import { TransactionModel } from 'src/models/transaction.model'; import { SwapRouteModel } from 'src/modules/auto-router/models/auto-route.model'; +import { EsdtTokenPaymentModel } from 'src/modules/tokens/models/esdt.token.payment.model'; @ObjectType() export class PositionCreatorModel { @@ -14,6 +15,9 @@ export class PositionCreatorModel { @ObjectType() export class LiquidityPositionSingleTokenModel { + @Field(() => EsdtTokenPaymentModel) + payment: EsdtTokenPaymentModel; + @Field(() => [SwapRouteModel]) swaps: SwapRouteModel[]; @@ -27,6 +31,9 @@ export class LiquidityPositionSingleTokenModel { @ObjectType() export class FarmPositionSingleTokenModel { + @Field(() => EsdtTokenPaymentModel) + payment: EsdtTokenPaymentModel; + @Field(() => [SwapRouteModel]) swaps: SwapRouteModel[]; @@ -40,6 +47,9 @@ export class FarmPositionSingleTokenModel { @ObjectType() export class DualFarmPositionSingleTokenModel { + @Field(() => EsdtTokenPaymentModel) + payment: EsdtTokenPaymentModel; + @Field(() => [SwapRouteModel]) swaps: SwapRouteModel[]; @@ -53,6 +63,9 @@ export class DualFarmPositionSingleTokenModel { @ObjectType() export class StakingPositionSingleTokenModel { + @Field(() => EsdtTokenPaymentModel) + payment: EsdtTokenPaymentModel; + @Field(() => [SwapRouteModel]) swaps: SwapRouteModel[]; @@ -66,6 +79,9 @@ export class StakingPositionSingleTokenModel { @ObjectType() export class EnergyPositionSingleTokenModel { + @Field(() => EsdtTokenPaymentModel) + payment: EsdtTokenPaymentModel; + @Field(() => [SwapRouteModel]) swaps: SwapRouteModel[]; diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index 6e669997b..eb6ba0c33 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -37,11 +37,7 @@ export class LiquidityPositionSingleTokenResolver { ): Promise { const pairAddress = parent.swaps[parent.swaps.length - 1].pairs[0].address; - const payment = new EsdtTokenPayment({ - tokenIdentifier: parent.swaps[0].tokenInID, - tokenNonce: 0, - amount: parent.swaps[0].amountIn, - }); + const payment = new EsdtTokenPayment(parent.payment); const transactions = await this.posCreatorTransaction.createLiquidityPositionSingleToken( @@ -89,17 +85,11 @@ export class FarmPositionSingleTokenResolver { }); } - const firstPayment = new EsdtTokenPayment({ - tokenIdentifier: parent.swaps[0].tokenInID, - tokenNonce: 0, - amount: parent.swaps[0].amountIn, - }); - return this.posCreatorTransaction.createFarmPositionSingleToken( user.address, farmAddress, [ - firstPayment, + new EsdtTokenPayment(parent.payment), ...additionalPayments.map( (payment) => new EsdtTokenPayment({ @@ -148,17 +138,11 @@ export class DualFarmPositionSingleTokenResolver { }); } - const firstPayment = new EsdtTokenPayment({ - tokenIdentifier: parent.swaps[0].tokenInID, - tokenNonce: 0, - amount: parent.swaps[0].amountIn, - }); - return this.posCreatorTransaction.createDualFarmPositionSingleToken( user.address, dualFarmAddress, [ - firstPayment, + new EsdtTokenPayment(parent.payment), ...additionalPayments.map( (payment) => new EsdtTokenPayment({ @@ -205,18 +189,12 @@ export class StakingPositionSingleTokenResolver { }); } - const firstPayment = new EsdtTokenPayment({ - tokenIdentifier: parent.swaps[0].tokenInID, - tokenNonce: 0, - amount: parent.swaps[0].amountIn, - }); - return this.posCreatorTransaction.createStakingPositionSingleToken( user.address, stakingAddress, parent.swaps[0], [ - firstPayment, + new EsdtTokenPayment(parent.payment), ...additionalPayments.map( (payment) => new EsdtTokenPayment({ @@ -243,15 +221,9 @@ export class EnergyPositionSingleTokenResolver { @Parent() parent: EnergyPositionSingleTokenModel, @Args('lockEpochs') lockEpochs: number, ): Promise { - const firstPayment = new EsdtTokenPayment({ - tokenIdentifier: parent.swaps[0].tokenInID, - tokenNonce: 0, - amount: parent.swaps[0].amountIn, - }); - return this.posCreatorTransaction.createEnergyPosition( user.address, - firstPayment, + new EsdtTokenPayment(parent.payment), parent.swaps[0], lockEpochs, ); @@ -273,18 +245,20 @@ export class PositionCreatorTransactionResolver { @Args('payment') payment: InputTokenModel, @Args('tolerance') tolerance: number, ): Promise { + const esdtTokenPayment = new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }); const swapRoutes = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), + esdtTokenPayment, tolerance, ); return new LiquidityPositionSingleTokenModel({ + payment: esdtTokenPayment, swaps: swapRoutes, }); } @@ -296,19 +270,21 @@ export class PositionCreatorTransactionResolver { @Args('tolerance') tolerance: number, ): Promise { const pairAddress = await this.farmAbi.pairContractAddress(farmAddress); + const esdtTokenPayment = new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }); const swapRoutes = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), + esdtTokenPayment, tolerance, ); return new FarmPositionSingleTokenModel({ + payment: esdtTokenPayment, swaps: swapRoutes, }); } @@ -322,19 +298,21 @@ export class PositionCreatorTransactionResolver { const pairAddress = await this.stakingProxyAbi.pairAddress( dualFarmAddress, ); + const esdtTokenPayment = new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }); const swapRoutes = await this.posCreatorCompute.computeSingleTokenPairInput( pairAddress, - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), + esdtTokenPayment, tolerance, ); return new DualFarmPositionSingleTokenModel({ + payment: esdtTokenPayment, swaps: swapRoutes, }); } @@ -427,17 +405,20 @@ export class PositionCreatorTransactionResolver { @Args('payment') payment: InputTokenModel, @Args('tolerance') tolerance: number, ): Promise { + const esdtTokenPayment = new EsdtTokenPayment({ + tokenIdentifier: payment.tokenID, + tokenNonce: payment.nonce, + amount: payment.amount, + }); + const swapRoute = await this.posCreatorCompute.computeSingleTokenInput( - new EsdtTokenPayment({ - tokenIdentifier: payment.tokenID, - tokenNonce: payment.nonce, - amount: payment.amount, - }), + esdtTokenPayment, constantsConfig.MEX_TOKEN_ID, tolerance, ); return new EnergyPositionSingleTokenModel({ + payment: esdtTokenPayment, swaps: [swapRoute], }); } diff --git a/src/modules/position-creator/services/position.creator.compute.ts b/src/modules/position-creator/services/position.creator.compute.ts index 3d481f20f..b7a9c19b2 100644 --- a/src/modules/position-creator/services/position.creator.compute.ts +++ b/src/modules/position-creator/services/position.creator.compute.ts @@ -17,7 +17,8 @@ import { StakingPositionSingleTokenModel } from '../models/position.creator.mode import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; import { denominateAmount } from 'src/utils/token.converters'; import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; -import { constantsConfig } from 'src/config'; +import { constantsConfig, mxConfig } from 'src/config'; +import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; export type PositionCreatorSingleTokenInput = { swapRouteArgs: TypedValue[]; @@ -33,6 +34,7 @@ export class PositionCreatorComputeService { private readonly routerAbi: RouterAbiService, private readonly stakingAbi: StakingAbiService, private readonly autoRouterService: AutoRouterService, + private readonly wrapAbi: WrapAbiService, ) {} async computeSwap( @@ -63,6 +65,7 @@ export class PositionCreatorComputeService { await this.routerAbi.commonTokensForUserPairs(); const [ + wrappedTokenID, firstToken, secondToken, lpTokenID, @@ -71,6 +74,7 @@ export class PositionCreatorComputeService { reserves, totalFeePercent, ] = await Promise.all([ + this.wrapAbi.wrappedEgldTokenID(), this.pairService.getFirstToken(pairAddress), this.pairService.getSecondToken(pairAddress), this.pairAbi.lpTokenID(pairAddress), @@ -86,6 +90,11 @@ export class PositionCreatorComputeService { const profiler = new PerformanceProfiler(); + const paymentTokenID = + payment.tokenIdentifier === mxConfig.EGLDIdentifier + ? wrappedTokenID + : payment.tokenIdentifier; + const swapRoutes = []; let amountOut: BigNumber; @@ -93,8 +102,8 @@ export class PositionCreatorComputeService { let tokenOut: EsdtToken; if ( - payment.tokenIdentifier !== firstToken.identifier && - payment.tokenIdentifier !== secondToken.identifier + paymentTokenID !== firstToken.identifier && + paymentTokenID !== secondToken.identifier ) { [tokenIn, tokenOut] = acceptedPairedTokensIDs.includes( firstToken.identifier, @@ -103,7 +112,7 @@ export class PositionCreatorComputeService { : [secondToken, firstToken]; const swapRoute = await this.autoRouterService.swap({ - tokenInID: payment.tokenIdentifier, + tokenInID: paymentTokenID, amountIn: payment.amount, tokenOutID: tokenIn.identifier, tolerance, @@ -114,7 +123,7 @@ export class PositionCreatorComputeService { } else { amountOut = new BigNumber(payment.amount); [tokenIn, tokenOut] = - payment.tokenIdentifier === firstToken.identifier + paymentTokenID === firstToken.identifier ? [firstToken, secondToken] : [secondToken, firstToken]; } @@ -223,6 +232,7 @@ export class PositionCreatorComputeService { tolerance, ); return new StakingPositionSingleTokenModel({ + payment, swaps: [swapRoute], }); } From b3245f073344a214d3ae865f38c2996fa2d52df9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 May 2024 15:53:07 +0300 Subject: [PATCH 167/313] MEX-414: add swap fixed output and unwrap for multi pair swap composed task Signed-off-by: Claudiu Lataretu --- .../auto-router.transactions.service.ts | 123 ++++++++++++------ 1 file changed, 85 insertions(+), 38 deletions(-) diff --git a/src/modules/auto-router/services/auto-router.transactions.service.ts b/src/modules/auto-router/services/auto-router.transactions.service.ts index d95ce8691..372160abb 100644 --- a/src/modules/auto-router/services/auto-router.transactions.service.ts +++ b/src/modules/auto-router/services/auto-router.transactions.service.ts @@ -43,8 +43,17 @@ export class AutoRouterTransactionService { if (args.tokenInID === mxConfig.EGLDIdentifier) { return [ - await this.wrapEgldAndMultiSwapFixedInputTransaction( - args.intermediaryAmounts[0], + await this.wrapEgldAndMultiSwapTransaction( + amountIn.integerValue().toFixed(), + args, + ), + ]; + } + + if (args.tokenOutID === mxConfig.EGLDIdentifier) { + return [ + await this.multiSwapAndUnwrapEgldTransaction( + amountIn.integerValue().toFixed(), args, ), ]; @@ -199,45 +208,15 @@ export class AutoRouterTransactionService { return swaps; } - async wrapEgldAndMultiSwapFixedInputTransaction( + async wrapEgldAndMultiSwapTransaction( value: string, args: MultiSwapTokensArgs, ): Promise { - const swaps: BytesValue[] = []; - - const intermediaryTolerance = args.tolerance / args.addressRoute.length; - - for (const [index, address] of args.addressRoute.entries()) { - const intermediaryToleranceMultiplier = - args.addressRoute.length - index; - - const toleranceAmount = new BigNumber( - args.intermediaryAmounts[index + 1], - ).multipliedBy( - intermediaryToleranceMultiplier * intermediaryTolerance, - ); - - const amountOutMin = new BigNumber( - args.intermediaryAmounts[index + 1], - ) - .minus(toleranceAmount) - .integerValue(); - - swaps.push( - ...[ - new BytesValue( - Buffer.from(Address.fromString(address).hex(), 'hex'), - ), - BytesValue.fromUTF8(args.tokenRoute[index + 1]), - new BytesValue( - Buffer.from( - decimalToHex(new BigNumber(amountOutMin)), - 'hex', - ), - ), - ], - ); - } + const typedArgs = + args.swapType === SWAP_TYPE.fixedInput + ? this.multiPairFixedInputSwaps(args) + : this.multiPairFixedOutputSwaps(args); + const swaps = this.convertMultiPairSwapsToBytesValues(typedArgs); return this.composeTasksTransactionService.getComposeTasksTransaction( new EsdtTokenPayment({ @@ -264,4 +243,72 @@ export class AutoRouterTransactionService { ], ); } + + async multiSwapAndUnwrapEgldTransaction( + value: string, + args: MultiSwapTokensArgs, + ): Promise { + const typedArgs = + args.swapType === SWAP_TYPE.fixedInput + ? this.multiPairFixedInputSwaps(args) + : this.multiPairFixedOutputSwaps(args); + const swaps = this.convertMultiPairSwapsToBytesValues(typedArgs); + + return this.composeTasksTransactionService.getComposeTasksTransaction( + new EsdtTokenPayment({ + tokenIdentifier: args.tokenRoute[0], + tokenNonce: 0, + amount: value, + }), + new EgldOrEsdtTokenPayment({ + tokenIdentifier: 'EGLD', + nonce: 0, + amount: args.intermediaryAmounts[ + args.intermediaryAmounts.length - 1 + ], + }), + [ + { + type: ComposableTaskType.ROUTER_SWAP, + arguments: swaps, + }, + { + type: ComposableTaskType.UNWRAP_EGLD, + arguments: [], + }, + ], + ); + } + + private convertMultiPairSwapsToBytesValues( + args: TypedValue[], + ): BytesValue[] { + if (args.length % 4 !== 0) { + throw new Error('Invalid number of router swap arguments'); + } + + const swaps: BytesValue[] = []; + + for (let index = 0; index <= args.length - 4; index += 4) { + const pairAddress = args[index]; + const functionName = args[index + 1]; + const tokenOutID = args[index + 2]; + const amountOutMin = args[index + 3]; + + swaps.push( + new BytesValue(Buffer.from(pairAddress.valueOf().hex(), 'hex')), + ); + swaps.push(BytesValue.fromUTF8(functionName.valueOf())); + swaps.push(BytesValue.fromUTF8(tokenOutID.valueOf())); + swaps.push( + new BytesValue( + Buffer.from( + decimalToHex(new BigNumber(amountOutMin.valueOf())), + 'hex', + ), + ), + ); + } + return swaps; + } } From f2567809a5cb9b3341ae000f51ed2f79942bdd45 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 21 May 2024 16:08:50 +0300 Subject: [PATCH 168/313] MEX-414: fix auto router unit test Signed-off-by: Claudiu Lataretu --- .../specs/auto-router.service.spec.ts | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/modules/auto-router/specs/auto-router.service.spec.ts b/src/modules/auto-router/specs/auto-router.service.spec.ts index f7b6499d7..3d092f06a 100644 --- a/src/modules/auto-router/specs/auto-router.service.spec.ts +++ b/src/modules/auto-router/specs/auto-router.service.spec.ts @@ -330,41 +330,13 @@ describe('AutoRouterService', () => { { nonce: 0, value: '0', - receiver: Address.fromHex( - '0000000000000000000000000000000000000000000000000000000000000011', - ).bech32(), - sender: '', - receiverUsername: undefined, - senderUsername: undefined, - gasPrice: 1000000000, - gasLimit: 2 * gasConfig.router.multiPairSwapMultiplier, - data: encodeTransactionData( - `ESDTTransfer@USDC-123456@508044325756587816@multiPairSwap@${Address.fromHex( - '0000000000000000000000000000000000000000000000000000000000000013', - ).bech32()}@swapTokensFixedOutput@WEGLD-123456@630015438895@${Address.fromHex( - '0000000000000000000000000000000000000000000000000000000000000012', - ).bech32()}@swapTokensFixedOutput@MEX-123456@500000000000000000`, - ), - chainID: 'T', - version: 1, - options: undefined, - signature: undefined, - guardian: undefined, - guardianSignature: undefined, - }, - { - nonce: 0, - value: '0', - receiver: - 'erd1qqqqqqqqqqqqqpgqd77fnev2sthnczp2lnfx0y5jdycynjfhzzgq6p3rax', + receiver: Address.Zero().bech32(), sender: '', receiverUsername: undefined, senderUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.wrapeGLD, - data: encodeTransactionData( - 'ESDTTransfer@WEGLD-123456@500000000000000000@unwrapEgld', - ), + gasLimit: gasConfig.composableTasks.default, + data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDcwY2VmOWY1ZWRmY2YyOEA2MzZmNmQ3MDZmNzM2NTU0NjE3MzZiNzNAMDAwMDAwMDQ0NTQ3NGM0NDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODA2ZjA1YjU5ZDNiMjAwMDBAMDNAMDAwMDAwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEzMDAwMDAwMTU3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0Zjc1NzQ3MDc1NzQwMDAwMDAwYzU3NDU0NzRjNDQyZDMxMzIzMzM0MzUzNjAwMDAwMDA1OTJhZmQ4YjAyZjAwMDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMjAwMDAwMDE1NzM3NzYxNzA1NDZmNmI2NTZlNzM0NjY5Nzg2NTY0NGY3NTc0NzA3NTc0MDAwMDAwMGE0ZDQ1NTgyZDMxMzIzMzM0MzUzNjAwMDAwMDA4MDZmMDViNTlkM2IyMDAwMEAwMUA=', chainID: 'T', version: 1, options: undefined, From 43bda817ba476197bd181f2a55271631b95677de Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 24 May 2024 18:49:28 +0300 Subject: [PATCH 169/313] MEX-414: compute gas limit for composable tasks - compute gas limit for composable tasks based on the tasks included in the transaction; - use highest gas limit for swap transactions; Signed-off-by: Claudiu Lataretu --- .../services/composable.tasks.transaction.ts | 24 ++++++++++++++++++- .../composable.tasks.transaction.spec.ts | 6 ++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/modules/composable-tasks/services/composable.tasks.transaction.ts b/src/modules/composable-tasks/services/composable.tasks.transaction.ts index 33f5291da..e2bf75bb9 100644 --- a/src/modules/composable-tasks/services/composable.tasks.transaction.ts +++ b/src/modules/composable-tasks/services/composable.tasks.transaction.ts @@ -44,6 +44,28 @@ export class ComposableTasksTransactionService { tokenOut: EgldOrEsdtTokenPayment, tasks: ComposableTask[], ): Promise { + let gasLimit: number = gasConfig.composableTasks.default; + + for (const task of tasks) { + switch (task.type) { + case ComposableTaskType.WRAP_EGLD: + gasLimit += gasConfig.wrapeGLD; + break; + case ComposableTaskType.UNWRAP_EGLD: + gasLimit += gasConfig.wrapeGLD; + break; + case ComposableTaskType.SWAP: + gasLimit += + gasConfig.pairs.swapTokensFixedOutput.withFeeSwap; + case ComposableTaskType.ROUTER_SWAP: + const routes = Math.trunc(task.arguments.length / 4); + gasLimit += + routes * gasConfig.router.multiPairSwapMultiplier; + default: + break; + } + } + const contract = await this.mxProxy.getComposableTasksSmartContract(); let interaction = contract.methodsExplicit @@ -61,7 +83,7 @@ export class ComposableTasksTransactionService { ]), ...this.getRawTasks(tasks), ]) - .withGasLimit(gasConfig.composableTasks.default) + .withGasLimit(gasLimit) .withChainID(mxConfig.chainID); switch (payment.tokenIdentifier) { diff --git a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts index ec480aced..a277d9e3b 100644 --- a/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts +++ b/src/modules/composable-tasks/specs/composable.tasks.transaction.spec.ts @@ -70,7 +70,7 @@ describe('Composable Tasks Transaction', () => { expect(transaction).toEqual({ chainID: 'T', data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MGRlMGI2YjNhNzY0MDAwMEBA', - gasLimit: gasConfig.composableTasks.default, + gasLimit: 5200000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -102,7 +102,7 @@ describe('Composable Tasks Transaction', () => { chainID: 'T', data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAxMzEyZDAwQEBAMDJAMDAwMDAwMTQ3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0OTZlNzA3NTc0MDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDQwMTMxMmQwMA==', - gasLimit: gasConfig.composableTasks.default, + gasLimit: 40200000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -136,7 +136,7 @@ describe('Composable Tasks Transaction', () => { expect(transaction).toEqual({ chainID: 'T', data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDEzMTJkMDBANjM2ZjZkNzA2ZjczNjU1NDYxNzM2YjczQDAwMDAwMDA0NDU0NzRjNDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAyQDAwMDAwMDE0NzM3NzYxNzA1NDZmNmI2NTZlNzM0NjY5Nzg2NTY0NDk2ZTcwNzU3NDAwMDAwMDBjNTc0NTQ3NGM0NDJkMzEzMjMzMzQzNTM2MDAwMDAwMDgwZGUwYjZiM2E3NjQwMDAwQDAxQA==', - gasLimit: gasConfig.composableTasks.default, + gasLimit: 40200000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From 6131e55625ea839ae95e53df7667b610dba2e904 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 24 May 2024 18:56:18 +0300 Subject: [PATCH 170/313] MEX-414: update unit tests gas limits Signed-off-by: Claudiu Lataretu --- src/modules/auto-router/specs/auto-router.service.spec.ts | 4 ++-- src/modules/pair/specs/pair.transactions.service.spec.ts | 4 ++-- .../specs/position.creator.transaction.spec.ts | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/modules/auto-router/specs/auto-router.service.spec.ts b/src/modules/auto-router/specs/auto-router.service.spec.ts index a01b96c8b..5cb3ea5b1 100644 --- a/src/modules/auto-router/specs/auto-router.service.spec.ts +++ b/src/modules/auto-router/specs/auto-router.service.spec.ts @@ -282,7 +282,7 @@ describe('AutoRouterService', () => { receiverUsername: undefined, senderUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.composableTasks.default, + gasLimit: 40200000, data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBiNTU1MzQ0NDMyZDMxMzIzMzM0MzUzNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNzExNzJhY2UwMjZiMGM0QEBAMDJAMDAwMDAwMTQ3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0OTZlNzA3NTc0MDAwMDAwMGI1NTUzNDQ0MzJkMzEzMjMzMzQzNTM2MDAwMDAwMDcxMTcyYWNlMDI2YjBjNA==', chainID: 'T', version: 1, @@ -337,7 +337,7 @@ describe('AutoRouterService', () => { receiverUsername: undefined, senderUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.composableTasks.default, + gasLimit: 75200000, data: 'RVNEVFRyYW5zZmVyQDU1NTM0NDQzMmQzMTMyMzMzNDM1MzZAMDcwY2VmOWY1ZWRmY2YyOEA2MzZmNmQ3MDZmNzM2NTU0NjE3MzZiNzNAMDAwMDAwMDQ0NTQ3NGM0NDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODA2ZjA1YjU5ZDNiMjAwMDBAMDNAMDAwMDAwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEzMDAwMDAwMTU3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0Zjc1NzQ3MDc1NzQwMDAwMDAwYzU3NDU0NzRjNDQyZDMxMzIzMzM0MzUzNjAwMDAwMDA1OTJhZmQ4YjAyZjAwMDAwMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMjAwMDAwMDE1NzM3NzYxNzA1NDZmNmI2NTZlNzM0NjY5Nzg2NTY0NGY3NTc0NzA3NTc0MDAwMDAwMGE0ZDQ1NTgyZDMxMzIzMzM0MzUzNjAwMDAwMDA4MDZmMDViNTlkM2IyMDAwMEAwMUA=', chainID: 'T', version: 1, diff --git a/src/modules/pair/specs/pair.transactions.service.spec.ts b/src/modules/pair/specs/pair.transactions.service.spec.ts index a2f2ad73d..05d17c32e 100644 --- a/src/modules/pair/specs/pair.transactions.service.spec.ts +++ b/src/modules/pair/specs/pair.transactions.service.spec.ts @@ -461,7 +461,7 @@ describe('TransactionPairService', () => { expect(transactions).toEqual({ chainID: 'T', data: 'Y29tcG9zZVRhc2tzQDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwNEBAQDAyQDAwMDAwMDE0NzM3NzYxNzA1NDZmNmI2NTZlNzM0NjY5Nzg2NTY0NDk2ZTcwNzU3NDAwMDAwMDBhNGQ0NTU4MmQzMTMyMzMzNDM1MzYwMDAwMDAwMTA0', - gasLimit: gasConfig.composableTasks.default, + gasLimit: 40200000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -503,7 +503,7 @@ describe('TransactionPairService', () => { receiverUsername: undefined, senderUsername: undefined, gasPrice: 1000000000, - gasLimit: gasConfig.composableTasks.default, + gasLimit: 40200000, data: 'RVNEVFRyYW5zZmVyQDRkNDU1ODJkMzEzMjMzMzQzNTM2QDA1QDYzNmY2ZDcwNmY3MzY1NTQ2MTczNmI3M0AwMDAwMDAwNDQ1NDc0YzQ0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDVAMDJAMDAwMDAwMTU3Mzc3NjE3MDU0NmY2YjY1NmU3MzQ2Njk3ODY1NjQ0Zjc1NzQ3MDc1NzQwMDAwMDAwYzU3NDU0NzRjNDQyZDMxMzIzMzM0MzUzNjAwMDAwMDAxMDVAMDFA', chainID: mxConfig.chainID, version: 1, diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index f980eea78..535886c44 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -36,6 +36,7 @@ import { constantsConfig, gasConfig, scAddress } from 'src/config'; import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; import { SwapRouteModel } from 'src/modules/auto-router/models/auto-route.model'; +import { PairFilteringService } from 'src/modules/pair/services/pair.filtering.service'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -56,6 +57,7 @@ describe('PositionCreatorTransaction', () => { PairService, PairComputeServiceProvider, PairTransactionService, + PairFilteringService, WrapService, WrapAbiServiceProvider, WrapTransactionsService, From f373416dea3614cc5ded7d8134c0cc42437cbc8f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 24 May 2024 18:59:46 +0300 Subject: [PATCH 171/313] MEX-414: update default gas limit for composable tasks Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 506f2a7c6..ba2809a52 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -515,7 +515,7 @@ "energyPosition": 45000000 }, "composableTasks": { - "default": 35000000 + "default": 1000000 }, "lockedAssetCreate": 5000000, "wrapeGLD": 4200000, From 83a87228923b5b9204684040bb642aaedcff6bc9 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 28 May 2024 17:46:03 +0300 Subject: [PATCH 172/313] SERVICES-2416: add PairCompoundAPR model [WIP] - add 'compoundAPR' field on PairModel - add resolver for new model - compute farm APR --- .../v2/services/farm.v2.compute.service.ts | 1 + .../pair/models/pair.compounded.apr.model.ts | 23 +++++ src/modules/pair/models/pair.model.ts | 4 + src/modules/pair/pair.resolver.ts | 25 ++++++ .../pair/services/pair.compute.service.ts | 86 +++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 src/modules/pair/models/pair.compounded.apr.model.ts diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index 54a033e45..7b9a9e337 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -30,6 +30,7 @@ export class FarmComputeServiceV2 @Inject(forwardRef(() => FarmServiceV2)) protected readonly farmService: FarmServiceV2, protected readonly pairService: PairService, + @Inject(forwardRef(() => PairComputeService)) protected readonly pairCompute: PairComputeService, protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, diff --git a/src/modules/pair/models/pair.compounded.apr.model.ts b/src/modules/pair/models/pair.compounded.apr.model.ts new file mode 100644 index 000000000..890802cc4 --- /dev/null +++ b/src/modules/pair/models/pair.compounded.apr.model.ts @@ -0,0 +1,23 @@ +import { ObjectType, Field } from '@nestjs/graphql'; + +@ObjectType() +export class PairCompoundedAPRModel { + @Field({ nullable: true }) + feesAPR: string; + + @Field({ nullable: true }) + farmBaseAPR: string; + + @Field({ nullable: true }) + farmBoostedAPR: string; + + @Field({ nullable: true }) + dualFarmAPR: string; + + @Field({ nullable: true }) + dualFarmBoostedAPR: string; + + constructor(init?: Partial) { + Object.assign(this, init); + } +} diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 543358a54..24ecefa6e 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -4,6 +4,7 @@ import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { PairInfoModel } from './pair-info.model'; import { SimpleLockModel } from 'src/modules/simple-lock/models/simple.lock.model'; import { FeesCollectorModel } from 'src/modules/fees-collector/models/fees-collector.model'; +import { PairCompoundedAPRModel } from './pair.compounded.apr.model'; @ArgsType() export class GetPairsArgs extends PaginationArgs {} @@ -150,6 +151,9 @@ export class PairModel { @Field() deployedAt: number; + @Field(() => PairCompoundedAPRModel, { nullable: true }) + compoundedAPR: PairCompoundedAPRModel; + constructor(init?: Partial) { Object.assign(this, init); } diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 10b9c63fd..625a0a57a 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -27,6 +27,24 @@ import { PairComputeService } from './services/pair.compute.service'; import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { FeesCollectorModel } from '../fees-collector/models/fees-collector.model'; import { constantsConfig } from 'src/config'; +import { PairCompoundedAPRModel } from './models/pair.compounded.apr.model'; +import { GenericResolver } from 'src/services/generics/generic.resolver'; + +@Resolver(() => PairCompoundedAPRModel) +export class PairCompoundedAPRResolver extends GenericResolver { + constructor(private readonly pairCompute: PairComputeService) { + super(); + } + + @ResolveField(() => String) + async feesAPR(@Parent() parent: PairModel): Promise { + console.log(parent); + return this.pairCompute.feesAPR(parent.address); + return await this.genericFieldResolver(() => + this.pairCompute.feesAPR(parent.address), + ); + } +} @Resolver(() => PairModel) export class PairResolver { @@ -229,6 +247,13 @@ export class PairResolver { return this.pairCompute.deployedAt(parent.address); } + @ResolveField(() => PairCompoundedAPRModel, { nullable: true }) + async compoundedAPR( + @Parent() parent: PairModel, + ): Promise { + return this.pairCompute.computeCompoundedAPR(parent.address); + } + @Query(() => String) async getAmountOut( @Args('pairAddress') pairAddress: string, diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 471dfcf38..d688153d9 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -23,6 +23,8 @@ import { MXApiService } from 'src/services/multiversx-communication/mx.api.servi import { FarmVersion } from 'src/modules/farm/models/farm.model'; import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; import { TransactionStatus } from 'src/utils/transaction.utils'; +import { PairCompoundedAPRModel } from '../models/pair.compounded.apr.model'; +import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; @Injectable() export class PairComputeService implements IPairComputeService { @@ -42,6 +44,8 @@ export class PairComputeService implements IPairComputeService { private readonly stakingProxyAbiService: StakingProxyAbiService, private readonly elasticService: ElasticService, private readonly apiService: MXApiService, + @Inject(forwardRef(() => FarmComputeServiceV2)) + private readonly farmCompute: FarmComputeServiceV2, ) {} async getTokenPrice(pairAddress: string, tokenID: string): Promise { @@ -654,4 +658,86 @@ export class PairComputeService implements IPairComputeService { ); return deployedAt ?? undefined; } + + async computeCompoundedAPR( + pairAddress: string, + ): Promise { + const [feesAPR, farmAPRs, dualFarmAPRs] = await Promise.all([ + this.feesAPR(pairAddress), + this.computeFarmAPRs(pairAddress), + this.computeDualFarmAPRs(pairAddress), + ]); + + return new PairCompoundedAPRModel({ + feesAPR: feesAPR, + farmBaseAPR: farmAPRs.base, + farmBoostedAPR: farmAPRs.boosted, + dualFarmAPR: dualFarmAPRs.base, + dualFarmBoostedAPR: dualFarmAPRs.boosted, + }); + } + + async computeFarmAPRs( + pairAddress: string, + ): Promise<{ base: string; boosted: string }> { + const hasFarms = await this.hasFarms(pairAddress); + + if (!hasFarms) { + return { base: '0', boosted: '0' }; + } + + const addresses: string[] = farmsAddresses([FarmVersion.V2]); + const lpTokenID = await this.pairAbi.lpTokenID(pairAddress); + + const farmingTokenIDs = await Promise.all( + addresses.map((address) => this.farmAbi.farmingTokenID(address)), + ); + + const farmAddressIndex = farmingTokenIDs.findIndex( + (tokenID) => tokenID === lpTokenID, + ); + + if (farmAddressIndex === -1) { + return { base: '0', boosted: '0' }; + } + + const baseAPR = await this.farmCompute.farmBaseAPR( + addresses[farmAddressIndex], + ); + + const [boostedYieldsFactors, boostedYieldsRewardsPercenatage] = + await Promise.all([ + this.farmAbi.boostedYieldsFactors(addresses[farmAddressIndex]), + this.farmAbi.boostedYieldsRewardsPercenatage( + addresses[farmAddressIndex], + ), + ]); + + const maxRewardsFactor = boostedYieldsFactors?.maxRewardsFactor ?? '0'; + + const bnRawMaxBoostedApr = new BigNumber(baseAPR) + .multipliedBy(maxRewardsFactor) + .multipliedBy(boostedYieldsRewardsPercenatage) + .dividedBy(10000 - boostedYieldsRewardsPercenatage); + + return { base: baseAPR, boosted: bnRawMaxBoostedApr.toFixed() }; + } + + async computeDualFarmAPRs( + pairAddress: string, + ): Promise<{ base: string; boosted: string }> { + const hasFarms = await this.hasDualFarms(pairAddress); + + if (!hasFarms) { + return { + base: '0', + boosted: '0', + }; + } + + return { + base: '11', + boosted: '11', + }; + } } From 119e4e2ac9a340c88ecf3fdfeb0dbbe0479f15f4 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 29 May 2024 15:53:02 +0300 Subject: [PATCH 173/313] SERVICES-2416: compute dual farm aprs --- src/modules/pair/pair.module.ts | 2 + .../pair/services/pair.compute.service.ts | 85 ++++++++++++++----- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/modules/pair/pair.module.ts b/src/modules/pair/pair.module.ts index 69279607f..9de907c99 100644 --- a/src/modules/pair/pair.module.ts +++ b/src/modules/pair/pair.module.ts @@ -19,6 +19,7 @@ import { StakingProxyModule } from '../staking-proxy/staking.proxy.module'; import { ElasticService } from 'src/helpers/elastic.service'; import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; import { PairFilteringService } from './services/pair.filtering.service'; +import { StakingModule } from '../staking/staking.module'; @Module({ imports: [ CommonAppModule, @@ -33,6 +34,7 @@ import { PairFilteringService } from './services/pair.filtering.service'; RemoteConfigModule, FarmModuleV2, StakingProxyModule, + StakingModule, ], providers: [ PairService, diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index d688153d9..ab3adc032 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -25,6 +25,7 @@ import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.servi import { TransactionStatus } from 'src/utils/transaction.utils'; import { PairCompoundedAPRModel } from '../models/pair.compounded.apr.model'; import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; +import { StakingComputeService } from 'src/modules/staking/services/staking.compute.service'; @Injectable() export class PairComputeService implements IPairComputeService { @@ -46,6 +47,7 @@ export class PairComputeService implements IPairComputeService { private readonly apiService: MXApiService, @Inject(forwardRef(() => FarmComputeServiceV2)) private readonly farmCompute: FarmComputeServiceV2, + private readonly stakingCompute: StakingComputeService, ) {} async getTokenPrice(pairAddress: string, tokenID: string): Promise { @@ -677,13 +679,11 @@ export class PairComputeService implements IPairComputeService { }); } - async computeFarmAPRs( - pairAddress: string, - ): Promise<{ base: string; boosted: string }> { + async computePairFarmAddress(pairAddress: string): Promise { const hasFarms = await this.hasFarms(pairAddress); if (!hasFarms) { - return { base: '0', boosted: '0' }; + return undefined; } const addresses: string[] = farmsAddresses([FarmVersion.V2]); @@ -698,27 +698,34 @@ export class PairComputeService implements IPairComputeService { ); if (farmAddressIndex === -1) { - return { base: '0', boosted: '0' }; + return undefined; } - const baseAPR = await this.farmCompute.farmBaseAPR( - addresses[farmAddressIndex], - ); + return addresses[farmAddressIndex]; + } + + async computeFarmAPRs( + pairAddress: string, + ): Promise<{ base: string; boosted: string }> { + const farmAddress = await this.computePairFarmAddress(pairAddress); + + if (!farmAddress) { + return { base: '0', boosted: '0' }; + } + const baseAPR = await this.farmCompute.farmBaseAPR(farmAddress); - const [boostedYieldsFactors, boostedYieldsRewardsPercenatage] = + const [boostedYieldsFactors, boostedYieldsRewardsPercentage] = await Promise.all([ - this.farmAbi.boostedYieldsFactors(addresses[farmAddressIndex]), - this.farmAbi.boostedYieldsRewardsPercenatage( - addresses[farmAddressIndex], - ), + this.farmAbi.boostedYieldsFactors(farmAddress), + this.farmAbi.boostedYieldsRewardsPercenatage(farmAddress), ]); const maxRewardsFactor = boostedYieldsFactors?.maxRewardsFactor ?? '0'; const bnRawMaxBoostedApr = new BigNumber(baseAPR) .multipliedBy(maxRewardsFactor) - .multipliedBy(boostedYieldsRewardsPercenatage) - .dividedBy(10000 - boostedYieldsRewardsPercenatage); + .multipliedBy(boostedYieldsRewardsPercentage) + .dividedBy(10000 - boostedYieldsRewardsPercentage); return { base: baseAPR, boosted: bnRawMaxBoostedApr.toFixed() }; } @@ -726,18 +733,50 @@ export class PairComputeService implements IPairComputeService { async computeDualFarmAPRs( pairAddress: string, ): Promise<{ base: string; boosted: string }> { - const hasFarms = await this.hasDualFarms(pairAddress); + const hasDualFarms = await this.hasDualFarms(pairAddress); - if (!hasFarms) { - return { - base: '0', - boosted: '0', - }; + if (!hasDualFarms) { + return { base: '0', boosted: '0' }; + } + + const stakingProxyAddresses = + await this.remoteConfigGetterService.getStakingProxyAddresses(); + + const pairAddresses = await Promise.all( + stakingProxyAddresses.map((address) => + this.stakingProxyAbiService.pairAddress(address), + ), + ); + + const stakingProxyIndex = pairAddresses.findIndex( + (address) => address === pairAddress, + ); + + if (stakingProxyIndex === -1) { + return { base: '0', boosted: '0' }; } + const [stakingFarmAddress, farmAddress] = await Promise.all([ + this.stakingProxyAbiService.stakingFarmAddress( + stakingProxyAddresses[stakingProxyIndex], + ), + this.computePairFarmAddress(pairAddress), + ]); + + const boostedYieldsRewardsPercentage = + await this.farmAbi.boostedYieldsRewardsPercenatage(farmAddress); + + const baseAPR = await this.stakingCompute.stakeFarmAPR( + stakingFarmAddress, + ); + + const rawBoostedApr = new BigNumber(baseAPR).multipliedBy( + new BigNumber(boostedYieldsRewardsPercentage).dividedBy(100), + ); + return { - base: '11', - boosted: '11', + base: baseAPR, + boosted: rawBoostedApr.toFixed(), }; } } From e2437768881dc92fb748e1265e1e34b141695380 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 29 May 2024 17:50:27 +0300 Subject: [PATCH 174/313] SERVICES-2416: fix apr calculation --- .../pair/services/pair.compute.service.ts | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index ab3adc032..094f05c15 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -26,6 +26,7 @@ import { TransactionStatus } from 'src/utils/transaction.utils'; import { PairCompoundedAPRModel } from '../models/pair.compounded.apr.model'; import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; import { StakingComputeService } from 'src/modules/staking/services/staking.compute.service'; +import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; @Injectable() export class PairComputeService implements IPairComputeService { @@ -48,6 +49,7 @@ export class PairComputeService implements IPairComputeService { @Inject(forwardRef(() => FarmComputeServiceV2)) private readonly farmCompute: FarmComputeServiceV2, private readonly stakingCompute: StakingComputeService, + private readonly stakingAbi: StakingAbiService, ) {} async getTokenPrice(pairAddress: string, tokenID: string): Promise { @@ -671,7 +673,7 @@ export class PairComputeService implements IPairComputeService { ]); return new PairCompoundedAPRModel({ - feesAPR: feesAPR, + feesAPR: new BigNumber(feesAPR).multipliedBy(100).toFixed(), farmBaseAPR: farmAPRs.base, farmBoostedAPR: farmAPRs.boosted, dualFarmAPR: dualFarmAPRs.base, @@ -722,12 +724,17 @@ export class PairComputeService implements IPairComputeService { const maxRewardsFactor = boostedYieldsFactors?.maxRewardsFactor ?? '0'; - const bnRawMaxBoostedApr = new BigNumber(baseAPR) + const bnBaseApr = new BigNumber(baseAPR).multipliedBy(100); + + const bnRawMaxBoostedApr = bnBaseApr .multipliedBy(maxRewardsFactor) .multipliedBy(boostedYieldsRewardsPercentage) .dividedBy(10000 - boostedYieldsRewardsPercentage); - return { base: baseAPR, boosted: bnRawMaxBoostedApr.toFixed() }; + return { + base: bnBaseApr.toFixed(), + boosted: bnRawMaxBoostedApr.toFixed(), + }; } async computeDualFarmAPRs( @@ -756,26 +763,37 @@ export class PairComputeService implements IPairComputeService { return { base: '0', boosted: '0' }; } - const [stakingFarmAddress, farmAddress] = await Promise.all([ - this.stakingProxyAbiService.stakingFarmAddress( + const stakingFarmAddress = + await this.stakingProxyAbiService.stakingFarmAddress( stakingProxyAddresses[stakingProxyIndex], - ), - this.computePairFarmAddress(pairAddress), - ]); + ); - const boostedYieldsRewardsPercentage = - await this.farmAbi.boostedYieldsRewardsPercenatage(farmAddress); + const [boostedYieldsRewardsPercentage, annualPercentageRewards] = + await Promise.all([ + this.stakingAbi.boostedYieldsRewardsPercenatage( + stakingFarmAddress, + ), + this.stakingAbi.annualPercentageRewards(stakingFarmAddress), + ]); - const baseAPR = await this.stakingCompute.stakeFarmAPR( - stakingFarmAddress, - ); + const bnBoostedRewardsPercentage = new BigNumber( + boostedYieldsRewardsPercentage ?? 0, + ) + .dividedBy(10000) + .multipliedBy(100); - const rawBoostedApr = new BigNumber(baseAPR).multipliedBy( - new BigNumber(boostedYieldsRewardsPercentage).dividedBy(100), + const apr = await this.stakingCompute.stakeFarmAPR(stakingFarmAddress); + + const bnApr = new BigNumber(apr).multipliedBy(100); + + const rawBoostedApr = bnApr.multipliedBy( + bnBoostedRewardsPercentage.dividedBy(100), ); + const bnBaseApr = bnApr.minus(rawBoostedApr); + return { - base: baseAPR, + base: bnBaseApr.toFixed(), boosted: rawBoostedApr.toFixed(), }; } From 108da384eff688211fe11b7012b65abec5c179fa Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 30 May 2024 09:23:51 +0300 Subject: [PATCH 175/313] SERVICES-2416: cache APRs in pairs compute service --- src/modules/pair/pair.resolver.ts | 32 +++- .../pair/services/pair.compute.service.ts | 138 ++++++++++++++---- 2 files changed, 139 insertions(+), 31 deletions(-) diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 625a0a57a..f49cae4f1 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -38,12 +38,38 @@ export class PairCompoundedAPRResolver extends GenericResolver { @ResolveField(() => String) async feesAPR(@Parent() parent: PairModel): Promise { - console.log(parent); - return this.pairCompute.feesAPR(parent.address); return await this.genericFieldResolver(() => this.pairCompute.feesAPR(parent.address), ); } + + @ResolveField(() => String) + async farmBaseAPR(@Parent() parent: PairModel): Promise { + return await this.genericFieldResolver(() => + this.pairCompute.farmBaseAPR(parent.address), + ); + } + + @ResolveField(() => String) + async farmBoostedAPR(@Parent() parent: PairModel): Promise { + return await this.genericFieldResolver(() => + this.pairCompute.farmBoostedAPR(parent.address), + ); + } + + @ResolveField(() => String) + async dualFarmBaseAPR(@Parent() parent: PairModel): Promise { + return await this.genericFieldResolver(() => + this.pairCompute.dualFarmBaseAPR(parent.address), + ); + } + + @ResolveField(() => String) + async dualFarmBoostedAPR(@Parent() parent: PairModel): Promise { + return await this.genericFieldResolver(() => + this.pairCompute.dualFarmBoostedAPR(parent.address), + ); + } } @Resolver(() => PairModel) @@ -251,7 +277,7 @@ export class PairResolver { async compoundedAPR( @Parent() parent: PairModel, ): Promise { - return this.pairCompute.computeCompoundedAPR(parent.address); + return this.pairCompute.compoundedAPR(parent.address); } @Query(() => String) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 094f05c15..b7a0189f1 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -663,13 +663,25 @@ export class PairComputeService implements IPairComputeService { return deployedAt ?? undefined; } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async compoundedAPR(pairAddress: string): Promise { + return await this.computeCompoundedAPR(pairAddress); + } + async computeCompoundedAPR( pairAddress: string, ): Promise { const [feesAPR, farmAPRs, dualFarmAPRs] = await Promise.all([ this.feesAPR(pairAddress), - this.computeFarmAPRs(pairAddress), - this.computeDualFarmAPRs(pairAddress), + this.farmAPRs(pairAddress), + this.dualFarmAPRs(pairAddress), ]); return new PairCompoundedAPRModel({ @@ -681,6 +693,18 @@ export class PairComputeService implements IPairComputeService { }); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async pairFarmAddress(pairAddress: string): Promise { + return await this.computePairFarmAddress(pairAddress); + } + async computePairFarmAddress(pairAddress: string): Promise { const hasFarms = await this.hasFarms(pairAddress); @@ -706,26 +730,48 @@ export class PairComputeService implements IPairComputeService { return addresses[farmAddressIndex]; } + async farmBaseAPR(pairAddress: string): Promise { + const farmAPRs = await this.farmAPRs(pairAddress); + return farmAPRs.base; + } + + async farmBoostedAPR(pairAddress: string): Promise { + const farmAPRs = await this.farmAPRs(pairAddress); + return farmAPRs.boosted; + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async farmAPRs( + pairAddress: string, + ): Promise<{ base: string; boosted: string }> { + return await this.computeFarmAPRs(pairAddress); + } + async computeFarmAPRs( pairAddress: string, ): Promise<{ base: string; boosted: string }> { - const farmAddress = await this.computePairFarmAddress(pairAddress); + const farmAddress = await this.pairFarmAddress(pairAddress); if (!farmAddress) { return { base: '0', boosted: '0' }; } - const baseAPR = await this.farmCompute.farmBaseAPR(farmAddress); - const [boostedYieldsFactors, boostedYieldsRewardsPercentage] = + const [baseAPR, boostedYieldsFactors, boostedYieldsRewardsPercentage] = await Promise.all([ + this.farmCompute.farmBaseAPR(farmAddress), this.farmAbi.boostedYieldsFactors(farmAddress), this.farmAbi.boostedYieldsRewardsPercenatage(farmAddress), ]); const maxRewardsFactor = boostedYieldsFactors?.maxRewardsFactor ?? '0'; - const bnBaseApr = new BigNumber(baseAPR).multipliedBy(100); - const bnRawMaxBoostedApr = bnBaseApr .multipliedBy(maxRewardsFactor) .multipliedBy(boostedYieldsRewardsPercentage) @@ -737,13 +783,23 @@ export class PairComputeService implements IPairComputeService { }; } - async computeDualFarmAPRs( - pairAddress: string, - ): Promise<{ base: string; boosted: string }> { + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async pairStakingFarmAddress(pairAddress: string): Promise { + return await this.computePairStakingFarmAddress(pairAddress); + } + + async computePairStakingFarmAddress(pairAddress: string): Promise { const hasDualFarms = await this.hasDualFarms(pairAddress); if (!hasDualFarms) { - return { base: '0', boosted: '0' }; + return undefined; } const stakingProxyAddresses = @@ -760,21 +816,51 @@ export class PairComputeService implements IPairComputeService { ); if (stakingProxyIndex === -1) { - return { base: '0', boosted: '0' }; + return undefined; } - const stakingFarmAddress = - await this.stakingProxyAbiService.stakingFarmAddress( - stakingProxyAddresses[stakingProxyIndex], - ); + return stakingProxyAddresses[stakingProxyIndex]; + } - const [boostedYieldsRewardsPercentage, annualPercentageRewards] = - await Promise.all([ - this.stakingAbi.boostedYieldsRewardsPercenatage( - stakingFarmAddress, - ), - this.stakingAbi.annualPercentageRewards(stakingFarmAddress), - ]); + async dualFarmBaseAPR(pairAddress: string): Promise { + const dualFarmAPRs = await this.dualFarmAPRs(pairAddress); + return dualFarmAPRs.base; + } + + async dualFarmBoostedAPR(pairAddress: string): Promise { + const dualFarmAPRs = await this.dualFarmAPRs(pairAddress); + return dualFarmAPRs.boosted; + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async dualFarmAPRs( + pairAddress: string, + ): Promise<{ base: string; boosted: string }> { + return await this.computeDualFarmAPRs(pairAddress); + } + + async computeDualFarmAPRs( + pairAddress: string, + ): Promise<{ base: string; boosted: string }> { + const stakingFarmAddress = await this.pairStakingFarmAddress( + pairAddress, + ); + + if (!stakingFarmAddress) { + return { base: '0', boosted: '0' }; + } + + const [boostedYieldsRewardsPercentage, apr] = await Promise.all([ + this.stakingAbi.boostedYieldsRewardsPercenatage(stakingFarmAddress), + this.stakingCompute.stakeFarmAPR(stakingFarmAddress), + ]); const bnBoostedRewardsPercentage = new BigNumber( boostedYieldsRewardsPercentage ?? 0, @@ -782,18 +868,14 @@ export class PairComputeService implements IPairComputeService { .dividedBy(10000) .multipliedBy(100); - const apr = await this.stakingCompute.stakeFarmAPR(stakingFarmAddress); - const bnApr = new BigNumber(apr).multipliedBy(100); const rawBoostedApr = bnApr.multipliedBy( bnBoostedRewardsPercentage.dividedBy(100), ); - const bnBaseApr = bnApr.minus(rawBoostedApr); - return { - base: bnBaseApr.toFixed(), + base: bnApr.minus(rawBoostedApr).toFixed(), boosted: rawBoostedApr.toFixed(), }; } From 4dfc757130e894eabc07b3e500f52444f7a084c8 Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 30 May 2024 11:54:30 +0300 Subject: [PATCH 176/313] SERVICES-2416: fix bad staking address --- src/modules/pair/services/pair.compute.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index b7a0189f1..e1832d458 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -819,7 +819,9 @@ export class PairComputeService implements IPairComputeService { return undefined; } - return stakingProxyAddresses[stakingProxyIndex]; + return await this.stakingProxyAbiService.stakingFarmAddress( + stakingProxyAddresses[stakingProxyIndex], + ); } async dualFarmBaseAPR(pairAddress: string): Promise { From ae989371d64f9f1ada4aedf057c291e1cd5b5eae Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 30 May 2024 17:26:40 +0300 Subject: [PATCH 177/313] SERVICES-2416: add PairCompoundedAPRResolver provider to enable --- .../pair/models/pair.compounded.apr.model.ts | 3 +++ src/modules/pair/pair.module.ts | 3 ++- src/modules/pair/pair.resolver.ts | 20 +++++++++++++------ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/modules/pair/models/pair.compounded.apr.model.ts b/src/modules/pair/models/pair.compounded.apr.model.ts index 890802cc4..bb22dfb3d 100644 --- a/src/modules/pair/models/pair.compounded.apr.model.ts +++ b/src/modules/pair/models/pair.compounded.apr.model.ts @@ -2,6 +2,9 @@ import { ObjectType, Field } from '@nestjs/graphql'; @ObjectType() export class PairCompoundedAPRModel { + @Field() + address: string; + @Field({ nullable: true }) feesAPR: string; diff --git a/src/modules/pair/pair.module.ts b/src/modules/pair/pair.module.ts index 9de907c99..acb139d6b 100644 --- a/src/modules/pair/pair.module.ts +++ b/src/modules/pair/pair.module.ts @@ -1,6 +1,6 @@ import { forwardRef, Module } from '@nestjs/common'; import { PairService } from './services/pair.service'; -import { PairResolver } from './pair.resolver'; +import { PairCompoundedAPRResolver, PairResolver } from './pair.resolver'; import { PairAbiService } from './services/pair.abi.service'; import { PairTransactionService } from './services/pair.transactions.service'; import { ContextModule } from '../../services/context/context.module'; @@ -45,6 +45,7 @@ import { StakingModule } from '../staking/staking.module'; PairResolver, ElasticService, PairFilteringService, + PairCompoundedAPRResolver, ], exports: [ PairService, diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index f49cae4f1..520b6ad1c 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -37,35 +37,43 @@ export class PairCompoundedAPRResolver extends GenericResolver { } @ResolveField(() => String) - async feesAPR(@Parent() parent: PairModel): Promise { + async feesAPR(@Parent() parent: PairCompoundedAPRModel): Promise { return await this.genericFieldResolver(() => this.pairCompute.feesAPR(parent.address), ); } @ResolveField(() => String) - async farmBaseAPR(@Parent() parent: PairModel): Promise { + async farmBaseAPR( + @Parent() parent: PairCompoundedAPRModel, + ): Promise { return await this.genericFieldResolver(() => this.pairCompute.farmBaseAPR(parent.address), ); } @ResolveField(() => String) - async farmBoostedAPR(@Parent() parent: PairModel): Promise { + async farmBoostedAPR( + @Parent() parent: PairCompoundedAPRModel, + ): Promise { return await this.genericFieldResolver(() => this.pairCompute.farmBoostedAPR(parent.address), ); } @ResolveField(() => String) - async dualFarmBaseAPR(@Parent() parent: PairModel): Promise { + async dualFarmBaseAPR( + @Parent() parent: PairCompoundedAPRModel, + ): Promise { return await this.genericFieldResolver(() => this.pairCompute.dualFarmBaseAPR(parent.address), ); } @ResolveField(() => String) - async dualFarmBoostedAPR(@Parent() parent: PairModel): Promise { + async dualFarmBoostedAPR( + @Parent() parent: PairCompoundedAPRModel, + ): Promise { return await this.genericFieldResolver(() => this.pairCompute.dualFarmBoostedAPR(parent.address), ); @@ -277,7 +285,7 @@ export class PairResolver { async compoundedAPR( @Parent() parent: PairModel, ): Promise { - return this.pairCompute.compoundedAPR(parent.address); + return new PairCompoundedAPRModel({ address: parent.address }); } @Query(() => String) From ee72479b6e0129f3424f33614d0b43fba8abd695 Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 30 May 2024 18:38:26 +0300 Subject: [PATCH 178/313] SERVICES-2416: move boosted APR computation to farm v2 compute service --- .../v2/services/farm.v2.compute.service.ts | 30 +++++++++++++ .../pair/models/pair.compounded.apr.model.ts | 2 +- src/modules/pair/pair.resolver.ts | 42 ++++++++++++++----- .../pair/services/pair.compute.service.ts | 2 +- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index 7b9a9e337..c112299a9 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -529,4 +529,34 @@ export class FarmComputeServiceV2 return total + current; }); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async maxBoostedApr(farmAddress: string): Promise { + return await this.computeMaxBoostedApr(farmAddress); + } + + async computeMaxBoostedApr(farmAddress: string): Promise { + const [baseAPR, boostedYieldsFactors, boostedYieldsRewardsPercentage] = + await Promise.all([ + this.farmBaseAPR(farmAddress), + this.farmAbi.boostedYieldsFactors(farmAddress), + this.farmAbi.boostedYieldsRewardsPercenatage(farmAddress), + ]); + + const bnRawMaxBoostedApr = new BigNumber(baseAPR) + .multipliedBy(boostedYieldsFactors.maxRewardsFactor) + .multipliedBy(boostedYieldsRewardsPercentage) + .dividedBy( + constantsConfig.MAX_PERCENT - boostedYieldsRewardsPercentage, + ); + + return bnRawMaxBoostedApr.toFixed(); + } } diff --git a/src/modules/pair/models/pair.compounded.apr.model.ts b/src/modules/pair/models/pair.compounded.apr.model.ts index bb22dfb3d..aa31c6948 100644 --- a/src/modules/pair/models/pair.compounded.apr.model.ts +++ b/src/modules/pair/models/pair.compounded.apr.model.ts @@ -15,7 +15,7 @@ export class PairCompoundedAPRModel { farmBoostedAPR: string; @Field({ nullable: true }) - dualFarmAPR: string; + dualFarmBaseAPR: string; @Field({ nullable: true }) dualFarmBoostedAPR: string; diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 520b6ad1c..8bfe3f8c4 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -29,45 +29,67 @@ import { FeesCollectorModel } from '../fees-collector/models/fees-collector.mode import { constantsConfig } from 'src/config'; import { PairCompoundedAPRModel } from './models/pair.compounded.apr.model'; import { GenericResolver } from 'src/services/generics/generic.resolver'; +import { FarmComputeServiceV2 } from '../farm/v2/services/farm.v2.compute.service'; +import { StakingComputeService } from '../staking/services/staking.compute.service'; @Resolver(() => PairCompoundedAPRModel) export class PairCompoundedAPRResolver extends GenericResolver { - constructor(private readonly pairCompute: PairComputeService) { + constructor( + private readonly pairCompute: PairComputeService, + private readonly farmCompute: FarmComputeServiceV2, + private readonly stakingCompute: StakingComputeService, + ) { super(); } @ResolveField(() => String) async feesAPR(@Parent() parent: PairCompoundedAPRModel): Promise { - return await this.genericFieldResolver(() => - this.pairCompute.feesAPR(parent.address), - ); + return await this.pairCompute.feesAPR(parent.address); } @ResolveField(() => String) async farmBaseAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - return await this.genericFieldResolver(() => - this.pairCompute.farmBaseAPR(parent.address), + const farmAddress = await this.pairCompute.pairFarmAddress( + parent.address, ); + + if (!farmAddress) { + return '0'; + } + + return await this.farmCompute.farmBaseAPR(farmAddress); } @ResolveField(() => String) async farmBoostedAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - return await this.genericFieldResolver(() => - this.pairCompute.farmBoostedAPR(parent.address), + const farmAddress = await this.pairCompute.pairFarmAddress( + parent.address, ); + + if (!farmAddress) { + return '0'; + } + + return await this.farmCompute.maxBoostedApr(farmAddress); } @ResolveField(() => String) async dualFarmBaseAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - return await this.genericFieldResolver(() => - this.pairCompute.dualFarmBaseAPR(parent.address), + const stakingAddress = await this.pairCompute.pairStakingFarmAddress( + parent.address, ); + + if (!stakingAddress) { + return '0'; + } + + return await this.stakingCompute.stakeFarmAPR(stakingAddress); } @ResolveField(() => String) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index e1832d458..7ba7405c1 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -688,7 +688,7 @@ export class PairComputeService implements IPairComputeService { feesAPR: new BigNumber(feesAPR).multipliedBy(100).toFixed(), farmBaseAPR: farmAPRs.base, farmBoostedAPR: farmAPRs.boosted, - dualFarmAPR: dualFarmAPRs.base, + dualFarmBaseAPR: dualFarmAPRs.base, dualFarmBoostedAPR: dualFarmAPRs.boosted, }); } From d6e27d74ad6a39ffe6d1ef83991e485533fb8ffe Mon Sep 17 00:00:00 2001 From: hschiau Date: Fri, 31 May 2024 09:12:58 +0300 Subject: [PATCH 179/313] SERVICES-2416: remove APRs computation logic from pair compute service - move dual farm boosted APR computation to staking compute service - update field resolver for dualFarmBoostedAPR --- src/modules/pair/pair.resolver.ts | 10 +- .../pair/services/pair.compute.service.ts | 149 ------------------ .../services/staking.compute.service.ts | 31 ++++ 3 files changed, 39 insertions(+), 151 deletions(-) diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 8bfe3f8c4..d649c571a 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -96,9 +96,15 @@ export class PairCompoundedAPRResolver extends GenericResolver { async dualFarmBoostedAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - return await this.genericFieldResolver(() => - this.pairCompute.dualFarmBoostedAPR(parent.address), + const stakingAddress = await this.pairCompute.pairStakingFarmAddress( + parent.address, ); + + if (!stakingAddress) { + return '0'; + } + + return await this.stakingCompute.boostedApr(stakingAddress); } } diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 7ba7405c1..65c0a4be4 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -23,10 +23,6 @@ import { MXApiService } from 'src/services/multiversx-communication/mx.api.servi import { FarmVersion } from 'src/modules/farm/models/farm.model'; import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; import { TransactionStatus } from 'src/utils/transaction.utils'; -import { PairCompoundedAPRModel } from '../models/pair.compounded.apr.model'; -import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; -import { StakingComputeService } from 'src/modules/staking/services/staking.compute.service'; -import { StakingAbiService } from 'src/modules/staking/services/staking.abi.service'; @Injectable() export class PairComputeService implements IPairComputeService { @@ -46,10 +42,6 @@ export class PairComputeService implements IPairComputeService { private readonly stakingProxyAbiService: StakingProxyAbiService, private readonly elasticService: ElasticService, private readonly apiService: MXApiService, - @Inject(forwardRef(() => FarmComputeServiceV2)) - private readonly farmCompute: FarmComputeServiceV2, - private readonly stakingCompute: StakingComputeService, - private readonly stakingAbi: StakingAbiService, ) {} async getTokenPrice(pairAddress: string, tokenID: string): Promise { @@ -663,36 +655,6 @@ export class PairComputeService implements IPairComputeService { return deployedAt ?? undefined; } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'pair', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async compoundedAPR(pairAddress: string): Promise { - return await this.computeCompoundedAPR(pairAddress); - } - - async computeCompoundedAPR( - pairAddress: string, - ): Promise { - const [feesAPR, farmAPRs, dualFarmAPRs] = await Promise.all([ - this.feesAPR(pairAddress), - this.farmAPRs(pairAddress), - this.dualFarmAPRs(pairAddress), - ]); - - return new PairCompoundedAPRModel({ - feesAPR: new BigNumber(feesAPR).multipliedBy(100).toFixed(), - farmBaseAPR: farmAPRs.base, - farmBoostedAPR: farmAPRs.boosted, - dualFarmBaseAPR: dualFarmAPRs.base, - dualFarmBoostedAPR: dualFarmAPRs.boosted, - }); - } - @ErrorLoggerAsync({ logArgs: true, }) @@ -730,59 +692,6 @@ export class PairComputeService implements IPairComputeService { return addresses[farmAddressIndex]; } - async farmBaseAPR(pairAddress: string): Promise { - const farmAPRs = await this.farmAPRs(pairAddress); - return farmAPRs.base; - } - - async farmBoostedAPR(pairAddress: string): Promise { - const farmAPRs = await this.farmAPRs(pairAddress); - return farmAPRs.boosted; - } - - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'pair', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async farmAPRs( - pairAddress: string, - ): Promise<{ base: string; boosted: string }> { - return await this.computeFarmAPRs(pairAddress); - } - - async computeFarmAPRs( - pairAddress: string, - ): Promise<{ base: string; boosted: string }> { - const farmAddress = await this.pairFarmAddress(pairAddress); - - if (!farmAddress) { - return { base: '0', boosted: '0' }; - } - - const [baseAPR, boostedYieldsFactors, boostedYieldsRewardsPercentage] = - await Promise.all([ - this.farmCompute.farmBaseAPR(farmAddress), - this.farmAbi.boostedYieldsFactors(farmAddress), - this.farmAbi.boostedYieldsRewardsPercenatage(farmAddress), - ]); - - const maxRewardsFactor = boostedYieldsFactors?.maxRewardsFactor ?? '0'; - const bnBaseApr = new BigNumber(baseAPR).multipliedBy(100); - const bnRawMaxBoostedApr = bnBaseApr - .multipliedBy(maxRewardsFactor) - .multipliedBy(boostedYieldsRewardsPercentage) - .dividedBy(10000 - boostedYieldsRewardsPercentage); - - return { - base: bnBaseApr.toFixed(), - boosted: bnRawMaxBoostedApr.toFixed(), - }; - } - @ErrorLoggerAsync({ logArgs: true, }) @@ -823,62 +732,4 @@ export class PairComputeService implements IPairComputeService { stakingProxyAddresses[stakingProxyIndex], ); } - - async dualFarmBaseAPR(pairAddress: string): Promise { - const dualFarmAPRs = await this.dualFarmAPRs(pairAddress); - return dualFarmAPRs.base; - } - - async dualFarmBoostedAPR(pairAddress: string): Promise { - const dualFarmAPRs = await this.dualFarmAPRs(pairAddress); - return dualFarmAPRs.boosted; - } - - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'pair', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async dualFarmAPRs( - pairAddress: string, - ): Promise<{ base: string; boosted: string }> { - return await this.computeDualFarmAPRs(pairAddress); - } - - async computeDualFarmAPRs( - pairAddress: string, - ): Promise<{ base: string; boosted: string }> { - const stakingFarmAddress = await this.pairStakingFarmAddress( - pairAddress, - ); - - if (!stakingFarmAddress) { - return { base: '0', boosted: '0' }; - } - - const [boostedYieldsRewardsPercentage, apr] = await Promise.all([ - this.stakingAbi.boostedYieldsRewardsPercenatage(stakingFarmAddress), - this.stakingCompute.stakeFarmAPR(stakingFarmAddress), - ]); - - const bnBoostedRewardsPercentage = new BigNumber( - boostedYieldsRewardsPercentage ?? 0, - ) - .dividedBy(10000) - .multipliedBy(100); - - const bnApr = new BigNumber(apr).multipliedBy(100); - - const rawBoostedApr = bnApr.multipliedBy( - bnBoostedRewardsPercentage.dividedBy(100), - ); - - return { - base: bnApr.minus(rawBoostedApr).toFixed(), - boosted: rawBoostedApr.toFixed(), - }; - } } diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index f25c8c549..75b99caee 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -727,4 +727,35 @@ export class StakingComputeService { return total + current; }); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async boostedApr(stakeAddress: string): Promise { + return await this.computeBoostedApr(stakeAddress); + } + + async computeBoostedApr(stakeAddress: string): Promise { + const [boostedYieldsRewardsPercentage, apr] = await Promise.all([ + this.stakingAbi.boostedYieldsRewardsPercenatage(stakeAddress), + this.stakeFarmAPR(stakeAddress), + ]); + + const bnBoostedRewardsPercentage = new BigNumber( + boostedYieldsRewardsPercentage, + ) + .dividedBy(10000) + .multipliedBy(100); + + const rawBoostedApr = new BigNumber(apr).multipliedBy( + bnBoostedRewardsPercentage.dividedBy(100), + ); + + return rawBoostedApr.toFixed(); + } } From 6de0dbb6e7f9aabae739cd136724987191885bb0 Mon Sep 17 00:00:00 2001 From: hschiau Date: Fri, 31 May 2024 09:13:57 +0300 Subject: [PATCH 180/313] SERVICES-2416: add 'boostedApr' field + resolver to Staking model --- src/modules/staking/models/staking.model.ts | 2 ++ src/modules/staking/staking.resolver.ts | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 1de0a8eec..06c41aa16 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -32,6 +32,8 @@ export class StakingModel { annualPercentageRewards: string; @Field() apr: string; + @Field() + boostedApr: string; @Field(() => Int) minUnboundEpochs: number; @Field() diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 8e5655bfa..e3c0c8030 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -93,6 +93,11 @@ export class StakingResolver { return this.stakingCompute.stakeFarmAPR(parent.address); } + @ResolveField() + async boostedApr(@Parent() parent: StakingModel) { + return this.stakingCompute.boostedApr(parent.address); + } + @ResolveField() async minUnboundEpochs(@Parent() parent: StakingModel) { return this.stakingAbi.minUnbondEpochs(parent.address); From f282845ce859488ff0c230514b94d8dee8c827d2 Mon Sep 17 00:00:00 2001 From: hschiau Date: Fri, 31 May 2024 09:16:22 +0300 Subject: [PATCH 181/313] SERVICES-2416: add 'boostedApr' field + resolver to Farm V2 model --- src/modules/farm/models/farm.v2.model.ts | 2 ++ src/modules/farm/v2/farm.v2.resolver.ts | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/modules/farm/models/farm.v2.model.ts b/src/modules/farm/models/farm.v2.model.ts index 0d14d880d..b8a6579ff 100644 --- a/src/modules/farm/models/farm.v2.model.ts +++ b/src/modules/farm/models/farm.v2.model.ts @@ -50,6 +50,8 @@ export class FarmModelV2 extends BaseFarmModel { @Field() baseApr: string; @Field() + boostedApr: string; + @Field() optimalEnergyPerLp: string; constructor(init?: Partial) { diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 1af0773ff..e976b42dd 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -61,6 +61,11 @@ export class FarmResolverV2 extends FarmResolver { return this.farmCompute.farmBaseAPR(parent.address); } + @ResolveField() + async boostedApr(@Parent() parent: FarmModelV2): Promise { + return this.farmCompute.maxBoostedApr(parent.address); + } + @ResolveField() async boosterRewards( @Parent() parent: FarmModelV2, From 8c5b49ffd240d32507e3f18b01eb64a3c2731a1c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 3 Jun 2024 17:23:08 +0300 Subject: [PATCH 182/313] MEX-480: use dynamic gas limit for energy position creator transaction Signed-off-by: Claudiu Lataretu --- src/config/default.json | 4 ++-- .../services/position.creator.transaction.ts | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index ba2809a52..ca1da8a30 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -390,7 +390,7 @@ }, "unstakeFarm": 18500000, "unbondFarm": 8000000, - "claimRewards": 10000000, + "claimRewards": 15000000, "claimBoostedRewards": 20000000, "claimRewardsWithNewValue": 7500000, "compoundRewards": 10000000, @@ -512,7 +512,7 @@ "dualFarmPosition": 67000000, "exitFarm": 50000000 }, - "energyPosition": 45000000 + "energyPosition": 10000000 }, "composableTasks": { "default": 1000000 diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index c713f67c8..6cb7c5130 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -709,6 +709,10 @@ export class PositionCreatorTransactionService { .integerValue(); const swapRouteArgs = this.serializeSwapRouteArgs(swapRoute); + const gasLimit = + gasConfig.positionCreator.energyPosition + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoute.pairs.length; const contract = await this.mxProxy.getLockedTokenPositionCreatorContract(); @@ -720,7 +724,7 @@ export class PositionCreatorTransactionService { ...swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.energyPosition) + .withGasLimit(gasLimit) .withChainID(mxConfig.chainID); if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { From ae04bf4b16ccff02d4b57bd5e2c70484cea856c3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 3 Jun 2024 17:26:29 +0300 Subject: [PATCH 183/313] MEX-480: update gas limits in unit test for energy position creator Signed-off-by: Claudiu Lataretu --- .../specs/position.creator.transaction.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 535886c44..c7e96136f 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -2268,7 +2268,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: gasConfig.positionCreator.energyPosition, + gasLimit: 40000000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -2320,7 +2320,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: gasConfig.positionCreator.energyPosition, + gasLimit: 40000000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From fdb04c8a12179a27fced81ea7a58f03632101ba7 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 3 Jun 2024 18:23:48 +0300 Subject: [PATCH 184/313] MEX-481: add guard for position creator dual tokens transactions Signed-off-by: Claudiu Lataretu --- .../position-creator/position.creator.transaction.resolver.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index eb6ba0c33..c15de5175 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -335,6 +335,7 @@ export class PositionCreatorTransactionResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) @Query(() => [TransactionModel]) async createFarmPositionDualTokens( @AuthUser() user: UserAuthResult, @@ -358,6 +359,7 @@ export class PositionCreatorTransactionResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) @Query(() => [TransactionModel]) async createDualFarmPositionDualTokens( @AuthUser() user: UserAuthResult, @@ -381,6 +383,7 @@ export class PositionCreatorTransactionResolver { ); } + @UseGuards(JwtOrNativeAuthGuard) @Query(() => TransactionModel) async exitFarmPositionDualTokens( @AuthUser() user: UserAuthResult, From 5d4a8c16ef447bf71c14779aa111c883911383e3 Mon Sep 17 00:00:00 2001 From: hschiau Date: Mon, 17 Jun 2024 14:13:53 +0300 Subject: [PATCH 185/313] SERVICES-2416: fixes after review - rename methods for getting a pair's farm and staking farm address + remove caching - use MAX_PERCENTAGE const --- src/modules/pair/models/pair.model.ts | 26 ++++++++++++++++- src/modules/pair/pair.resolver.ts | 10 +++---- .../pair/services/pair.compute.service.ts | 28 ++----------------- .../services/staking.compute.service.ts | 2 +- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 24ecefa6e..4902c17e5 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -4,7 +4,6 @@ import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { PairInfoModel } from './pair-info.model'; import { SimpleLockModel } from 'src/modules/simple-lock/models/simple.lock.model'; import { FeesCollectorModel } from 'src/modules/fees-collector/models/fees-collector.model'; -import { PairCompoundedAPRModel } from './pair.compounded.apr.model'; @ArgsType() export class GetPairsArgs extends PaginationArgs {} @@ -42,6 +41,31 @@ export class LockedTokensInfo { } } +@ObjectType() +export class PairCompoundedAPRModel { + @Field() + address: string; + + @Field({ nullable: true }) + feesAPR: string; + + @Field({ nullable: true }) + farmBaseAPR: string; + + @Field({ nullable: true }) + farmBoostedAPR: string; + + @Field({ nullable: true }) + dualFarmBaseAPR: string; + + @Field({ nullable: true }) + dualFarmBoostedAPR: string; + + constructor(init?: Partial) { + Object.assign(this, init); + } +} + @ObjectType() export class PairModel { @Field() diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index d649c571a..37258f8d5 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -5,6 +5,7 @@ import { FeeDestination, LiquidityPosition, LockedTokensInfo, + PairCompoundedAPRModel, PairModel, } from './models/pair.model'; import { TransactionModel } from '../../models/transaction.model'; @@ -27,7 +28,6 @@ import { PairComputeService } from './services/pair.compute.service'; import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { FeesCollectorModel } from '../fees-collector/models/fees-collector.model'; import { constantsConfig } from 'src/config'; -import { PairCompoundedAPRModel } from './models/pair.compounded.apr.model'; import { GenericResolver } from 'src/services/generics/generic.resolver'; import { FarmComputeServiceV2 } from '../farm/v2/services/farm.v2.compute.service'; import { StakingComputeService } from '../staking/services/staking.compute.service'; @@ -51,7 +51,7 @@ export class PairCompoundedAPRResolver extends GenericResolver { async farmBaseAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - const farmAddress = await this.pairCompute.pairFarmAddress( + const farmAddress = await this.pairCompute.getPairFarmAddress( parent.address, ); @@ -66,7 +66,7 @@ export class PairCompoundedAPRResolver extends GenericResolver { async farmBoostedAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - const farmAddress = await this.pairCompute.pairFarmAddress( + const farmAddress = await this.pairCompute.getPairFarmAddress( parent.address, ); @@ -81,7 +81,7 @@ export class PairCompoundedAPRResolver extends GenericResolver { async dualFarmBaseAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - const stakingAddress = await this.pairCompute.pairStakingFarmAddress( + const stakingAddress = await this.pairCompute.getPairStakingFarmAddress( parent.address, ); @@ -96,7 +96,7 @@ export class PairCompoundedAPRResolver extends GenericResolver { async dualFarmBoostedAPR( @Parent() parent: PairCompoundedAPRModel, ): Promise { - const stakingAddress = await this.pairCompute.pairStakingFarmAddress( + const stakingAddress = await this.pairCompute.getPairStakingFarmAddress( parent.address, ); diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 65c0a4be4..03371c8f6 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -655,19 +655,7 @@ export class PairComputeService implements IPairComputeService { return deployedAt ?? undefined; } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'pair', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async pairFarmAddress(pairAddress: string): Promise { - return await this.computePairFarmAddress(pairAddress); - } - - async computePairFarmAddress(pairAddress: string): Promise { + async getPairFarmAddress(pairAddress: string): Promise { const hasFarms = await this.hasFarms(pairAddress); if (!hasFarms) { @@ -692,19 +680,7 @@ export class PairComputeService implements IPairComputeService { return addresses[farmAddressIndex]; } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'pair', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async pairStakingFarmAddress(pairAddress: string): Promise { - return await this.computePairStakingFarmAddress(pairAddress); - } - - async computePairStakingFarmAddress(pairAddress: string): Promise { + async getPairStakingFarmAddress(pairAddress: string): Promise { const hasDualFarms = await this.hasDualFarms(pairAddress); if (!hasDualFarms) { diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 51964dd46..6a39e0a95 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -777,7 +777,7 @@ export class StakingComputeService { const bnBoostedRewardsPercentage = new BigNumber( boostedYieldsRewardsPercentage, ) - .dividedBy(10000) + .dividedBy(constantsConfig.MAX_PERCENT) .multipliedBy(100); const rawBoostedApr = new BigNumber(apr).multipliedBy( From a9a139d043e0c881082e7a3540d0253910f42ef7 Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 20 Jun 2024 19:36:13 +0300 Subject: [PATCH 186/313] SERVICES-2469: add sorting by compounded APR to filteredPairs query --- .../pair/services/pair.compute.service.ts | 56 +++++++++++++++++++ src/modules/router/models/filter.args.ts | 1 + src/modules/router/services/router.service.ts | 7 +++ 3 files changed, 64 insertions(+) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index d5ad4bed5..497490714 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -23,6 +23,8 @@ import { MXApiService } from 'src/services/multiversx-communication/mx.api.servi import { FarmVersion } from 'src/modules/farm/models/farm.model'; import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; import { TransactionStatus } from 'src/utils/transaction.utils'; +import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; +import { StakingComputeService } from 'src/modules/staking/services/staking.compute.service'; @Injectable() export class PairComputeService implements IPairComputeService { @@ -42,6 +44,8 @@ export class PairComputeService implements IPairComputeService { private readonly stakingProxyAbiService: StakingProxyAbiService, private readonly elasticService: ElasticService, private readonly apiService: MXApiService, + private readonly farmCompute: FarmComputeServiceV2, + private readonly stakingCompute: StakingComputeService, ) {} async getTokenPrice(pairAddress: string, tokenID: string): Promise { @@ -771,4 +775,56 @@ export class PairComputeService implements IPairComputeService { stakingProxyAddresses[stakingProxyIndex], ); } + + async computeCompoundedApr(pairAddress: string): Promise { + const [feesAPR, farmAddress, stakingFarmAddress] = await Promise.all([ + this.feesAPR(pairAddress), + this.getPairFarmAddress(pairAddress), + this.getPairStakingFarmAddress(pairAddress), + ]); + + let feesAprBN = new BigNumber(feesAPR); + let farmBaseAprBN = new BigNumber('0'); + let farmBoostedAprBN = new BigNumber('0'); + let dualFarmBaseAprBN = new BigNumber('0'); + + if (farmAddress) { + const [farmBaseAPR, farmBoostedAPR] = await Promise.all([ + this.farmCompute.farmBaseAPR(farmAddress), + this.farmCompute.maxBoostedApr(farmAddress), + ]); + + farmBaseAprBN = new BigNumber(farmBaseAPR); + farmBoostedAprBN = new BigNumber(farmBoostedAPR); + + if (farmBaseAprBN.isNaN() || !farmBaseAprBN.isFinite()) { + farmBaseAprBN = new BigNumber(0); + } + if (farmBoostedAprBN.isNaN() || !farmBoostedAprBN.isFinite()) { + farmBoostedAprBN = new BigNumber(0); + } + } + + if (stakingFarmAddress) { + const dualFarmBaseAPR = await this.stakingCompute.stakeFarmAPR( + stakingFarmAddress, + ); + + dualFarmBaseAprBN = new BigNumber(dualFarmBaseAPR); + + if (dualFarmBaseAprBN.isNaN() || !dualFarmBaseAprBN.isFinite()) { + dualFarmBaseAprBN = new BigNumber(0); + } + } + + if (feesAprBN.isNaN() || !feesAprBN.isFinite()) { + feesAprBN = new BigNumber(0); + } + + return feesAprBN + .plus(farmBaseAprBN) + .plus(farmBoostedAprBN) + .plus(dualFarmBaseAprBN) + .toFixed(); + } } diff --git a/src/modules/router/models/filter.args.ts b/src/modules/router/models/filter.args.ts index 729ff2084..835e07821 100644 --- a/src/modules/router/models/filter.args.ts +++ b/src/modules/router/models/filter.args.ts @@ -7,6 +7,7 @@ export enum PairSortableFields { VOLUME_24 = 'volume_24h', FEES_24 = 'fees_24h', DEPLOYED_AT = 'deployed_at', + APR = 'apr', } registerEnumType(PairSortableFields, { name: 'PairSortableFields' }); diff --git a/src/modules/router/services/router.service.ts b/src/modules/router/services/router.service.ts index 46a684464..3d0527de5 100644 --- a/src/modules/router/services/router.service.ts +++ b/src/modules/router/services/router.service.ts @@ -323,6 +323,13 @@ export class RouterService { ), ); break; + case PairSortableFields.APR: + sortFieldData = await Promise.all( + pairsMetadata.map((pair) => + this.pairCompute.computeCompoundedApr(pair.address), + ), + ); + break; default: break; } From 045ca4560ba489a6a9a75ac073b3db26b9295197 Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 20 Jun 2024 19:36:44 +0300 Subject: [PATCH 187/313] SERVICES-2469: fix unit tests and imports --- .../v2/services/farm.v2.compute.service.ts | 1 + .../pair/specs/pair.compute.service.spec.ts | 22 +++++++++++++++++++ ...eekly-rewards-splitting.compute.service.ts | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index c112299a9..e8bf394e9 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -29,6 +29,7 @@ export class FarmComputeServiceV2 protected readonly farmAbi: FarmAbiServiceV2, @Inject(forwardRef(() => FarmServiceV2)) protected readonly farmService: FarmServiceV2, + @Inject(forwardRef(() => PairService)) protected readonly pairService: PairService, @Inject(forwardRef(() => PairComputeService)) protected readonly pairCompute: PairComputeService, diff --git a/src/modules/pair/specs/pair.compute.service.spec.ts b/src/modules/pair/specs/pair.compute.service.spec.ts index a445eecb9..1f2ebe65e 100644 --- a/src/modules/pair/specs/pair.compute.service.spec.ts +++ b/src/modules/pair/specs/pair.compute.service.spec.ts @@ -28,6 +28,17 @@ import { RemoteConfigGetterServiceProvider } from 'src/modules/remote-config/moc import { StakingProxyAbiServiceProvider } from 'src/modules/staking-proxy/mocks/staking.proxy.abi.service.mock'; import { FarmAbiServiceProviderV2 } from 'src/modules/farm/mocks/farm.v2.abi.service.mock'; import { ElasticSearchModule } from 'src/services/elastic-search/elastic.search.module'; +import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; +import { StakingComputeService } from 'src/modules/staking/services/staking.compute.service'; +import { FarmServiceV2 } from 'src/modules/farm/v2/services/farm.v2.service'; +import { WeekTimekeepingComputeService } from 'src/submodules/week-timekeeping/services/week-timekeeping.compute.service'; +import { WeeklyRewardsSplittingAbiServiceProvider } from 'src/submodules/weekly-rewards-splitting/mocks/weekly.rewards.splitting.abi.mock'; +import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; +import { WeekTimekeepingAbiServiceProvider } from 'src/submodules/week-timekeeping/mocks/week.timekeeping.abi.service.mock'; +import { StakingAbiServiceProvider } from 'src/modules/staking/mocks/staking.abi.service.mock'; +import { StakingService } from 'src/modules/staking/services/staking.service'; +import { StakingFilteringService } from 'src/modules/staking/services/staking.filtering.service'; +import { EnergyAbiServiceProvider } from 'src/modules/energy/mocks/energy.abi.service.mock'; describe('PairService', () => { let module: TestingModule; @@ -59,6 +70,17 @@ describe('PairService', () => { FarmAbiServiceProviderV2, RemoteConfigGetterServiceProvider, StakingProxyAbiServiceProvider, + FarmComputeServiceV2, + FarmServiceV2, + WeekTimekeepingComputeService, + WeekTimekeepingAbiServiceProvider, + WeeklyRewardsSplittingAbiServiceProvider, + WeeklyRewardsSplittingComputeService, + StakingComputeService, + StakingService, + StakingAbiServiceProvider, + StakingFilteringService, + EnergyAbiServiceProvider, ], }).compile(); }); diff --git a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts index b2811585f..ed7f96d7e 100644 --- a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts +++ b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable, forwardRef } from '@nestjs/common'; import BigNumber from 'bignumber.js'; import { EsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; import { TokenDistributionModel } from '../models/weekly-rewards-splitting.model'; @@ -20,7 +20,9 @@ export class WeeklyRewardsSplittingComputeService constructor( private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, private readonly energyAbi: EnergyAbiService, + @Inject(forwardRef(() => TokenComputeService)) private readonly tokenCompute: TokenComputeService, + @Inject(forwardRef(() => TokenService)) private readonly tokenService: TokenService, ) {} From 5bc480f8ece87d3e388f01a3943c827ae7ccfc9f Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 25 Jun 2024 13:04:20 +0300 Subject: [PATCH 188/313] SERVICES-2469: add 'dualFarmBoostedApr' to compute result --- .../pair/services/pair.compute.service.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 497490714..dc7fcbf77 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -787,6 +787,7 @@ export class PairComputeService implements IPairComputeService { let farmBaseAprBN = new BigNumber('0'); let farmBoostedAprBN = new BigNumber('0'); let dualFarmBaseAprBN = new BigNumber('0'); + let dualFarmBoostedAprBN = new BigNumber('0'); if (farmAddress) { const [farmBaseAPR, farmBoostedAPR] = await Promise.all([ @@ -806,15 +807,23 @@ export class PairComputeService implements IPairComputeService { } if (stakingFarmAddress) { - const dualFarmBaseAPR = await this.stakingCompute.stakeFarmAPR( - stakingFarmAddress, - ); + const [dualFarmBaseAPR, dualFarmBoostedAPR] = await Promise.all([ + this.stakingCompute.stakeFarmAPR(stakingFarmAddress), + this.stakingCompute.boostedApr(stakingFarmAddress), + ]); dualFarmBaseAprBN = new BigNumber(dualFarmBaseAPR); + dualFarmBoostedAprBN = new BigNumber(dualFarmBoostedAPR); if (dualFarmBaseAprBN.isNaN() || !dualFarmBaseAprBN.isFinite()) { dualFarmBaseAprBN = new BigNumber(0); } + if ( + dualFarmBoostedAprBN.isNaN() || + !dualFarmBoostedAprBN.isFinite() + ) { + dualFarmBoostedAprBN = new BigNumber(0); + } } if (feesAprBN.isNaN() || !feesAprBN.isFinite()) { @@ -825,6 +834,7 @@ export class PairComputeService implements IPairComputeService { .plus(farmBaseAprBN) .plus(farmBoostedAprBN) .plus(dualFarmBaseAprBN) + .plus(dualFarmBoostedAprBN) .toFixed(); } } From 5bf8646150b7dd622aed351b82f673a63b022848 Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 4 Jul 2024 18:19:26 +0300 Subject: [PATCH 189/313] SERVICES-2488: add pairRewardTokens model + resolver --- src/modules/pair/models/pair.model.ts | 16 +++++++++ src/modules/pair/pair.module.ts | 7 +++- src/modules/pair/pair.resolver.ts | 35 +++++++++++++++++++ .../pair/services/pair.compute.service.ts | 24 +++++++++---- 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 93b14d92a..1e1f077b0 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -66,6 +66,19 @@ export class PairCompoundedAPRModel { } } +@ObjectType() +export class PairRewardTokensModel { + @Field() + address: string; + + @Field({ nullable: true }) + dualFardRewardToken: EsdtToken; + + constructor(init?: Partial) { + Object.assign(this, init); + } +} + @ObjectType() export class PairModel { @Field() @@ -187,6 +200,9 @@ export class PairModel { @Field(() => PairCompoundedAPRModel, { nullable: true }) compoundedAPR: PairCompoundedAPRModel; + @Field(() => PairRewardTokensModel, { nullable: true }) + rewardTokens: PairRewardTokensModel; + constructor(init?: Partial) { Object.assign(this, init); } diff --git a/src/modules/pair/pair.module.ts b/src/modules/pair/pair.module.ts index acb139d6b..6ccf9d5dd 100644 --- a/src/modules/pair/pair.module.ts +++ b/src/modules/pair/pair.module.ts @@ -1,6 +1,10 @@ import { forwardRef, Module } from '@nestjs/common'; import { PairService } from './services/pair.service'; -import { PairCompoundedAPRResolver, PairResolver } from './pair.resolver'; +import { + PairCompoundedAPRResolver, + PairResolver, + PairRewardTokensResolver, +} from './pair.resolver'; import { PairAbiService } from './services/pair.abi.service'; import { PairTransactionService } from './services/pair.transactions.service'; import { ContextModule } from '../../services/context/context.module'; @@ -46,6 +50,7 @@ import { StakingModule } from '../staking/staking.module'; ElasticService, PairFilteringService, PairCompoundedAPRResolver, + PairRewardTokensResolver, ], exports: [ PairService, diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 134b0a0a2..bfa5aefff 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -7,6 +7,7 @@ import { LockedTokensInfo, PairCompoundedAPRModel, PairModel, + PairRewardTokensModel, } from './models/pair.model'; import { TransactionModel } from '../../models/transaction.model'; import { @@ -31,6 +32,33 @@ import { constantsConfig } from 'src/config'; import { GenericResolver } from 'src/services/generics/generic.resolver'; import { FarmComputeServiceV2 } from '../farm/v2/services/farm.v2.compute.service'; import { StakingComputeService } from '../staking/services/staking.compute.service'; +import { StakingProxyService } from '../staking-proxy/services/staking.proxy.service'; + +@Resolver(() => PairRewardTokensModel) +export class PairRewardTokensResolver extends GenericResolver { + constructor( + private readonly pairCompute: PairComputeService, + private readonly stakingProxyService: StakingProxyService, + ) { + super(); + } + + @ResolveField() + async dualFardRewardToken( + @Parent() parent: PairRewardTokensModel, + ): Promise { + const stakingProxyAddress = + await this.pairCompute.getPairStakingProxyAddress(parent.address); + + if (!stakingProxyAddress) { + return undefined; + } + + return await this.stakingProxyService.getStakingToken( + stakingProxyAddress, + ); + } +} @Resolver(() => PairCompoundedAPRModel) export class PairCompoundedAPRResolver extends GenericResolver { @@ -333,6 +361,13 @@ export class PairResolver { return new PairCompoundedAPRModel({ address: parent.address }); } + @ResolveField(() => PairRewardTokensModel, { nullable: true }) + async rewardTokens( + @Parent() parent: PairModel, + ): Promise { + return new PairRewardTokensModel({ address: parent.address }); + } + @Query(() => String) async getAmountOut( @Args('pairAddress') pairAddress: string, diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index dc7fcbf77..ba1b9f736 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -748,6 +748,20 @@ export class PairComputeService implements IPairComputeService { } async getPairStakingFarmAddress(pairAddress: string): Promise { + const stakingProxyAddress = await this.getPairStakingProxyAddress( + pairAddress, + ); + + if (!stakingProxyAddress) { + return undefined; + } + + return await this.stakingProxyAbiService.stakingFarmAddress( + stakingProxyAddress, + ); + } + + async getPairStakingProxyAddress(pairAddress: string): Promise { const hasDualFarms = await this.hasDualFarms(pairAddress); if (!hasDualFarms) { @@ -767,13 +781,9 @@ export class PairComputeService implements IPairComputeService { (address) => address === pairAddress, ); - if (stakingProxyIndex === -1) { - return undefined; - } - - return await this.stakingProxyAbiService.stakingFarmAddress( - stakingProxyAddresses[stakingProxyIndex], - ); + return stakingProxyIndex === -1 + ? undefined + : stakingProxyAddresses[stakingProxyIndex]; } async computeCompoundedApr(pairAddress: string): Promise { From 77ffbc2bb76534d73f73a02bdf1da0d09e469935 Mon Sep 17 00:00:00 2001 From: hschiau Date: Fri, 5 Jul 2024 10:18:29 +0300 Subject: [PATCH 190/313] SERVICES-2488: add pool and farm rewards to new model + rename fields --- src/modules/pair/models/pair.model.ts | 9 +++++++- src/modules/pair/pair.module.ts | 2 ++ src/modules/pair/pair.resolver.ts | 31 ++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 1e1f077b0..978dc3e07 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -4,6 +4,7 @@ import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { PairInfoModel } from './pair-info.model'; import { SimpleLockModel } from 'src/modules/simple-lock/models/simple.lock.model'; import { FeesCollectorModel } from 'src/modules/fees-collector/models/fees-collector.model'; +import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; @ArgsType() export class GetPairsArgs extends PaginationArgs {} @@ -71,8 +72,14 @@ export class PairRewardTokensModel { @Field() address: string; + @Field(() => [EsdtToken]) + poolRewards: EsdtToken[]; + + @Field({ nullable: true }) + farmReward: NftCollection; + @Field({ nullable: true }) - dualFardRewardToken: EsdtToken; + dualFarmReward: EsdtToken; constructor(init?: Partial) { Object.assign(this, init); diff --git a/src/modules/pair/pair.module.ts b/src/modules/pair/pair.module.ts index 6ccf9d5dd..db21a0c68 100644 --- a/src/modules/pair/pair.module.ts +++ b/src/modules/pair/pair.module.ts @@ -24,6 +24,7 @@ import { ElasticService } from 'src/helpers/elastic.service'; import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; import { PairFilteringService } from './services/pair.filtering.service'; import { StakingModule } from '../staking/staking.module'; +import { EnergyModule } from '../energy/energy.module'; @Module({ imports: [ CommonAppModule, @@ -39,6 +40,7 @@ import { StakingModule } from '../staking/staking.module'; FarmModuleV2, StakingProxyModule, StakingModule, + EnergyModule, ], providers: [ PairService, diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index bfa5aefff..dd066b434 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -33,18 +33,47 @@ import { GenericResolver } from 'src/services/generics/generic.resolver'; import { FarmComputeServiceV2 } from '../farm/v2/services/farm.v2.compute.service'; import { StakingComputeService } from '../staking/services/staking.compute.service'; import { StakingProxyService } from '../staking-proxy/services/staking.proxy.service'; +import { NftCollection } from '../tokens/models/nftCollection.model'; +import { EnergyService } from '../energy/services/energy.service'; @Resolver(() => PairRewardTokensModel) export class PairRewardTokensResolver extends GenericResolver { constructor( private readonly pairCompute: PairComputeService, + private readonly pairService: PairService, private readonly stakingProxyService: StakingProxyService, + private readonly energyService: EnergyService, ) { super(); } @ResolveField() - async dualFardRewardToken( + async poolRewards( + @Parent() parent: PairRewardTokensModel, + ): Promise { + return await Promise.all([ + this.pairService.getFirstToken(parent.address), + this.pairService.getSecondToken(parent.address), + ]); + } + + @ResolveField() + async farmReward( + @Parent() parent: PairRewardTokensModel, + ): Promise { + const farmAddress = await this.pairCompute.getPairFarmAddress( + parent.address, + ); + + if (!farmAddress) { + return undefined; + } + + return await this.energyService.getLockedToken(); + } + + @ResolveField() + async dualFarmReward( @Parent() parent: PairRewardTokensModel, ): Promise { const stakingProxyAddress = From 270bca025e8d12f4e7c8fa76defd386c9ddfbc36 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 9 Jul 2024 20:04:36 +0300 Subject: [PATCH 191/313] SERVICES-2492: add pairs filtering by LP token identifiers --- .../pair/services/pair.filtering.service.ts | 19 +++++++++++++++++++ .../pair/services/pair.metadata.builder.ts | 8 ++++++++ src/modules/router/models/filter.args.ts | 2 ++ 3 files changed, 29 insertions(+) diff --git a/src/modules/pair/services/pair.filtering.service.ts b/src/modules/pair/services/pair.filtering.service.ts index 5e9d862cf..03cc220c4 100644 --- a/src/modules/pair/services/pair.filtering.service.ts +++ b/src/modules/pair/services/pair.filtering.service.ts @@ -128,6 +128,25 @@ export class PairFilteringService { return filteredPairs; } + async pairsByLpTokenIds( + pairFilter: PairsFilter, + pairsMetadata: PairMetadata[], + ): Promise { + if (!pairFilter.lpTokenIds || pairFilter.lpTokenIds.length === 0) { + return pairsMetadata; + } + + const lpTokensIDs = await Promise.all( + pairsMetadata.map((pairMetadata) => + this.pairAbi.lpTokenID(pairMetadata.address), + ), + ); + + return pairsMetadata.filter((_, index) => + pairFilter.lpTokenIds.includes(lpTokensIDs[index]), + ); + } + async pairsByState( pairFilter: PairFilterArgs | PairsFilter, pairsMetadata: PairMetadata[], diff --git a/src/modules/pair/services/pair.metadata.builder.ts b/src/modules/pair/services/pair.metadata.builder.ts index 48175a05c..bcdbc718a 100644 --- a/src/modules/pair/services/pair.metadata.builder.ts +++ b/src/modules/pair/services/pair.metadata.builder.ts @@ -67,6 +67,14 @@ export class PairsMetadataBuilder { return this; } + async filterByLpTokens(): Promise { + this.pairsMetadata = await this.filteringService.pairsByLpTokenIds( + this.filters, + this.pairsMetadata, + ); + return this; + } + async filterByState(): Promise { this.pairsMetadata = await this.filteringService.pairsByState( this.filters, diff --git a/src/modules/router/models/filter.args.ts b/src/modules/router/models/filter.args.ts index 835e07821..bc5e5e4b5 100644 --- a/src/modules/router/models/filter.args.ts +++ b/src/modules/router/models/filter.args.ts @@ -60,6 +60,8 @@ export class PairsFilter { minDeployedAt: number; @Field({ nullable: true }) searchToken: string; + @Field(() => [String], { nullable: true }) + lpTokenIds: string[]; } @InputType() From a4669d56a4ec06b990d6a6d5577d5093b56fb743 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 10 Jul 2024 13:35:43 +0300 Subject: [PATCH 192/313] SERVICES-2492: add pairs filtering by farm tokens --- .../pair/services/pair.filtering.service.ts | 33 +++++++++++++++++++ .../pair/services/pair.metadata.builder.ts | 8 +++++ src/modules/router/models/filter.args.ts | 2 ++ 3 files changed, 43 insertions(+) diff --git a/src/modules/pair/services/pair.filtering.service.ts b/src/modules/pair/services/pair.filtering.service.ts index 03cc220c4..7de5e0d7a 100644 --- a/src/modules/pair/services/pair.filtering.service.ts +++ b/src/modules/pair/services/pair.filtering.service.ts @@ -8,6 +8,9 @@ import { } from 'src/modules/router/models/filter.args'; import BigNumber from 'bignumber.js'; import { PairService } from './pair.service'; +import { farmsAddresses } from 'src/utils/farm.utils'; +import { FarmVersion } from 'src/modules/farm/models/farm.model'; +import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; @Injectable() export class PairFilteringService { @@ -15,6 +18,7 @@ export class PairFilteringService { private readonly pairAbi: PairAbiService, private readonly pairCompute: PairComputeService, private readonly pairService: PairService, + private readonly farmAbi: FarmAbiServiceV2, ) {} async pairsByIssuedLpToken( @@ -147,6 +151,35 @@ export class PairFilteringService { ); } + async pairsByFarmTokens( + pairFilter: PairsFilter, + pairsMetadata: PairMetadata[], + ): Promise { + if (!pairFilter.farmTokens || pairFilter.farmTokens.length === 0) { + return pairsMetadata; + } + + const farmAddresses = await Promise.all( + pairsMetadata.map((pairMetadata) => + this.pairCompute.getPairFarmAddress(pairMetadata.address), + ), + ); + + const farmTokens = await Promise.all( + farmAddresses.map((farmAddress) => { + if (!farmAddress) { + return undefined; + } + + return this.farmAbi.farmTokenID(farmAddress); + }), + ); + + return pairsMetadata.filter((_, index) => + pairFilter.farmTokens.includes(farmTokens[index]), + ); + } + async pairsByState( pairFilter: PairFilterArgs | PairsFilter, pairsMetadata: PairMetadata[], diff --git a/src/modules/pair/services/pair.metadata.builder.ts b/src/modules/pair/services/pair.metadata.builder.ts index bcdbc718a..853093538 100644 --- a/src/modules/pair/services/pair.metadata.builder.ts +++ b/src/modules/pair/services/pair.metadata.builder.ts @@ -75,6 +75,14 @@ export class PairsMetadataBuilder { return this; } + async filterByFarmTokens(): Promise { + this.pairsMetadata = await this.filteringService.pairsByFarmTokens( + this.filters, + this.pairsMetadata, + ); + return this; + } + async filterByState(): Promise { this.pairsMetadata = await this.filteringService.pairsByState( this.filters, diff --git a/src/modules/router/models/filter.args.ts b/src/modules/router/models/filter.args.ts index bc5e5e4b5..10c98bbfd 100644 --- a/src/modules/router/models/filter.args.ts +++ b/src/modules/router/models/filter.args.ts @@ -62,6 +62,8 @@ export class PairsFilter { searchToken: string; @Field(() => [String], { nullable: true }) lpTokenIds: string[]; + @Field(() => [String], { nullable: true }) + farmTokens: string[]; } @InputType() From cf5d63ef348f3b1e5407933c958fbd75fb7f602c Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 10 Jul 2024 13:54:07 +0300 Subject: [PATCH 193/313] SERVICES-2492: remove farmAbiV2 dependency from pair filtering service --- .../pair/services/pair.compute.service.ts | 10 ++++++++ .../pair/services/pair.filtering.service.ts | 24 +++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index ba1b9f736..31685b05e 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -747,6 +747,16 @@ export class PairComputeService implements IPairComputeService { return addresses[farmAddressIndex]; } + async getPairFarmToken(pairAddress: string): Promise { + const farmAddress = await this.getPairFarmAddress(pairAddress); + + if (!farmAddress) { + return undefined; + } + + return this.farmAbi.farmTokenID(farmAddress); + } + async getPairStakingFarmAddress(pairAddress: string): Promise { const stakingProxyAddress = await this.getPairStakingProxyAddress( pairAddress, diff --git a/src/modules/pair/services/pair.filtering.service.ts b/src/modules/pair/services/pair.filtering.service.ts index 7de5e0d7a..32bcb2e1b 100644 --- a/src/modules/pair/services/pair.filtering.service.ts +++ b/src/modules/pair/services/pair.filtering.service.ts @@ -8,9 +8,6 @@ import { } from 'src/modules/router/models/filter.args'; import BigNumber from 'bignumber.js'; import { PairService } from './pair.service'; -import { farmsAddresses } from 'src/utils/farm.utils'; -import { FarmVersion } from 'src/modules/farm/models/farm.model'; -import { FarmAbiServiceV2 } from 'src/modules/farm/v2/services/farm.v2.abi.service'; @Injectable() export class PairFilteringService { @@ -18,7 +15,6 @@ export class PairFilteringService { private readonly pairAbi: PairAbiService, private readonly pairCompute: PairComputeService, private readonly pairService: PairService, - private readonly farmAbi: FarmAbiServiceV2, ) {} async pairsByIssuedLpToken( @@ -159,24 +155,16 @@ export class PairFilteringService { return pairsMetadata; } - const farmAddresses = await Promise.all( + const farmTokens = await Promise.all( pairsMetadata.map((pairMetadata) => - this.pairCompute.getPairFarmAddress(pairMetadata.address), + this.pairCompute.getPairFarmToken(pairMetadata.address), ), ); - const farmTokens = await Promise.all( - farmAddresses.map((farmAddress) => { - if (!farmAddress) { - return undefined; - } - - return this.farmAbi.farmTokenID(farmAddress); - }), - ); - - return pairsMetadata.filter((_, index) => - pairFilter.farmTokens.includes(farmTokens[index]), + return pairsMetadata.filter( + (_, index) => + farmTokens[index] && + pairFilter.farmTokens.includes(farmTokens[index]), ); } From ecd3a89d7b20d77a025c54f3c9d718c56320f923 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 12 Jul 2024 14:21:48 +0300 Subject: [PATCH 194/313] MEX-480: update gas limits for create energy position Signed-off-by: Claudiu Lataretu --- src/config/default.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 77b97dbdc..59494a93c 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -450,7 +450,7 @@ "claimRewardsFarmLockedToken": 35000000 }, "simpleLockEnergy": { - "lockTokens": 10000000, + "lockTokens": 13000000, "unlockTokens": { "unlockEarly": 25000000, "reduceLockPeriod": 20000000, @@ -512,7 +512,7 @@ "dualFarmPosition": 67000000, "exitFarm": 50000000 }, - "energyPosition": 10000000 + "energyPosition": 13000000 }, "composableTasks": { "default": 1000000 From 0ffee43a64eca3f55b9ce2ea13b3d861f99137ad Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 12 Jul 2024 14:33:42 +0300 Subject: [PATCH 195/313] MEX-480: fix position creator unit tests Signed-off-by: Claudiu Lataretu --- .../specs/position.creator.transaction.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index c7e96136f..65d925c54 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -2268,7 +2268,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'ESDTTransfer@WEGLD-123456@1000000000000000000@createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: 40000000, + gasLimit: 43000000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, @@ -2320,7 +2320,7 @@ describe('PositionCreatorTransaction', () => { data: encodeTransactionData( 'createEnergyPosition@1440@986046911229504184328@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@986046911229504184328', ), - gasLimit: 40000000, + gasLimit: 43000000, gasPrice: 1000000000, guardian: undefined, guardianSignature: undefined, From f4bd51a54461995ebd8fb874dca676e76fae63e4 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 17 Jul 2024 08:50:33 +0300 Subject: [PATCH 196/313] SERVICES-2495: fix negative energy false positive --- .../user/services/userEnergy/user.energy.compute.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/user/services/userEnergy/user.energy.compute.service.ts b/src/modules/user/services/userEnergy/user.energy.compute.service.ts index 167054ec3..c238b7462 100644 --- a/src/modules/user/services/userEnergy/user.energy.compute.service.ts +++ b/src/modules/user/services/userEnergy/user.energy.compute.service.ts @@ -492,6 +492,9 @@ export class UserEnergyComputeService { } } } + if (lockedTokensAttributes.length === 0) { + return false; + } return true; } @@ -504,6 +507,9 @@ export class UserEnergyComputeService { return false; } } + if (lockedTokensAttributes.length === 0) { + return false; + } return true; } From 8fd57417d2a481af3da9d2ee7beb5a72d85c617d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 18 Jul 2024 23:08:57 +0300 Subject: [PATCH 197/313] MEX-480: update gas limit for stake farm tokens Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 59494a93c..4f42a4446 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -414,7 +414,7 @@ "stakeProxy": { "stakeFarmTokens": { "default": 27000000, - "withTokenMerge": 36000000 + "withTokenMerge": 60000000 }, "claimDualYield": 55000000, "unstakeFarmTokens": 75000000 From 0215b7b69960d6c10defca8911c66d679b690647 Mon Sep 17 00:00:00 2001 From: hschiau Date: Sat, 20 Jul 2024 18:09:36 +0300 Subject: [PATCH 198/313] SERVICES-2495: fix dual farm base apr computation --- src/modules/pair/pair.resolver.ts | 2 +- .../services/staking.compute.service.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index dd066b434..affc95674 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -146,7 +146,7 @@ export class PairCompoundedAPRResolver extends GenericResolver { return '0'; } - return await this.stakingCompute.stakeFarmAPR(stakingAddress); + return await this.stakingCompute.stakeFarmBaseAPR(stakingAddress); } @ResolveField(() => String) diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 25536d7b6..f37e2f377 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -231,6 +231,29 @@ export class StakingComputeService { .toFixed(); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async stakeFarmBaseAPR(stakeAddress: string): Promise { + return await this.computeStakeFarmBaseAPR(stakeAddress); + } + + async computeStakeFarmBaseAPR(stakeAddress: string): Promise { + const [apr, rawBoostedApr] = await Promise.all([ + this.stakeFarmAPR(stakeAddress), + this.boostedApr(stakeAddress), + ]); + + const baseApr = new BigNumber(apr).minus(rawBoostedApr); + + return baseApr.toFixed(); + } + async computeRewardsRemainingDays(stakeAddress: string): Promise { const [perBlockRewardAmount, accumulatedRewards, rewardsCapacity] = await Promise.all([ From a40d9af392f106ed29d9ec66fbbb00f6a5e2e28e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 25 Jul 2024 17:51:31 +0300 Subject: [PATCH 199/313] MEX-470: added shadowfork4 config Signed-off-by: Claudiu Lataretu --- src/config/shadowfork4.json | 125 ++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/config/shadowfork4.json diff --git a/src/config/shadowfork4.json b/src/config/shadowfork4.json new file mode 100644 index 000000000..4ae99f382 --- /dev/null +++ b/src/config/shadowfork4.json @@ -0,0 +1,125 @@ +{ + "multiversx": { + "chainID": "1" + }, + "scAddress": { + "routerAddress": "erd1qqqqqqqqqqqqqpgqq66xk9gfr4esuhem3jru86wg5hvp33a62jps2fy57p", + "MEX-455c57": "erd1qqqqqqqqqqqqqpgqa0fsfshnff4n76jhcye6k7uvd7qacsq42jpsp6shh2", + "WEGLD_USDC": "erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq", + "distributionAddress": "erd1qqqqqqqqqqqqqpgqyg62a3tswuzun2wzp8a83sjkc709wwjt2jpssphddm", + "proxyDexAddress": { + "v1": "erd1qqqqqqqqqqqqqpgqrc4pg2xarca9z34njcxeur622qmfjp8w2jps89fxnl", + "v2": "erd1qqqqqqqqqqqqqpgqt6ltx52ukss9d2qag2k67at28a36xc9lkp2sr06394" + }, + "lockedAssetAddress": "erd1qqqqqqqqqqqqqpgqjpt0qqgsrdhp2xqygpjtfrpwf76f9nvg2jpsg4q7th", + "metabondingStakingAddress": "erd1qqqqqqqqqqqqqpgqt7tyyswqvplpcqnhwe20xqrj7q7ap27d2jps7zczse", + "simpleLockAddress": [ + "erd1qqqqqqqqqqqqqpgqs0jjyjmx0cvek4p8yj923q5yreshtpa62jpsz6vt84", + "erd1qqqqqqqqqqqqqpgqawujux7w60sjhm8xdx3n0ed8v9h7kpqu2jpsecw6ek", + "erd1qqqqqqqqqqqqqpgq6nu2t8lzakmcfmu4pu5trjdarca587hn2jpsyjapr5" + ], + "wrappingAddress": { + "shardID-0": "erd1qqqqqqqqqqqqqpgqvc7gdl0p4s97guh498wgz75k8sav6sjfjlwqh679jy", + "shardID-1": "erd1qqqqqqqqqqqqqpgqhe8t5jewej70zupmh44jurgn29psua5l2jps3ntjj3", + "shardID-2": "erd1qqqqqqqqqqqqqpgqmuk0q2saj0mgutxm4teywre6dl8wqf58xamqdrukln" + }, + "priceDiscovery": [ + "erd1qqqqqqqqqqqqqpgq38cmvwwkujae3c0xmc9p9554jjctkv4w2jps83r492", + "erd1qqqqqqqqqqqqqpgq666ta9cwtmjn6dtzv9fhfcmahkjmzhg62jps49k7k8", + "erd1qqqqqqqqqqqqqpgqq93kw6jngqjugfyd7pf555lwurfg2z5e2jpsnhhywk" + ], + "simpleLockEnergy": "erd1qqqqqqqqqqqqqpgq0tajepcazernwt74820t8ef7t28vjfgukp2sw239f3", + "feesCollector": "erd1qqqqqqqqqqqqqpgqjsnxqprks7qxfwkcg2m2v9hxkrchgm9akp2segrswt", + "energyUpdate": "erd1qqqqqqqqqqqqqpgqg48z8qfm028ze6y3d6tamdfaw6vn774tkp2shg5dt5", + "tokenUnstake": "erd1qqqqqqqqqqqqqpgqxv0y4p6vvszrknaztatycac77yvsxqrrkp2sghd86c", + "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq97fctvqlskzu0str05muew2qyjttp8kfkp2sl9k0gx", + "escrow": "erd1qqqqqqqqqqqqqpgqeh4yv09rmyg4xgn5ma03mvm4v5gndu8w2jpsglz3cn", + "positionCreator": "erd1qqqqqqqqqqqqqpgqvedfh0qhaa8nqdzv96v3kygyvnd4ewqd2jpsku82sz", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqzcy4fwq6dhjrfcsgzwwjg3m035rrxhkp2jpshzm3fn", + "composableTasks": "erd1qqqqqqqqqqqqqpgqhmjfrn2azv4c4tmy3dns78naey9qjnte2jpsywt8ex" + }, + "governance": { + "oldEnergy": { + "cvadratic": [ + "erd1qqqqqqqqqqqqqpgqdt9aady5jp7av97m7rqxh6e5ywyqsplz2jps5mw02n" + ] + }, + "energy": { + "cvadratic": [] + }, + "tokenSnapshot": { + "linear": [ + "erd1qqqqqqqqqqqqqpgq7cywmem5g66ad49rce0ctu6yqmscmp9v7j6q7cded4" + ], + "cvadratic": [] + } + }, + "farms": { + "v1.2": [ + "erd1qqqqqqqqqqqqqpgqye633y7k0zd7nedfnp3m48h24qygm5jl2jpslxallh", + "erd1qqqqqqqqqqqqqpgqsw9pssy8rchjeyfh8jfafvl3ynum0p9k2jps6lwewp", + "erd1qqqqqqqqqqqqqpgqv4ks4nzn2cw96mm06lt7s2l3xfrsznmp2jpsszdry5" + ], + "v1.3": { + "unlockedRewards": [ + "erd1qqqqqqqqqqqqqpgqnqvjnn4haygsw2hls2k9zjjadnjf9w7g2jpsmc60a4", + "erd1qqqqqqqqqqqqqpgqutddd7dva0x4xmehyljp7wh7ecynag0u2jpskxx6xt", + "erd1qqqqqqqqqqqqqpgqe9v45fnpkv053fj0tk7wvnkred9pms892jps9lkqrn", + "erd1qqqqqqqqqqqqqpgqs2puacesqywt84jawqenmmree20uj80d2jpsefuka9", + "erd1qqqqqqqqqqqqqpgq6xp2uvdk22frgqkwm9pwa564gqg2gxfm2jpszggzgw", + "erd1qqqqqqqqqqqqqpgqmaj6qc4c6f9ldkuwllqh7pyd8e9yu3m82jpscc6f9s", + "erd1qqqqqqqqqqqqqpgqc3p5xkj656h2ts649h4f447se4clns662jpsccq7rx", + "erd1qqqqqqqqqqqqqpgqzy0qwv0jf0yk8tysll0y6ea0fhpfqlp32jpsder9ne" + ], + "lockedRewards": [ + "erd1qqqqqqqqqqqqqpgqyawg3d9r4l27zue7e9sz7djf7p9aj3sz2jpsm070jf", + "erd1qqqqqqqqqqqqqpgqwtzqckt793q8ggufxxlsv3za336674qq2jpszzgqra", + "erd1qqqqqqqqqqqqqpgq7qhsw8kffad85jtt79t9ym0a4ycvan9a2jps0zkpen", + "erd1qqqqqqqqqqqqqpgqs2mmvzpu6wz83z3vthajl4ncpwz67ctu2jpsrcl0ct", + "erd1qqqqqqqqqqqqqpgqdt892e0aflgm0xwryhhegsjhw0zru60m2jps959w5z", + "erd1qqqqqqqqqqqqqpgq3f8jfeg34ujzy0muhe9dvn5yngwgud022jpsscjxgl", + "erd1qqqqqqqqqqqqqpgqcejzfjfmmgvq7yjch2tqnhhsngr7hqyw2jpshce5u2", + "erd1qqqqqqqqqqqqqpgqlu6vrtkgfjh68nycf5sdw4nyzudncns42jpsr7szch" + ], + "customRewards": [ + "erd1qqqqqqqqqqqqqpgq5e2m9df5yxxkmr86rusejc979arzayjk2jpsz2q43s" + ] + }, + "v2": { + "lockedRewards": [ + "erd1qqqqqqqqqqqqqpgqapxdp9gjxtg60mjwhle3n6h88zch9e7kkp2s8aqhkg", + "erd1qqqqqqqqqqqqqpgqv0pz5z3fkz54nml6pkzzdruuf020gqzykp2sya7kkv", + "erd1qqqqqqqqqqqqqpgqenvn0s3ldc94q2mlkaqx4arj3zfnvnmakp2sxca2h9", + "erd1qqqqqqqqqqqqqpgqrdq6zvepdxg36rey8pmluwur43q4hcx3kp2su4yltq", + "erd1qqqqqqqqqqqqqpgq4acurmluezvmhna8tztgcrnwh0l70a2wkp2sfh6jkp", + "erd1qqqqqqqqqqqqqpgquhqfknr9dg5t0xma0zcc55dm8gwv5rpkkp2sq8f40p", + "erd1qqqqqqqqqqqqqpgq6v5ta4memvrhjs4x3gqn90c3pujc77takp2sqhxm9j", + "erd1qqqqqqqqqqqqqpgqqqckvlhd3n7ntt5w3vln4xh3tsfpj0hr2jpsxlzgcj", + "erd1qqqqqqqqqqqqqpgqcfkmz2j0e8pj0nzuk9j6mzcav0wpthq52jpsg546ch", + "erd1qqqqqqqqqqqqqpgqr9gx79xls33tnzt0a94uvnkf452fz26q2jpsau4ulw", + "erd1qqqqqqqqqqqqqpgqwcdc3ve8ww6psam9hhu5nqqjlt22nagw2jpspt9rev", + "erd1qqqqqqqqqqqqqpgqmvzrwmwkrrzug50e64kk7xerufncp47d2jpsf9m8rx", + "erd1qqqqqqqqqqqqqpgqc6fyckfxvehlwr2zvfl34dycwgtm988g2jpsrnyazn", + "erd1qqqqqqqqqqqqqpgqm78a0zmuqlm7z5qw9nseg6ecdszafljp2jpsxkm4rp", + "erd1qqqqqqqqqqqqqpgqk5xplswklltypmazcxx5mhn3jdvaxuf92jpsudss7r", + "erd1qqqqqqqqqqqqqpgqjgtun0fhl2em5ayukvahm3myltaxc5tz2jpssz8axf", + "erd1qqqqqqqqqqqqqpgq4se997u3eyhjmpwaa57lvdgvmyn23yqpx9rsdzja7j", + "erd1qqqqqqqqqqqqqpgqhh8lke0kfch0g20jrmskczwq86d23qhex9rs5hm4uk", + "erd1qqqqqqqqqqqqqpgqgjqu7c0mq8a4jd4d43st44gvwhduvqaxkp2s7vsq64" + ] + } + }, + "tokenProviderUSD": "WEGLD-bd4d79", + "tokensSupply": ["MEX-455c57"], + "cachedTokensPrice": ["MEX-455c57", "WEGLD-bd4d79"], + "constants": { + "MEX_TOKEN_ID": "MEX-455c57", + "USDC_TOKEN_ID": "USDC-c76f1f", + "MAINTENANCE": false, + "roundedSwapEnable": { + "USDC-c76f1f": "5000000", + "WEGLD-bd4d79": "100000000000000000", + "MEX-455c57": "1000000000000000000000000" + } + } +} From a8a933b99e16761ae92241291cf1adb1d9c68384 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 25 Jul 2024 18:08:44 +0300 Subject: [PATCH 200/313] MEX-480: Update gas limit for position creator Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 4f42a4446..0121df35b 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -507,7 +507,7 @@ "stakingPosition": 55000000 }, "dualTokens": { - "farmPosition": 30000000, + "farmPosition": 35000000, "farmPositionProxy": 50000000, "dualFarmPosition": 67000000, "exitFarm": 50000000 From 6145aa839217f042259070b1cccec2e130f19158 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 29 Jul 2024 14:03:57 +0300 Subject: [PATCH 201/313] MEX-470: update shadowfork4 contracts addresses Signed-off-by: Claudiu Lataretu --- src/config/shadowfork4.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/shadowfork4.json b/src/config/shadowfork4.json index 4ae99f382..491ff5f69 100644 --- a/src/config/shadowfork4.json +++ b/src/config/shadowfork4.json @@ -34,9 +34,9 @@ "tokenUnstake": "erd1qqqqqqqqqqqqqpgqxv0y4p6vvszrknaztatycac77yvsxqrrkp2sghd86c", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq97fctvqlskzu0str05muew2qyjttp8kfkp2sl9k0gx", "escrow": "erd1qqqqqqqqqqqqqpgqeh4yv09rmyg4xgn5ma03mvm4v5gndu8w2jpsglz3cn", - "positionCreator": "erd1qqqqqqqqqqqqqpgqvedfh0qhaa8nqdzv96v3kygyvnd4ewqd2jpsku82sz", - "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqzcy4fwq6dhjrfcsgzwwjg3m035rrxhkp2jpshzm3fn", - "composableTasks": "erd1qqqqqqqqqqqqqpgqhmjfrn2azv4c4tmy3dns78naey9qjnte2jpsywt8ex" + "positionCreator": "erd1qqqqqqqqqqqqqpgqp6s52q4m6nkf5usqacjuuqymnayv6puc2jpsp35nhg", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqucxekem48mrlzcenugl7z6k38l2ead702jpsrlfg4r", + "composableTasks": "erd1qqqqqqqqqqqqqpgqa6a50v9m5nc50cxaz9qxrd8mz9lxnfjt2jpsjj0cky" }, "governance": { "oldEnergy": { From 8e3a652d4b5b03017ec1b6c2f5e4883c6c56bf80 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 29 Jul 2024 15:55:08 +0300 Subject: [PATCH 202/313] MEX-492: fix return value on user total farm position Signed-off-by: Claudiu Lataretu --- src/abis/farm-staking.abi.json | 108 +++++++++--------- .../v2/farm-with-locked-rewards.abi.json | 105 +++++++++-------- .../farm/v2/services/farm.v2.abi.service.ts | 2 +- .../staking/services/staking.abi.service.ts | 2 +- 4 files changed, 110 insertions(+), 107 deletions(-) diff --git a/src/abis/farm-staking.abi.json b/src/abis/farm-staking.abi.json index 90ede4779..223ad5e17 100644 --- a/src/abis/farm-staking.abi.json +++ b/src/abis/farm-staking.abi.json @@ -1,20 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.76.0-nightly", - "commitHash": "d86d65bbc19b928387f68427fcc3a0da498d8a19", - "commitDate": "2023-12-10", + "version": "1.80.0-nightly", + "commitHash": "84b40fc908c3adc7e0e470b3fbaa264df0e122b8", + "commitDate": "2024-05-27", "channel": "Nightly", - "short": "rustc 1.76.0-nightly (d86d65bbc 2023-12-10)" + "short": "rustc 1.80.0-nightly (84b40fc90 2024-05-27)" }, "contractCrate": { "name": "farm-staking", "version": "0.0.0", - "gitVersion": "v1.6.0-1579-ge4b95afa" + "gitVersion": "v1.6.0-1831-g81b6253f" }, "framework": { "name": "multiversx-sc", - "version": "0.45.2" + "version": "0.50.5" } }, "name": "FarmStaking", @@ -48,13 +48,11 @@ ], "outputs": [] }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, "endpoints": [ - { - "name": "upgrade", - "mutability": "mutable", - "inputs": [], - "outputs": [] - }, { "name": "mergeFarmTokens", "mutability": "mutable", @@ -71,6 +69,17 @@ } ] }, + { + "name": "setBoostedYieldsRewardsPercentage", + "mutability": "mutable", + "inputs": [ + { + "name": "percentage", + "type": "u64" + } + ], + "outputs": [] + }, { "name": "calculateRewardsForGivenPosition", "mutability": "readonly", @@ -99,6 +108,20 @@ "inputs": [], "outputs": [] }, + { + "name": "withdrawRewards", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "withdraw_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, { "name": "endProduceRewards", "mutability": "mutable", @@ -205,7 +228,7 @@ ] }, { - "name": "allowExternalClaimBoostedRewards", + "name": "setAllowExternalClaimBoostedRewards", "mutability": "mutable", "inputs": [ { @@ -215,21 +238,6 @@ ], "outputs": [] }, - { - "name": "getAllowExternalClaimRewards", - "mutability": "readonly", - "inputs": [ - { - "name": "user", - "type": "Address" - } - ], - "outputs": [ - { - "type": "bool" - } - ] - }, { "name": "getFarmingTokenId", "mutability": "readonly", @@ -291,7 +299,22 @@ ], "outputs": [ { - "type": "UserTotalFarmPosition" + "type": "BigUint" + } + ] + }, + { + "name": "getAllowExternalClaim", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "bool" } ] }, @@ -659,9 +682,6 @@ { "name": "claimBoostedRewards", "mutability": "mutable", - "payableInTokens": [ - "*" - ], "inputs": [ { "name": "opt_user", @@ -675,17 +695,6 @@ } ] }, - { - "name": "setBoostedYieldsRewardsPercentage", - "mutability": "mutable", - "inputs": [ - { - "name": "percentage", - "type": "u64" - } - ], - "outputs": [] - }, { "name": "collectUndistributedBoostedRewards", "mutability": "mutable", @@ -1414,19 +1423,6 @@ "discriminant": 2 } ] - }, - "UserTotalFarmPosition": { - "type": "struct", - "fields": [ - { - "name": "total_farm_position", - "type": "BigUint" - }, - { - "name": "allow_external_claim_boosted_rewards", - "type": "bool" - } - ] } } } diff --git a/src/abis/farms/v2/farm-with-locked-rewards.abi.json b/src/abis/farms/v2/farm-with-locked-rewards.abi.json index 6ec271006..a60924419 100644 --- a/src/abis/farms/v2/farm-with-locked-rewards.abi.json +++ b/src/abis/farms/v2/farm-with-locked-rewards.abi.json @@ -1,20 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.73.0-nightly", - "commitHash": "4c8bb79d9f565115637cc6da739f8389e79f3a29", - "commitDate": "2023-07-15", + "version": "1.80.0-nightly", + "commitHash": "84b40fc908c3adc7e0e470b3fbaa264df0e122b8", + "commitDate": "2024-05-27", "channel": "Nightly", - "short": "rustc 1.73.0-nightly (4c8bb79d9 2023-07-15)" + "short": "rustc 1.80.0-nightly (84b40fc90 2024-05-27)" }, "contractCrate": { "name": "farm-with-locked-rewards", "version": "0.0.0", - "gitVersion": "v1.6.0-1513-g4dc02033" + "gitVersion": "v1.6.0-1831-g81b6253f" }, "framework": { "name": "multiversx-sc", - "version": "0.43.3" + "version": "0.50.5" } }, "name": "Farm", @@ -48,6 +48,10 @@ ], "outputs": [] }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, "endpoints": [ { "name": "enterFarm", @@ -176,6 +180,17 @@ ], "outputs": [] }, + { + "name": "setBoostedYieldsRewardsPercentage", + "mutability": "mutable", + "inputs": [ + { + "name": "percentage", + "type": "u64" + } + ], + "outputs": [] + }, { "name": "calculateRewardsForGivenPosition", "mutability": "readonly", @@ -220,7 +235,7 @@ ] }, { - "name": "allowExternalClaimBoostedRewards", + "name": "setAllowExternalClaimBoostedRewards", "mutability": "mutable", "inputs": [ { @@ -291,7 +306,22 @@ ], "outputs": [ { - "type": "UserTotalFarmPosition" + "type": "BigUint" + } + ] + }, + { + "name": "getAllowExternalClaim", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "bool" } ] }, @@ -600,17 +630,6 @@ } ] }, - { - "name": "setBoostedYieldsRewardsPercentage", - "mutability": "mutable", - "inputs": [ - { - "name": "percentage", - "type": "u64" - } - ], - "outputs": [] - }, { "name": "collectUndistributedBoostedRewards", "mutability": "mutable", @@ -1083,25 +1102,9 @@ ] } ], + "esdtAttributes": [], "hasCallback": true, "types": { - "Energy": { - "type": "struct", - "fields": [ - { - "name": "amount", - "type": "BigInt" - }, - { - "name": "last_update_epoch", - "type": "u64" - }, - { - "name": "total_locked_tokens", - "type": "BigUint" - } - ] - }, "BoostedYieldsFactors": { "type": "struct", "fields": [ @@ -1214,6 +1217,23 @@ } ] }, + "Energy": { + "type": "struct", + "fields": [ + { + "name": "amount", + "type": "BigInt" + }, + { + "name": "last_update_epoch", + "type": "u64" + }, + { + "name": "total_locked_tokens", + "type": "BigUint" + } + ] + }, "EnterFarmEvent": { "type": "struct", "fields": [ @@ -1342,19 +1362,6 @@ "discriminant": 2 } ] - }, - "UserTotalFarmPosition": { - "type": "struct", - "fields": [ - { - "name": "total_farm_position", - "type": "BigUint" - }, - { - "name": "allow_external_claim_boosted_rewards", - "type": "bool" - } - ] } } } diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index af70042c5..d7b9927b0 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -412,7 +412,7 @@ export class FarmAbiServiceV2 return '0'; } - return response.firstValue.valueOf().total_farm_position.toFixed(); + return response.firstValue.valueOf().toFixed(); } @ErrorLoggerAsync({ diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 820d2a44b..711f037be 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -667,7 +667,7 @@ export class StakingAbiService return '0'; } - return response.firstValue.valueOf().total_farm_position.toFixed(); + return response.firstValue.valueOf().toFixed(); } @ErrorLoggerAsync({ From ab170c659cdad3578fc80d734f74b715ef683b47 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 29 Jul 2024 16:45:12 +0300 Subject: [PATCH 203/313] MEX-492: fix undefined values on pair and ESDT models Signed-off-by: Claudiu Lataretu --- src/modules/pair/models/pair.model.ts | 2 +- src/modules/tokens/models/esdtToken.interface.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 978dc3e07..246752ea5 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -201,7 +201,7 @@ export class PairModel { @Field() tradesCount: number; - @Field() + @Field({ nullable: true }) deployedAt: number; @Field(() => PairCompoundedAPRModel, { nullable: true }) diff --git a/src/modules/tokens/models/esdtToken.interface.ts b/src/modules/tokens/models/esdtToken.interface.ts index 798728a84..27c035600 100644 --- a/src/modules/tokens/models/esdtToken.interface.ts +++ b/src/modules/tokens/models/esdtToken.interface.ts @@ -17,7 +17,7 @@ export abstract class IEsdtToken { @Field({ nullable: true }) circulatingSupply?: string; @Field(() => IAssets, { nullable: true }) assets?: IAssets; @Field(() => Int) transactions: number; - @Field(() => Int) accounts: number; + @Field(() => Int, { defaultValue: 0 }) accounts: number; @Field() isPaused: boolean; @Field() canUpgrade: boolean; @Field() canMint: boolean; From aa2ab6eb5977bccb0f897be0ce7948eea070dcbe Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 09:35:01 +0300 Subject: [PATCH 204/313] SERVICES-2513: caching for null or undefined values - update GetOrSet decorator - update generic setter --- src/helpers/decorators/caching.decorator.ts | 37 +++++++++++++++++-- src/services/caching/cache.ttl.info.ts | 5 +++ .../generics/generic.setter.service.ts | 9 +++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/helpers/decorators/caching.decorator.ts b/src/helpers/decorators/caching.decorator.ts index fcc0ed5a0..34b11803b 100644 --- a/src/helpers/decorators/caching.decorator.ts +++ b/src/helpers/decorators/caching.decorator.ts @@ -2,6 +2,7 @@ import { Inject } from '@nestjs/common'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { generateCacheKeyFromParams } from 'src/utils/generate-cache-key'; import { ContextTracker } from '@multiversx/sdk-nestjs-common'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; export interface ICachingOptions { baseKey: string; @@ -35,12 +36,40 @@ export function GetOrSetCache(cachingOptions: ICachingOptions) { const cachingService: CacheService = this.cachingService; - return await cachingService.getOrSet( + let cachedValue = await cachingService.get(cacheKey); + + if (cachedValue !== undefined) { + cachedValue = cachedValue === 'null' ? null : cachedValue; + cachedValue = + cachedValue === 'undefined' ? undefined : cachedValue; + + return cachedValue; + } + + const value = await originalMethod.apply(this, args); + + console.log('CACHE MISS', cacheKey); + + let { remoteTtl, localTtl } = cachingOptions; + + if (typeof value === 'undefined' || value === null) { + console.log('NULL', cacheKey, value); + + remoteTtl = CacheTtlInfo.NullValue.remoteTtl; + localTtl = CacheTtlInfo.NullValue.localTtl; + } + + cachedValue = typeof value === 'undefined' ? 'undefined' : value; + cachedValue = cachedValue === null ? 'null' : cachedValue; + + await cachingService.set( cacheKey, - () => originalMethod.apply(this, args), - cachingOptions.remoteTtl, - cachingOptions.localTtl, + cachedValue, + remoteTtl, + localTtl, ); + + return value; }; return descriptor; }; diff --git a/src/services/caching/cache.ttl.info.ts b/src/services/caching/cache.ttl.info.ts index 15c69276a..314793f9c 100644 --- a/src/services/caching/cache.ttl.info.ts +++ b/src/services/caching/cache.ttl.info.ts @@ -43,4 +43,9 @@ export class CacheTtlInfo { Constants.oneHour(), Constants.oneMinute() * 45, ); + + static NullValue: CacheTtlInfo = new CacheTtlInfo( + Constants.oneMinute() * 2, + Constants.oneMinute(), + ); } diff --git a/src/services/generics/generic.setter.service.ts b/src/services/generics/generic.setter.service.ts index d7653383c..672940e54 100644 --- a/src/services/generics/generic.setter.service.ts +++ b/src/services/generics/generic.setter.service.ts @@ -4,6 +4,7 @@ import { generateSetLogMessage } from 'src/utils/generate-log-message'; import { Logger } from 'winston'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { generateCacheKeyFromParams } from '../../utils/generate-cache-key'; +import { CacheTtlInfo } from '../caching/cache.ttl.info'; @Injectable() export class GenericSetterService { @@ -19,6 +20,14 @@ export class GenericSetterService { remoteTtl: number, localTtl?: number, ): Promise { + if (typeof value === 'undefined' || value === null) { + remoteTtl = CacheTtlInfo.NullValue.remoteTtl; + localTtl = CacheTtlInfo.NullValue.localTtl; + } + + value = typeof value === 'undefined' ? 'undefined' : value; + value = value === null ? 'null' : value; + await this.cachingService.set(cacheKey, value, remoteTtl, localTtl); return cacheKey; } From e01d39102b02416924d8d3dd4872e97b8c2d566a Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 09:41:11 +0300 Subject: [PATCH 205/313] SERVICES-2513: update pairs cache warmer --- .../pair/services/pair.setter.service.ts | 24 ++++++++++++ .../crons/pair.cache.warmer.service.ts | 37 +++++++++++++++---- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/modules/pair/services/pair.setter.service.ts b/src/modules/pair/services/pair.setter.service.ts index e6ce09af2..01114ef02 100644 --- a/src/modules/pair/services/pair.setter.service.ts +++ b/src/modules/pair/services/pair.setter.service.ts @@ -402,4 +402,28 @@ export class PairSetterService extends GenericSetterService { CacheTtlInfo.ContractState.localTtl, ); } + + async setInitialLiquidityAdder( + pairAddress: string, + value: string, + ): Promise { + return await this.setData( + this.getCacheKey('initialLiquidityAdder', pairAddress), + value, + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } + + async setTrustedSwapPairs( + pairAddress: string, + value: string[], + ): Promise { + return await this.setData( + this.getCacheKey('trustedSwapPairs', pairAddress), + value, + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } } diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index 71408be15..d88a2c602 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -49,15 +49,11 @@ export class PairCacheWarmerService { pairMetadata.address, pairMetadata.secondTokenID, ), + this.pairSetterService.setLpTokenID( + pairMetadata.address, + lpTokenID, + ), ]); - if (lpTokenID !== undefined) { - cachedKeys.push( - await this.pairSetterService.setLpTokenID( - pairMetadata.address, - lpTokenID, - ), - ); - } await this.deleteCacheKeys(cachedKeys); } @@ -148,6 +144,10 @@ export class PairCacheWarmerService { hasDualFarms, tradesCount, deployedAt, + whitelistedAddresses, + feeDestinations, + initialLiquidityAdder, + trustedSwapPairs, ] = await Promise.all([ this.pairComputeService.computeFeesAPR(pairAddress), this.pairAbi.getStateRaw(pairAddress), @@ -161,6 +161,10 @@ export class PairCacheWarmerService { this.pairComputeService.computeHasDualFarms(pairAddress), this.pairComputeService.computeTradesCount(pairAddress), this.pairComputeService.computeDeployedAt(pairAddress), + this.pairAbi.getWhitelistedAddressesRaw(pairAddress), + this.pairAbi.getFeeDestinationsRaw(pairAddress), + this.pairAbi.getInitialLiquidityAdderRaw(pairAddress), + this.pairAbi.getTrustedSwapPairsRaw(pairAddress), ]); const cachedKeys = await Promise.all([ @@ -195,12 +199,29 @@ export class PairCacheWarmerService { ), this.pairSetterService.setTradesCount(pairAddress, tradesCount), this.pairSetterService.setDeployedAt(pairAddress, deployedAt), + this.pairSetterService.setWhitelistedAddresses( + pairAddress, + whitelistedAddresses, + ), + this.pairSetterService.setFeeDestinations( + pairAddress, + feeDestinations, + ), + this.pairSetterService.setInitialLiquidityAdder( + pairAddress, + initialLiquidityAdder, + ), + this.pairSetterService.setTrustedSwapPairs( + pairAddress, + trustedSwapPairs, + ), ]); await this.deleteCacheKeys(cachedKeys); } } @Cron('*/12 * * * * *') // Update prices and reserves every 12 seconds + @Lock({ name: 'cachePairTokenPrices', verbose: true }) async cacheTokenPrices(): Promise { const pairsMetadata = await this.routerAbi.pairsMetadata(); const invalidatedKeys = []; From 854a642eddac384eabaf21b41f63314d229f7553 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 09:41:57 +0300 Subject: [PATCH 206/313] SERVICES-2513: update tokens cache warmer --- src/modules/tokens/services/token.setter.service.ts | 9 +++++++++ src/services/crons/tokens.cache.warmer.service.ts | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/modules/tokens/services/token.setter.service.ts b/src/modules/tokens/services/token.setter.service.ts index 00ad18700..4e8e2dc2d 100644 --- a/src/modules/tokens/services/token.setter.service.ts +++ b/src/modules/tokens/services/token.setter.service.ts @@ -146,6 +146,15 @@ export class TokenSetterService extends GenericSetterService { ); } + async setCreatedAt(tokenID: string, value: string): Promise { + return await this.setData( + `token.tokenCreatedAt.${tokenID}`, + value, + CacheTtlInfo.Token.remoteTtl, + CacheTtlInfo.Token.localTtl, + ); + } + private getTokenCacheKey(tokenID: string, ...args: any): string { return generateCacheKeyFromParams('token', tokenID, args); } diff --git a/src/services/crons/tokens.cache.warmer.service.ts b/src/services/crons/tokens.cache.warmer.service.ts index e40fa4782..8573b67e8 100644 --- a/src/services/crons/tokens.cache.warmer.service.ts +++ b/src/services/crons/tokens.cache.warmer.service.ts @@ -69,6 +69,7 @@ export class TokensCacheWarmerService { pricePrevious24h, pricePrevious7D, liquidityUSD, + createdAt, ] = await Promise.all([ this.tokenComputeService.computeTokenLast2DaysVolumeUSD( tokenID, @@ -76,6 +77,9 @@ export class TokensCacheWarmerService { this.tokenComputeService.computeTokenPrevious24hPrice(tokenID), this.tokenComputeService.computeTokenPrevious7dPrice(tokenID), this.tokenComputeService.computeTokenLiquidityUSD(tokenID), + this.tokenComputeService.computeTokenCreatedAtTimestamp( + tokenID, + ), ]); const cachedKeys = await Promise.all([ @@ -92,6 +96,7 @@ export class TokensCacheWarmerService { pricePrevious7D, ), this.tokenSetterService.setLiquidityUSD(tokenID, liquidityUSD), + this.tokenSetterService.setCreatedAt(tokenID, createdAt), ]); await this.deleteCacheKeys(cachedKeys); } From e5f9febea983374338ce24a32be2a1b3eb50fded Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 09:42:47 +0300 Subject: [PATCH 207/313] SERVICES-2513: update farms cache warmer --- .../services/farm.setter.service.ts | 23 ++++++++++++++++ src/services/cache.warmer.module.ts | 2 ++ .../crons/farm.cache.warmer.service.ts | 27 +++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/src/modules/farm/base-module/services/farm.setter.service.ts b/src/modules/farm/base-module/services/farm.setter.service.ts index 0df5f92f3..5167720ca 100644 --- a/src/modules/farm/base-module/services/farm.setter.service.ts +++ b/src/modules/farm/base-module/services/farm.setter.service.ts @@ -280,4 +280,27 @@ export abstract class FarmSetterService extends GenericSetterService { CacheTtlInfo.ContractInfo.localTtl, ); } + + async setFarmBaseAPR(farmAddress: string, value: string): Promise { + console.log(this.getCacheKey('farmBaseAPR', farmAddress)); + return await this.setData( + this.getCacheKey('farmBaseAPR', farmAddress), + value, + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } + + async setFarmBoostedAPR( + farmAddress: string, + value: string, + ): Promise { + console.log(this.getCacheKey('farmBoostedAPR', farmAddress)); + return await this.setData( + this.getCacheKey('maxBoostedApr', farmAddress), + value, + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } } diff --git a/src/services/cache.warmer.module.ts b/src/services/cache.warmer.module.ts index c30a097b3..779005282 100644 --- a/src/services/cache.warmer.module.ts +++ b/src/services/cache.warmer.module.ts @@ -36,6 +36,7 @@ import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; import { GovernanceCacheWarmerService } from './crons/governance.cache.warmer.service'; import { GovernanceModule } from '../modules/governance/governance.module'; import { TokensCacheWarmerService } from './crons/tokens.cache.warmer.service'; +import { FarmModuleV2 } from 'src/modules/farm/v2/farm.v2.module'; @Module({ imports: [ @@ -48,6 +49,7 @@ import { TokensCacheWarmerService } from './crons/tokens.cache.warmer.service'; FarmModule, FarmModuleV1_2, FarmModuleV1_3, + FarmModuleV2, StakingModule, StakingProxyModule, MetabondingModule, diff --git a/src/services/crons/farm.cache.warmer.service.ts b/src/services/crons/farm.cache.warmer.service.ts index 1cd5b02c9..87a76d199 100644 --- a/src/services/crons/farm.cache.warmer.service.ts +++ b/src/services/crons/farm.cache.warmer.service.ts @@ -10,6 +10,7 @@ import { FarmAbiFactory } from 'src/modules/farm/farm.abi.factory'; import { FarmAbiServiceV1_2 } from 'src/modules/farm/v1.2/services/farm.v1.2.abi.service'; import { FarmComputeFactory } from 'src/modules/farm/farm.compute.factory'; import { FarmSetterFactory } from 'src/modules/farm/farm.setter.factory'; +import { FarmComputeServiceV2 } from 'src/modules/farm/v2/services/farm.v2.compute.service'; @Injectable() export class FarmCacheWarmerService { @@ -21,6 +22,7 @@ export class FarmCacheWarmerService { private readonly farmComputeFactory: FarmComputeFactory, private readonly farmComputeV1_2: FarmComputeServiceV1_2, private readonly farmComputeV1_3: FarmComputeServiceV1_3, + private readonly farmComputeV2: FarmComputeServiceV2, private readonly farmSetterFactory: FarmSetterFactory, @Inject(PUB_SUB) private pubSub: RedisPubSub, ) {} @@ -117,6 +119,31 @@ export class FarmCacheWarmerService { } } + @Cron(CronExpression.EVERY_MINUTE) + async cacheFarmsV2APRs(): Promise { + for (const address of farmsAddresses()) { + if (farmVersion(address) !== FarmVersion.V2) { + continue; + } + + const [baseApr, boostedApr] = await Promise.all([ + this.farmComputeV2.computeFarmBaseAPR(address), + this.farmComputeV2.computeMaxBoostedApr(address), + ]); + + const cachedKeys = await Promise.all([ + this.farmSetterFactory + .useSetter(address) + .setFarmBaseAPR(address, baseApr), + this.farmSetterFactory + .useSetter(address) + .setFarmBoostedAPR(address, boostedApr), + ]); + this.invalidatedKeys.push(...cachedKeys); + await this.deleteCacheKeys(); + } + } + @Cron(CronExpression.EVERY_MINUTE) async cacheFarmInfo(): Promise { for (const farmAddress of farmsAddresses()) { From 0499f623c6148c7344d2ae747538adc153e5e4ea Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 10:16:45 +0300 Subject: [PATCH 208/313] SERVICES-2513: remove comments + fix unit test --- src/helpers/decorators/caching.decorator.ts | 4 ---- src/modules/tokens/specs/token.service.spec.ts | 3 --- 2 files changed, 7 deletions(-) diff --git a/src/helpers/decorators/caching.decorator.ts b/src/helpers/decorators/caching.decorator.ts index 34b11803b..40886e144 100644 --- a/src/helpers/decorators/caching.decorator.ts +++ b/src/helpers/decorators/caching.decorator.ts @@ -48,13 +48,9 @@ export function GetOrSetCache(cachingOptions: ICachingOptions) { const value = await originalMethod.apply(this, args); - console.log('CACHE MISS', cacheKey); - let { remoteTtl, localTtl } = cachingOptions; if (typeof value === 'undefined' || value === null) { - console.log('NULL', cacheKey, value); - remoteTtl = CacheTtlInfo.NullValue.remoteTtl; localTtl = CacheTtlInfo.NullValue.localTtl; } diff --git a/src/modules/tokens/specs/token.service.spec.ts b/src/modules/tokens/specs/token.service.spec.ts index 07e094483..7d1c2504e 100644 --- a/src/modules/tokens/specs/token.service.spec.ts +++ b/src/modules/tokens/specs/token.service.spec.ts @@ -62,8 +62,5 @@ describe('TokenService', () => { await cachingService.deleteInCache(cacheKey); token = await service.tokenMetadata(tokenID); expect(token).toEqual(undefined); - - token = await service.tokenMetadata(tokenID); - expect(token).toEqual(expectedToken); }); }); From fff202f01b173d89c790b934fe5eaa005afe84df Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 11:22:52 +0300 Subject: [PATCH 209/313] SERVICES-2513: update staking cache warmer --- .../services/farm.setter.service.ts | 2 -- src/modules/router/services/router.service.ts | 4 ++++ .../services/staking.setter.service.ts | 24 +++++++++++++++++++ .../crons/staking.cache.warmer.service.ts | 9 +++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/modules/farm/base-module/services/farm.setter.service.ts b/src/modules/farm/base-module/services/farm.setter.service.ts index 5167720ca..901a5468c 100644 --- a/src/modules/farm/base-module/services/farm.setter.service.ts +++ b/src/modules/farm/base-module/services/farm.setter.service.ts @@ -282,7 +282,6 @@ export abstract class FarmSetterService extends GenericSetterService { } async setFarmBaseAPR(farmAddress: string, value: string): Promise { - console.log(this.getCacheKey('farmBaseAPR', farmAddress)); return await this.setData( this.getCacheKey('farmBaseAPR', farmAddress), value, @@ -295,7 +294,6 @@ export abstract class FarmSetterService extends GenericSetterService { farmAddress: string, value: string, ): Promise { - console.log(this.getCacheKey('farmBoostedAPR', farmAddress)); return await this.setData( this.getCacheKey('maxBoostedApr', farmAddress), value, diff --git a/src/modules/router/services/router.service.ts b/src/modules/router/services/router.service.ts index 3d0527de5..50b1d2e35 100644 --- a/src/modules/router/services/router.service.ts +++ b/src/modules/router/services/router.service.ts @@ -287,6 +287,10 @@ export class RouterService { ): Promise { let sortFieldData = []; + if (!sortField) { + return pairsMetadata; + } + switch (sortField) { case PairSortableFields.DEPLOYED_AT: sortFieldData = await Promise.all( diff --git a/src/modules/staking/services/staking.setter.service.ts b/src/modules/staking/services/staking.setter.service.ts index 407ccdd51..d92deaada 100644 --- a/src/modules/staking/services/staking.setter.service.ts +++ b/src/modules/staking/services/staking.setter.service.ts @@ -213,4 +213,28 @@ export class StakingSetterService extends GenericSetterService { CacheTtlInfo.ContractBalance.localTtl, ); } + + async setStakeFarmBaseAPR( + stakeAddress: string, + value: string, + ): Promise { + return await this.setData( + this.getCacheKey('stakeFarmBaseAPR', stakeAddress), + value, + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } + + async setStakeFarmBoostedAPR( + stakeAddress: string, + value: string, + ): Promise { + return await this.setData( + this.getCacheKey('boostedApr', stakeAddress), + value, + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } } diff --git a/src/services/crons/staking.cache.warmer.service.ts b/src/services/crons/staking.cache.warmer.service.ts index 0a74e227a..114b1afce 100644 --- a/src/services/crons/staking.cache.warmer.service.ts +++ b/src/services/crons/staking.cache.warmer.service.ts @@ -56,12 +56,16 @@ export class StakingCacheWarmerService { divisionSafetyConstant, state, apr, + baseApr, + boostedApr, ] = await Promise.all([ this.stakingAbi.getAnnualPercentageRewardsRaw(address), this.stakingAbi.getMinUnbondEpochsRaw(address), this.stakingAbi.getDivisionSafetyConstantRaw(address), this.stakingAbi.getStateRaw(address), this.stakeCompute.computeStakeFarmAPR(address), + this.stakeCompute.computeStakeFarmBaseAPR(address), + this.stakeCompute.computeBoostedApr(address), ]); const cacheKeys = await Promise.all([ @@ -79,6 +83,11 @@ export class StakingCacheWarmerService { ), this.stakeSetterService.setState(address, state), this.stakeSetterService.setStakeFarmAPR(address, apr), + this.stakeSetterService.setStakeFarmBaseAPR(address, baseApr), + this.stakeSetterService.setStakeFarmBoostedAPR( + address, + boostedApr, + ), ]); await this.deleteCacheKeys(cacheKeys); From 10cb68537e409a45970970f3e90d0608acd54dc3 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 30 Jul 2024 14:17:06 +0300 Subject: [PATCH 210/313] SERVICES-2513: fixes after review - add utils functions for parsing or formatting null/undefined for caching - specify farm version in cache warmer --- src/helpers/decorators/caching.decorator.ts | 17 ++++++-------- .../crons/farm.cache.warmer.service.ts | 6 +---- .../generics/generic.setter.service.ts | 11 +++++---- src/utils/cache.utils.ts | 23 +++++++++++++++++++ 4 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 src/utils/cache.utils.ts diff --git a/src/helpers/decorators/caching.decorator.ts b/src/helpers/decorators/caching.decorator.ts index 40886e144..dfbb5d7c7 100644 --- a/src/helpers/decorators/caching.decorator.ts +++ b/src/helpers/decorators/caching.decorator.ts @@ -3,6 +3,10 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { generateCacheKeyFromParams } from 'src/utils/generate-cache-key'; import { ContextTracker } from '@multiversx/sdk-nestjs-common'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; +import { + formatNullOrUndefined, + parseCachedNullOrUndefined, +} from 'src/utils/cache.utils'; export interface ICachingOptions { baseKey: string; @@ -36,14 +40,10 @@ export function GetOrSetCache(cachingOptions: ICachingOptions) { const cachingService: CacheService = this.cachingService; - let cachedValue = await cachingService.get(cacheKey); + const cachedValue = await cachingService.get(cacheKey); if (cachedValue !== undefined) { - cachedValue = cachedValue === 'null' ? null : cachedValue; - cachedValue = - cachedValue === 'undefined' ? undefined : cachedValue; - - return cachedValue; + return parseCachedNullOrUndefined(cachedValue); } const value = await originalMethod.apply(this, args); @@ -55,12 +55,9 @@ export function GetOrSetCache(cachingOptions: ICachingOptions) { localTtl = CacheTtlInfo.NullValue.localTtl; } - cachedValue = typeof value === 'undefined' ? 'undefined' : value; - cachedValue = cachedValue === null ? 'null' : cachedValue; - await cachingService.set( cacheKey, - cachedValue, + formatNullOrUndefined(value), remoteTtl, localTtl, ); diff --git a/src/services/crons/farm.cache.warmer.service.ts b/src/services/crons/farm.cache.warmer.service.ts index 87a76d199..b0b6510ea 100644 --- a/src/services/crons/farm.cache.warmer.service.ts +++ b/src/services/crons/farm.cache.warmer.service.ts @@ -121,11 +121,7 @@ export class FarmCacheWarmerService { @Cron(CronExpression.EVERY_MINUTE) async cacheFarmsV2APRs(): Promise { - for (const address of farmsAddresses()) { - if (farmVersion(address) !== FarmVersion.V2) { - continue; - } - + for (const address of farmsAddresses([FarmVersion.V2])) { const [baseApr, boostedApr] = await Promise.all([ this.farmComputeV2.computeFarmBaseAPR(address), this.farmComputeV2.computeMaxBoostedApr(address), diff --git a/src/services/generics/generic.setter.service.ts b/src/services/generics/generic.setter.service.ts index 672940e54..92f078f72 100644 --- a/src/services/generics/generic.setter.service.ts +++ b/src/services/generics/generic.setter.service.ts @@ -5,6 +5,7 @@ import { Logger } from 'winston'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { generateCacheKeyFromParams } from '../../utils/generate-cache-key'; import { CacheTtlInfo } from '../caching/cache.ttl.info'; +import { formatNullOrUndefined } from 'src/utils/cache.utils'; @Injectable() export class GenericSetterService { @@ -25,10 +26,12 @@ export class GenericSetterService { localTtl = CacheTtlInfo.NullValue.localTtl; } - value = typeof value === 'undefined' ? 'undefined' : value; - value = value === null ? 'null' : value; - - await this.cachingService.set(cacheKey, value, remoteTtl, localTtl); + await this.cachingService.set( + cacheKey, + formatNullOrUndefined(value), + remoteTtl, + localTtl, + ); return cacheKey; } diff --git a/src/utils/cache.utils.ts b/src/utils/cache.utils.ts new file mode 100644 index 000000000..af60b05f7 --- /dev/null +++ b/src/utils/cache.utils.ts @@ -0,0 +1,23 @@ +export const formatNullOrUndefined = (value: any): any => { + if (typeof value === 'undefined') { + return 'undefined'; + } + + if (value === null) { + return 'null'; + } + + return value; +}; + +export const parseCachedNullOrUndefined = (cachedValue: any): any => { + if (cachedValue === 'undefined') { + return undefined; + } + + if (cachedValue === 'null') { + return null; + } + + return cachedValue; +}; From c8213b4b2961bc87685e7443dbac4b456fd0d14c Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 31 Jul 2024 10:18:29 +0300 Subject: [PATCH 211/313] SERVICES-2492: update pairs filtering to accept an array of states --- src/modules/pair/services/pair.filtering.service.ts | 13 ++++++------- src/modules/router/models/filter.args.ts | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/modules/pair/services/pair.filtering.service.ts b/src/modules/pair/services/pair.filtering.service.ts index 32bcb2e1b..31b0ddd30 100644 --- a/src/modules/pair/services/pair.filtering.service.ts +++ b/src/modules/pair/services/pair.filtering.service.ts @@ -172,7 +172,7 @@ export class PairFilteringService { pairFilter: PairFilterArgs | PairsFilter, pairsMetadata: PairMetadata[], ): Promise { - if (!pairFilter.state) { + if (!pairFilter.state || pairFilter.state.length === 0) { return pairsMetadata; } @@ -182,14 +182,13 @@ export class PairFilteringService { ), ); - const filteredPairsMetadata = []; - for (let index = 0; index < pairsStates.length; index++) { - if (pairsStates[index] === pairFilter.state) { - filteredPairsMetadata.push(pairsMetadata[index]); + return pairsMetadata.filter((_, index) => { + if (!Array.isArray(pairFilter.state)) { + return pairsStates[index] === pairFilter.state; } - } - return filteredPairsMetadata; + return pairFilter.state.includes(pairsStates[index]); + }); } async pairsByFeeState( diff --git a/src/modules/router/models/filter.args.ts b/src/modules/router/models/filter.args.ts index 10c98bbfd..71f1a8cd9 100644 --- a/src/modules/router/models/filter.args.ts +++ b/src/modules/router/models/filter.args.ts @@ -42,8 +42,8 @@ export class PairsFilter { secondTokenID: string; @Field(() => Boolean) issuedLpToken = true; - @Field({ nullable: true }) - state: string; + @Field(() => [String], { nullable: true }) + state: string[]; @Field({ nullable: true }) minVolume: number; @Field({ nullable: true }) From cddce165aa9ab94a548ef2f31b33db68acf57217 Mon Sep 17 00:00:00 2001 From: hschiau Date: Mon, 5 Aug 2024 15:56:03 +0300 Subject: [PATCH 212/313] SERVICES-2524: update token trending score formula - add weights used in formula to config + remove hardcoded values --- src/config/default.json | 5 ++++- .../tokens/services/token.compute.service.ts | 16 +++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 0121df35b..3956153c3 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -620,7 +620,10 @@ "COMPOUND_TRANSACTION_FEE": 0.000365144, "trendingScore": { "MIN_24H_VOLUME": 10000, - "MIN_24H_TRADE_COUNT": 100 + "MIN_24H_TRADE_COUNT": 100, + "PRICE_WEIGHT": 0.5, + "VOLUME_WEIGHT": 0.25, + "TRADES_COUNT_WEIGHT": 0.25 }, "AWS_QUERY_CACHE_WARMER_DELAY": 100 }, diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index 779310b0e..daf85bd85 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -634,13 +634,15 @@ export class TokenComputeService implements ITokenComputeService { this.tokenTradeChange24h(tokenID), ]); - const volumeScore = new BigNumber(0.4).multipliedBy( - Math.log(volumeChange), - ); - const priceScore = new BigNumber(0.3).multipliedBy(priceChange); - const tradeScore = new BigNumber(0.3).multipliedBy( - Math.log(tradeChange), - ); + const volumeScore = new BigNumber( + constantsConfig.trendingScore.VOLUME_WEIGHT, + ).multipliedBy(Math.log(volumeChange)); + const priceScore = new BigNumber( + constantsConfig.trendingScore.PRICE_WEIGHT, + ).multipliedBy(priceChange); + const tradeScore = new BigNumber( + constantsConfig.trendingScore.TRADES_COUNT_WEIGHT, + ).multipliedBy(Math.log(tradeChange)); if (volumeScore.isNaN() || priceScore.isNaN() || tradeScore.isNaN()) { return new BigNumber('-Infinity').toFixed(); From 37a53c6d6b7ca2215369fd06bcdc1e7c22716dbb Mon Sep 17 00:00:00 2001 From: hschiau Date: Mon, 5 Aug 2024 17:55:56 +0300 Subject: [PATCH 213/313] SERVICES-2525: return nextTime only when there are candles in the past --- .../services/trading.view.service.ts | 18 ++++++++++++++++-- .../interfaces/analytics.query.interface.ts | 2 ++ .../mocks/analytics.query.service.mock.ts | 3 +++ .../services/analytics.query.service.ts | 5 +++++ .../timescaledb/timescaledb.query.service.ts | 2 +- 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/modules/trading-view/services/trading.view.service.ts b/src/modules/trading-view/services/trading.view.service.ts index 3a1c85559..f041bf099 100644 --- a/src/modules/trading-view/services/trading.view.service.ts +++ b/src/modules/trading-view/services/trading.view.service.ts @@ -175,10 +175,24 @@ export class TradingViewService { }); if (priceCandles.length === 0) { - return new BarsResponse({ + const seriesStartDate = + await this.analyticsQueryService.getStartDate(series); + + const noDataResponse = new BarsResponse({ s: 'no_data', - nextTime: start - 1, }); + + if (!seriesStartDate) { + return noDataResponse; + } + + const seriesStartUnix = moment(seriesStartDate).unix(); + + if (start > seriesStartUnix) { + noDataResponse.nextTime = start - 1; + } + + return noDataResponse; } return this.formatCandlesResponse(metric, priceCandles, tokenDecimals); diff --git a/src/services/analytics/interfaces/analytics.query.interface.ts b/src/services/analytics/interfaces/analytics.query.interface.ts index 6eb54ba3d..c08ff9f84 100644 --- a/src/services/analytics/interfaces/analytics.query.interface.ts +++ b/src/services/analytics/interfaces/analytics.query.interface.ts @@ -45,4 +45,6 @@ export interface AnalyticsQueryInterface { start, end, }): Promise; + + getStartDate(series: string): Promise; } diff --git a/src/services/analytics/mocks/analytics.query.service.mock.ts b/src/services/analytics/mocks/analytics.query.service.mock.ts index 2b08e569a..545cbe7ea 100644 --- a/src/services/analytics/mocks/analytics.query.service.mock.ts +++ b/src/services/analytics/mocks/analytics.query.service.mock.ts @@ -52,6 +52,9 @@ export class AnalyticsQueryServiceMock implements AnalyticsQueryInterface { getCandles({ series, metric, start, end }): Promise { throw new Error('Method not implemented.'); } + getStartDate(series: string): Promise { + throw new Error('Method not implemented.'); + } } export const AnalyticsQueryServiceProvider = { diff --git a/src/services/analytics/services/analytics.query.service.ts b/src/services/analytics/services/analytics.query.service.ts index d7421025c..8251f0d17 100644 --- a/src/services/analytics/services/analytics.query.service.ts +++ b/src/services/analytics/services/analytics.query.service.ts @@ -116,6 +116,11 @@ export class AnalyticsQueryService implements AnalyticsQueryInterface { }); } + async getStartDate(series: string): Promise { + const service = await this.getService(); + return await service.getStartDate(series); + } + private async getService(): Promise { return this.timescaleDBQuery; } diff --git a/src/services/analytics/timescaledb/timescaledb.query.service.ts b/src/services/analytics/timescaledb/timescaledb.query.service.ts index 01b537b4a..6250641f6 100644 --- a/src/services/analytics/timescaledb/timescaledb.query.service.ts +++ b/src/services/analytics/timescaledb/timescaledb.query.service.ts @@ -421,7 +421,7 @@ export class TimescaleDBQueryService implements AnalyticsQueryInterface { ); } - private async getStartDate(series: string): Promise { + async getStartDate(series: string): Promise { const cacheKey = `startDate.${series}`; const cachedValue = await this.cacheService.get(cacheKey); if (cachedValue !== undefined) { From addd609f418606d07fcc8bacf4518c4474c4e53c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 5 Aug 2024 18:59:45 +0300 Subject: [PATCH 214/313] MEX-494: add farm token supply for the week filed on farmV2 model Signed-off-by: Claudiu Lataretu --- src/modules/farm/models/farm.v2.model.ts | 2 ++ src/modules/farm/v2/farm.v2.resolver.ts | 8 ++++++ .../farm/v2/services/farm.v2.abi.service.ts | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/modules/farm/models/farm.v2.model.ts b/src/modules/farm/models/farm.v2.model.ts index b8a6579ff..f7c9c1628 100644 --- a/src/modules/farm/models/farm.v2.model.ts +++ b/src/modules/farm/models/farm.v2.model.ts @@ -48,6 +48,8 @@ export class FarmModelV2 extends BaseFarmModel { @Field() lastGlobalUpdateWeek: number; @Field() + farmTokenSupplyCurrentWeek: string; + @Field() baseApr: string; @Field() boostedApr: string; diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index e976b42dd..7f8625610 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -159,6 +159,14 @@ export class FarmResolverV2 extends FarmResolver { return this.farmAbi.energyFactoryAddress(parent.address); } + @ResolveField() + async farmTokenSupplyCurrentWeek( + @Parent() parent: FarmModelV2, + ): Promise { + const week = await this.weekTimekeepingAbi.currentWeek(parent.address); + return this.farmAbi.farmSupplyForWeek(parent.address, week); + } + @UseGuards(JwtOrNativeAuthGuard) @Query(() => [UserTotalBoostedPosition], { description: 'Returns the total farm position of the user in the farm', diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index d7b9927b0..e7f49130f 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -436,4 +436,32 @@ export class FarmAbiServiceV2 const response = await this.getGenericData(interaction); return response.firstValue.valueOf().toNumber(); } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async farmSupplyForWeek( + farmAddress: string, + week: number, + ): Promise { + return this.getFarmSupplyForWeekRaw(farmAddress, week); + } + + async getFarmSupplyForWeekRaw( + farmAddress: string, + week: number, + ): Promise { + const contract = await this.mxProxy.getFarmSmartContract(farmAddress); + const interaction: Interaction = + contract.methodsExplicit.getFarmSupplyForWeek([ + new U32Value(new BigNumber(week)), + ]); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toFixed(); + } } From 0a362d8e33cf7a0daabe45ab4183fd5c3ea9c325 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 5 Aug 2024 19:00:45 +0300 Subject: [PATCH 215/313] MEX-494: add farm token supply for week on staking model Signed-off-by: Claudiu Lataretu --- src/modules/staking/models/staking.model.ts | 2 ++ .../staking/services/staking.abi.service.ts | 30 +++++++++++++++++++ src/modules/staking/staking.resolver.ts | 8 +++++ 3 files changed, 40 insertions(+) diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 1fa4f4edc..436c83eba 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -67,6 +67,8 @@ export class StakingModel { @Field() lastGlobalUpdateWeek: number; @Field() + farmTokenSupplyCurrentWeek: string; + @Field() energyFactoryAddress: string; @Field({ description: 'Accumulated boosted rewards for specific week' }) accumulatedRewardsForWeek: string; diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 711f037be..665993259 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -629,6 +629,36 @@ export class StakingAbiService return response.firstValue.valueOf().toFixed(); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'farm', + remoteTtl: CacheTtlInfo.ContractInfo.remoteTtl, + localTtl: CacheTtlInfo.ContractInfo.localTtl, + }) + async farmSupplyForWeek( + farmAddress: string, + week: number, + ): Promise { + return this.getFarmSupplyForWeekRaw(farmAddress, week); + } + + async getFarmSupplyForWeekRaw( + farmAddress: string, + week: number, + ): Promise { + const contract = await this.mxProxy.getStakingSmartContract( + farmAddress, + ); + const interaction: Interaction = + contract.methodsExplicit.getFarmSupplyForWeek([ + new U32Value(new BigNumber(week)), + ]); + const response = await this.getGenericData(interaction); + return response.firstValue.valueOf().toFixed(); + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index dabe355bd..8dcf81a03 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -191,6 +191,14 @@ export class StakingResolver { ); } + @ResolveField() + async farmTokenSupplyCurrentWeek( + @Parent() parent: StakingModel, + ): Promise { + const week = await this.weekTimekeepingAbi.currentWeek(parent.address); + return this.stakingAbi.farmSupplyForWeek(parent.address, week); + } + @ResolveField() async energyFactoryAddress( @Parent() parent: StakingModel, From cfdd2f07b6d096849c24aa1bb58254831441a625 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 5 Aug 2024 19:46:44 +0300 Subject: [PATCH 216/313] MEX-470: fix price discovery tokens prices Signed-off-by: Claudiu Lataretu --- .../services/price.discovery.compute.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/price-discovery/services/price.discovery.compute.service.ts b/src/modules/price-discovery/services/price.discovery.compute.service.ts index 0d1bbc91e..7c31aff10 100644 --- a/src/modules/price-discovery/services/price.discovery.compute.service.ts +++ b/src/modules/price-discovery/services/price.discovery.compute.service.ts @@ -241,6 +241,10 @@ export class PriceDiscoveryComputeService this.apiService.getBlockByNonce(1, endBlockNonce), ]); + if (startBlock === undefined) { + return []; + } + const [startDate, endDate] = [ startBlock.timestamp, endBlock ? endBlock.timestamp : moment().unix(), From 6efaeb50f972de647ab7a60d93678961eabd14a4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 5 Aug 2024 19:47:42 +0300 Subject: [PATCH 217/313] MEX-470: add config for new testing environment setup Signed-off-by: Claudiu Lataretu --- src/config/shadowfork2.json | 125 ++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/config/shadowfork2.json diff --git a/src/config/shadowfork2.json b/src/config/shadowfork2.json new file mode 100644 index 000000000..86642deee --- /dev/null +++ b/src/config/shadowfork2.json @@ -0,0 +1,125 @@ +{ + "multiversx": { + "chainID": "D" + }, + "scAddress": { + "routerAddress": "erd1qqqqqqqqqqqqqpgqq66xk9gfr4esuhem3jru86wg5hvp33a62jps2fy57p", + "MEX-455c57": "erd1qqqqqqqqqqqqqpgqa0fsfshnff4n76jhcye6k7uvd7qacsq42jpsp6shh2", + "WEGLD_USDC": "erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq", + "distributionAddress": "erd1qqqqqqqqqqqqqpgqyg62a3tswuzun2wzp8a83sjkc709wwjt2jpssphddm", + "proxyDexAddress": { + "v1": "erd1qqqqqqqqqqqqqpgqrc4pg2xarca9z34njcxeur622qmfjp8w2jps89fxnl", + "v2": "erd1qqqqqqqqqqqqqpgqt6ltx52ukss9d2qag2k67at28a36xc9lkp2sr06394" + }, + "lockedAssetAddress": "erd1qqqqqqqqqqqqqpgqjpt0qqgsrdhp2xqygpjtfrpwf76f9nvg2jpsg4q7th", + "metabondingStakingAddress": "erd1qqqqqqqqqqqqqpgqt7tyyswqvplpcqnhwe20xqrj7q7ap27d2jps7zczse", + "simpleLockAddress": [ + "erd1qqqqqqqqqqqqqpgqs0jjyjmx0cvek4p8yj923q5yreshtpa62jpsz6vt84", + "erd1qqqqqqqqqqqqqpgqawujux7w60sjhm8xdx3n0ed8v9h7kpqu2jpsecw6ek", + "erd1qqqqqqqqqqqqqpgq6nu2t8lzakmcfmu4pu5trjdarca587hn2jpsyjapr5" + ], + "wrappingAddress": { + "shardID-0": "erd1qqqqqqqqqqqqqpgqvc7gdl0p4s97guh498wgz75k8sav6sjfjlwqh679jy", + "shardID-1": "erd1qqqqqqqqqqqqqpgqhe8t5jewej70zupmh44jurgn29psua5l2jps3ntjj3", + "shardID-2": "erd1qqqqqqqqqqqqqpgqmuk0q2saj0mgutxm4teywre6dl8wqf58xamqdrukln" + }, + "priceDiscovery": [ + "erd1qqqqqqqqqqqqqpgq38cmvwwkujae3c0xmc9p9554jjctkv4w2jps83r492", + "erd1qqqqqqqqqqqqqpgq666ta9cwtmjn6dtzv9fhfcmahkjmzhg62jps49k7k8", + "erd1qqqqqqqqqqqqqpgqq93kw6jngqjugfyd7pf555lwurfg2z5e2jpsnhhywk" + ], + "simpleLockEnergy": "erd1qqqqqqqqqqqqqpgq0tajepcazernwt74820t8ef7t28vjfgukp2sw239f3", + "feesCollector": "erd1qqqqqqqqqqqqqpgqjsnxqprks7qxfwkcg2m2v9hxkrchgm9akp2segrswt", + "energyUpdate": "erd1qqqqqqqqqqqqqpgqg48z8qfm028ze6y3d6tamdfaw6vn774tkp2shg5dt5", + "tokenUnstake": "erd1qqqqqqqqqqqqqpgqxv0y4p6vvszrknaztatycac77yvsxqrrkp2sghd86c", + "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq97fctvqlskzu0str05muew2qyjttp8kfkp2sl9k0gx", + "escrow": "erd1qqqqqqqqqqqqqpgqeh4yv09rmyg4xgn5ma03mvm4v5gndu8w2jpsglz3cn", + "positionCreator": "erd1qqqqqqqqqqqqqpgqp6s52q4m6nkf5usqacjuuqymnayv6puc2jpsp35nhg", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqucxekem48mrlzcenugl7z6k38l2ead702jpsrlfg4r", + "composableTasks": "erd1qqqqqqqqqqqqqpgqa6a50v9m5nc50cxaz9qxrd8mz9lxnfjt2jpsjj0cky" + }, + "governance": { + "oldEnergy": { + "cvadratic": [ + "erd1qqqqqqqqqqqqqpgqdt9aady5jp7av97m7rqxh6e5ywyqsplz2jps5mw02n" + ] + }, + "energy": { + "cvadratic": [] + }, + "tokenSnapshot": { + "linear": [ + "erd1qqqqqqqqqqqqqpgq7cywmem5g66ad49rce0ctu6yqmscmp9v7j6q7cded4" + ], + "cvadratic": [] + } + }, + "farms": { + "v1.2": [ + "erd1qqqqqqqqqqqqqpgqye633y7k0zd7nedfnp3m48h24qygm5jl2jpslxallh", + "erd1qqqqqqqqqqqqqpgqsw9pssy8rchjeyfh8jfafvl3ynum0p9k2jps6lwewp", + "erd1qqqqqqqqqqqqqpgqv4ks4nzn2cw96mm06lt7s2l3xfrsznmp2jpsszdry5" + ], + "v1.3": { + "unlockedRewards": [ + "erd1qqqqqqqqqqqqqpgqnqvjnn4haygsw2hls2k9zjjadnjf9w7g2jpsmc60a4", + "erd1qqqqqqqqqqqqqpgqutddd7dva0x4xmehyljp7wh7ecynag0u2jpskxx6xt", + "erd1qqqqqqqqqqqqqpgqe9v45fnpkv053fj0tk7wvnkred9pms892jps9lkqrn", + "erd1qqqqqqqqqqqqqpgqs2puacesqywt84jawqenmmree20uj80d2jpsefuka9", + "erd1qqqqqqqqqqqqqpgq6xp2uvdk22frgqkwm9pwa564gqg2gxfm2jpszggzgw", + "erd1qqqqqqqqqqqqqpgqmaj6qc4c6f9ldkuwllqh7pyd8e9yu3m82jpscc6f9s", + "erd1qqqqqqqqqqqqqpgqc3p5xkj656h2ts649h4f447se4clns662jpsccq7rx", + "erd1qqqqqqqqqqqqqpgqzy0qwv0jf0yk8tysll0y6ea0fhpfqlp32jpsder9ne" + ], + "lockedRewards": [ + "erd1qqqqqqqqqqqqqpgqyawg3d9r4l27zue7e9sz7djf7p9aj3sz2jpsm070jf", + "erd1qqqqqqqqqqqqqpgqwtzqckt793q8ggufxxlsv3za336674qq2jpszzgqra", + "erd1qqqqqqqqqqqqqpgq7qhsw8kffad85jtt79t9ym0a4ycvan9a2jps0zkpen", + "erd1qqqqqqqqqqqqqpgqs2mmvzpu6wz83z3vthajl4ncpwz67ctu2jpsrcl0ct", + "erd1qqqqqqqqqqqqqpgqdt892e0aflgm0xwryhhegsjhw0zru60m2jps959w5z", + "erd1qqqqqqqqqqqqqpgq3f8jfeg34ujzy0muhe9dvn5yngwgud022jpsscjxgl", + "erd1qqqqqqqqqqqqqpgqcejzfjfmmgvq7yjch2tqnhhsngr7hqyw2jpshce5u2", + "erd1qqqqqqqqqqqqqpgqlu6vrtkgfjh68nycf5sdw4nyzudncns42jpsr7szch" + ], + "customRewards": [ + "erd1qqqqqqqqqqqqqpgq5e2m9df5yxxkmr86rusejc979arzayjk2jpsz2q43s" + ] + }, + "v2": { + "lockedRewards": [ + "erd1qqqqqqqqqqqqqpgqapxdp9gjxtg60mjwhle3n6h88zch9e7kkp2s8aqhkg", + "erd1qqqqqqqqqqqqqpgqv0pz5z3fkz54nml6pkzzdruuf020gqzykp2sya7kkv", + "erd1qqqqqqqqqqqqqpgqenvn0s3ldc94q2mlkaqx4arj3zfnvnmakp2sxca2h9", + "erd1qqqqqqqqqqqqqpgqrdq6zvepdxg36rey8pmluwur43q4hcx3kp2su4yltq", + "erd1qqqqqqqqqqqqqpgq4acurmluezvmhna8tztgcrnwh0l70a2wkp2sfh6jkp", + "erd1qqqqqqqqqqqqqpgquhqfknr9dg5t0xma0zcc55dm8gwv5rpkkp2sq8f40p", + "erd1qqqqqqqqqqqqqpgq6v5ta4memvrhjs4x3gqn90c3pujc77takp2sqhxm9j", + "erd1qqqqqqqqqqqqqpgqqqckvlhd3n7ntt5w3vln4xh3tsfpj0hr2jpsxlzgcj", + "erd1qqqqqqqqqqqqqpgqcfkmz2j0e8pj0nzuk9j6mzcav0wpthq52jpsg546ch", + "erd1qqqqqqqqqqqqqpgqr9gx79xls33tnzt0a94uvnkf452fz26q2jpsau4ulw", + "erd1qqqqqqqqqqqqqpgqwcdc3ve8ww6psam9hhu5nqqjlt22nagw2jpspt9rev", + "erd1qqqqqqqqqqqqqpgqmvzrwmwkrrzug50e64kk7xerufncp47d2jpsf9m8rx", + "erd1qqqqqqqqqqqqqpgqc6fyckfxvehlwr2zvfl34dycwgtm988g2jpsrnyazn", + "erd1qqqqqqqqqqqqqpgqm78a0zmuqlm7z5qw9nseg6ecdszafljp2jpsxkm4rp", + "erd1qqqqqqqqqqqqqpgqk5xplswklltypmazcxx5mhn3jdvaxuf92jpsudss7r", + "erd1qqqqqqqqqqqqqpgqjgtun0fhl2em5ayukvahm3myltaxc5tz2jpssz8axf", + "erd1qqqqqqqqqqqqqpgq4se997u3eyhjmpwaa57lvdgvmyn23yqpx9rsdzja7j", + "erd1qqqqqqqqqqqqqpgqhh8lke0kfch0g20jrmskczwq86d23qhex9rs5hm4uk", + "erd1qqqqqqqqqqqqqpgqgjqu7c0mq8a4jd4d43st44gvwhduvqaxkp2s7vsq64" + ] + } + }, + "tokenProviderUSD": "WEGLD-bd4d79", + "tokensSupply": ["MEX-455c57"], + "cachedTokensPrice": ["MEX-455c57", "WEGLD-bd4d79"], + "constants": { + "MEX_TOKEN_ID": "MEX-455c57", + "USDC_TOKEN_ID": "USDC-c76f1f", + "MAINTENANCE": false, + "roundedSwapEnable": { + "USDC-c76f1f": "5000000", + "WEGLD-bd4d79": "100000000000000000", + "MEX-455c57": "1000000000000000000000000" + } + } +} From ee3ad5ec6421fc0746a41970c20716a678681d73 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 5 Aug 2024 22:39:10 +0300 Subject: [PATCH 218/313] MEX-495: Fix total farm position migrations transactions Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/services/farm.v2.transaction.service.ts | 6 +----- .../services/proxy-farm/proxy.farm.transactions.service.ts | 2 +- .../staking/services/staking.transactions.service.ts | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.transaction.service.ts b/src/modules/farm/v2/services/farm.v2.transaction.service.ts index 93dc84eb4..b267da25e 100644 --- a/src/modules/farm/v2/services/farm.v2.transaction.service.ts +++ b/src/modules/farm/v2/services/farm.v2.transaction.service.ts @@ -166,14 +166,10 @@ export class FarmTransactionServiceV2 extends TransactionsFarmService { this.mxApi.getNftsCountForUser(userAddress), ]); - if (userNftsCount === 0) { - return []; - } - const userNfts = await this.contextGetter.getNftsForUser( userAddress, 0, - userNftsCount, + userNftsCount > 0 ? userNftsCount : 100, 'MetaESDT', [farmTokenID], ); diff --git a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts index f126a16c3..2693d035a 100644 --- a/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts +++ b/src/modules/proxy/services/proxy-farm/proxy.farm.transactions.service.ts @@ -277,7 +277,7 @@ export class ProxyFarmTransactionsService { const userNfts = await this.contextGetter.getNftsForUser( sender, 0, - userNftsCount, + userNftsCount > 0 ? userNftsCount : 100, 'MetaESDT', [wrappedFarmTokenID], ); diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index dabb0e039..f01dcdc83 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -215,7 +215,7 @@ export class StakingTransactionService { const userNfts = await this.contextGetter.getNftsForUser( userAddress, 0, - userNftsCount, + userNftsCount > 0 ? userNftsCount : 100, 'MetaESDT', [stakeTokenID], ); From cbbdbdf62114f630bc5a27c397d473d0a70f6d0e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 6 Aug 2024 16:48:05 +0300 Subject: [PATCH 219/313] MEX-496: fix metabonding negative energy check Signed-off-by: Claudiu Lataretu --- .../userEnergy/user.energy.compute.service.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/modules/user/services/userEnergy/user.energy.compute.service.ts b/src/modules/user/services/userEnergy/user.energy.compute.service.ts index c238b7462..d12f02124 100644 --- a/src/modules/user/services/userEnergy/user.energy.compute.service.ts +++ b/src/modules/user/services/userEnergy/user.energy.compute.service.ts @@ -454,20 +454,23 @@ export class UserEnergyComputeService { const userMetabondingEntry = await this.metabondingAbi.userEntry( userAddress, ); - const metabondingTokensAttributes = - await this.mxApi.getNftAttributesByTokenIdentifier( - scAddress.metabondingStakingAddress, - tokenIdentifier( - lkmexTokenID, - userMetabondingEntry.tokenNonce, + + if (userMetabondingEntry.tokenNonce > 0) { + const metabondingTokensAttributes = + await this.mxApi.getNftAttributesByTokenIdentifier( + scAddress.metabondingStakingAddress, + tokenIdentifier( + lkmexTokenID, + userMetabondingEntry.tokenNonce, + ), + ); + metabondingCheck = this.checkLKMEXNegativeEnergy(stats.epoch, [ + LockedAssetAttributes.fromAttributes( + userMetabondingEntry.tokenNonce >= lkmexActivationNonce, + metabondingTokensAttributes, ), - ); - metabondingCheck = this.checkLKMEXNegativeEnergy(stats.epoch, [ - LockedAssetAttributes.fromAttributes( - userMetabondingEntry.tokenNonce >= lkmexActivationNonce, - metabondingTokensAttributes, - ), - ]); + ]); + } } return new UserNegativeEnergyCheck({ From c2a2961a5af986bbf4cf5ae0d75ac1732e9e6972 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 7 Aug 2024 12:29:54 +0300 Subject: [PATCH 220/313] SERVICES-2532: expose farm,staking farm,staking proxy farm addr on pair --- src/modules/pair/models/pair.model.ts | 9 +++++++++ src/modules/pair/pair.resolver.ts | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 246752ea5..1493ceff5 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -210,6 +210,15 @@ export class PairModel { @Field(() => PairRewardTokensModel, { nullable: true }) rewardTokens: PairRewardTokensModel; + @Field({ nullable: true }) + farmAddress: string; + + @Field({ nullable: true }) + stakingFarmAddress: string; + + @Field({ nullable: true }) + stakingProxyAddress: string; + constructor(init?: Partial) { Object.assign(this, init); } diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index affc95674..01bdcf41f 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -397,6 +397,23 @@ export class PairResolver { return new PairRewardTokensModel({ address: parent.address }); } + @ResolveField() + async farmAddress(@Parent() parent: PairModel): Promise { + return await this.pairCompute.getPairFarmAddress(parent.address); + } + + @ResolveField() + async stakingFarmAddress(@Parent() parent: PairModel): Promise { + return await this.pairCompute.getPairStakingFarmAddress(parent.address); + } + + @ResolveField() + async stakingProxyAddress(@Parent() parent: PairModel): Promise { + return await this.pairCompute.getPairStakingProxyAddress( + parent.address, + ); + } + @Query(() => String) async getAmountOut( @Args('pairAddress') pairAddress: string, From f020a3d50908513e71c6512c08a9cb77efc995d0 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 7 Aug 2024 12:39:02 +0300 Subject: [PATCH 221/313] SERVICES-2532: remove staking farm address field --- src/modules/pair/models/pair.model.ts | 3 --- src/modules/pair/pair.resolver.ts | 5 ----- 2 files changed, 8 deletions(-) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 1493ceff5..de9e01b6e 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -213,9 +213,6 @@ export class PairModel { @Field({ nullable: true }) farmAddress: string; - @Field({ nullable: true }) - stakingFarmAddress: string; - @Field({ nullable: true }) stakingProxyAddress: string; diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 01bdcf41f..57d2d9e91 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -402,11 +402,6 @@ export class PairResolver { return await this.pairCompute.getPairFarmAddress(parent.address); } - @ResolveField() - async stakingFarmAddress(@Parent() parent: PairModel): Promise { - return await this.pairCompute.getPairStakingFarmAddress(parent.address); - } - @ResolveField() async stakingProxyAddress(@Parent() parent: PairModel): Promise { return await this.pairCompute.getPairStakingProxyAddress( From dd9b35cdf23f43db8f186f0984ad46dc99262518 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 7 Aug 2024 13:12:38 +0300 Subject: [PATCH 222/313] MEX-470: change chainID for battlenet environment Signed-off-by: Claudiu Lataretu --- src/config/shadowfork2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/shadowfork2.json b/src/config/shadowfork2.json index 86642deee..e307317e5 100644 --- a/src/config/shadowfork2.json +++ b/src/config/shadowfork2.json @@ -1,6 +1,6 @@ { "multiversx": { - "chainID": "D" + "chainID": "B" }, "scAddress": { "routerAddress": "erd1qqqqqqqqqqqqqpgqq66xk9gfr4esuhem3jru86wg5hvp33a62jps2fy57p", From 4dddbd8eaf3ddd4e9e183a44fdbf44e1d0b72667 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 7 Aug 2024 14:45:44 +0300 Subject: [PATCH 223/313] MEX-470: update battlenet smart contracts configs Signed-off-by: Claudiu Lataretu --- src/config/shadowfork2.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/shadowfork2.json b/src/config/shadowfork2.json index e307317e5..59a6b67b5 100644 --- a/src/config/shadowfork2.json +++ b/src/config/shadowfork2.json @@ -34,9 +34,9 @@ "tokenUnstake": "erd1qqqqqqqqqqqqqpgqxv0y4p6vvszrknaztatycac77yvsxqrrkp2sghd86c", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq97fctvqlskzu0str05muew2qyjttp8kfkp2sl9k0gx", "escrow": "erd1qqqqqqqqqqqqqpgqeh4yv09rmyg4xgn5ma03mvm4v5gndu8w2jpsglz3cn", - "positionCreator": "erd1qqqqqqqqqqqqqpgqp6s52q4m6nkf5usqacjuuqymnayv6puc2jpsp35nhg", - "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqucxekem48mrlzcenugl7z6k38l2ead702jpsrlfg4r", - "composableTasks": "erd1qqqqqqqqqqqqqpgqa6a50v9m5nc50cxaz9qxrd8mz9lxnfjt2jpsjj0cky" + "positionCreator": "erd1qqqqqqqqqqqqqpgqucxekem48mrlzcenugl7z6k38l2ead702jpsrlfg4r", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgq43cyf9yjfy79ey0cax55kenxgpefhayk2jps6zt793", + "composableTasks": "erd1qqqqqqqqqqqqqpgqte9qae42w7x4karshkmfdrmnv56ka2zc2jpsqg0ner" }, "governance": { "oldEnergy": { From 5b5b573b797bbb3427a09f5ea4ba3a7976099ad1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 7 Aug 2024 17:34:45 +0300 Subject: [PATCH 224/313] SERVICES-2532: return staking proxy address with farm V2 for pair model Signed-off-by: Claudiu Lataretu --- src/modules/pair/services/pair.compute.service.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 31685b05e..936fe95fb 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -780,15 +780,16 @@ export class PairComputeService implements IPairComputeService { const stakingProxyAddresses = await this.remoteConfigGetterService.getStakingProxyAddresses(); + const farmAddress = await this.getPairFarmAddress(pairAddress); - const pairAddresses = await Promise.all( + const farmsAddresses = await Promise.all( stakingProxyAddresses.map((address) => - this.stakingProxyAbiService.pairAddress(address), + this.stakingProxyAbiService.lpFarmAddress(address), ), ); - const stakingProxyIndex = pairAddresses.findIndex( - (address) => address === pairAddress, + const stakingProxyIndex = farmsAddresses.findIndex( + (address) => address === farmAddress, ); return stakingProxyIndex === -1 From 6f3cc0d394ec9c8deac601dd6a0697a8f24abef4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 8 Aug 2024 10:27:57 +0300 Subject: [PATCH 225/313] MEX-470: disable logs processor on battlenet env Signed-off-by: Claudiu Lataretu --- src/services/crons/logs.processor.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/crons/logs.processor.service.ts b/src/services/crons/logs.processor.service.ts index 9fd804bcc..fa2fb8559 100644 --- a/src/services/crons/logs.processor.service.ts +++ b/src/services/crons/logs.processor.service.ts @@ -43,7 +43,7 @@ export class LogsProcessorService { @Cron(CronExpression.EVERY_MINUTE) async handleNewLogs() { - if (this.isProcessing) { + if (this.isProcessing || process.env.NODE_ENV === 'shadowfork2') { return; } From 62d5d0a04c039fd7007f59b024fb28cfdf785f4c Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 8 Aug 2024 13:59:15 +0300 Subject: [PATCH 226/313] SERVICES-2534: add escrow SC storage cache warmer --- .../escrow/services/escrow.abi.service.ts | 34 +++++++------- src/services/cache.warmer.module.ts | 4 ++ .../crons/escrow.cache.warmer.service.ts | 45 +++++++++++++++++++ 3 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 src/services/crons/escrow.cache.warmer.service.ts diff --git a/src/modules/escrow/services/escrow.abi.service.ts b/src/modules/escrow/services/escrow.abi.service.ts index 215fd9642..d048526cb 100644 --- a/src/modules/escrow/services/escrow.abi.service.ts +++ b/src/modules/escrow/services/escrow.abi.service.ts @@ -115,14 +115,7 @@ export class EscrowAbiService } async getAllReceiversRaw(senderAddress: string): Promise { - let hexValues = await this.cachingService.get(`escrow.scKeys`); - if (!hexValues || hexValues === undefined) { - hexValues = await this.mxGateway.getSCStorageKeys( - scAddress.escrow, - [], - ); - await this.escrowSetter.setSCStorageKeys(hexValues); - } + const hexValues = await this.scKeys(); const receivers = []; const allSendersHex = Buffer.from('allSenders').toString('hex'); @@ -311,14 +304,7 @@ export class EscrowAbiService } async getAllAddressesWithPermissionsRaw(): Promise { - let hexValues = await this.cachingService.get(`escrow.scKeys`); - if (!hexValues || hexValues === undefined) { - hexValues = await this.mxGateway.getSCStorageKeys( - scAddress.escrow, - [], - ); - await this.escrowSetter.setSCStorageKeys(hexValues); - } + const hexValues = await this.scKeys(); const addresses = []; const permissionsHex = Buffer.from('permissions').toString('hex'); @@ -331,4 +317,20 @@ export class EscrowAbiService return addresses; } + + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'escrow', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async scKeys(): Promise { + return await this.scKeysRaw(); + } + + async scKeysRaw(): Promise { + return await this.mxGateway.getSCStorageKeys(scAddress.escrow, []); + } } diff --git a/src/services/cache.warmer.module.ts b/src/services/cache.warmer.module.ts index 779005282..ee0c89535 100644 --- a/src/services/cache.warmer.module.ts +++ b/src/services/cache.warmer.module.ts @@ -37,6 +37,8 @@ import { GovernanceCacheWarmerService } from './crons/governance.cache.warmer.se import { GovernanceModule } from '../modules/governance/governance.module'; import { TokensCacheWarmerService } from './crons/tokens.cache.warmer.service'; import { FarmModuleV2 } from 'src/modules/farm/v2/farm.v2.module'; +import { EscrowCacheWarmerService } from './crons/escrow.cache.warmer.service'; +import { EscrowModule } from 'src/modules/escrow/escrow.module'; @Module({ imports: [ @@ -63,6 +65,7 @@ import { FarmModuleV2 } from 'src/modules/farm/v2/farm.v2.module'; RemoteConfigModule, GovernanceModule, DynamicModuleUtils.getCacheModule(), + EscrowModule, ], controllers: [], providers: [ @@ -81,6 +84,7 @@ import { FarmModuleV2 } from 'src/modules/farm/v2/farm.v2.module'; LogsProcessorService, ElasticService, TokensCacheWarmerService, + EscrowCacheWarmerService, ], }) export class CacheWarmerModule {} diff --git a/src/services/crons/escrow.cache.warmer.service.ts b/src/services/crons/escrow.cache.warmer.service.ts new file mode 100644 index 000000000..7e960fdb2 --- /dev/null +++ b/src/services/crons/escrow.cache.warmer.service.ts @@ -0,0 +1,45 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { PUB_SUB } from '../redis.pubSub.module'; +import { RedisPubSub } from 'graphql-redis-subscriptions'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { Logger } from 'winston'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { Lock } from '@multiversx/sdk-nestjs-common'; +import { PerformanceProfiler } from '@multiversx/sdk-nestjs-monitoring'; +import { EscrowAbiService } from 'src/modules/escrow/services/escrow.abi.service'; +import { EscrowSetterService } from 'src/modules/escrow/services/escrow.setter.service'; + +@Injectable() +export class EscrowCacheWarmerService { + constructor( + private readonly escrowAbiService: EscrowAbiService, + private readonly escrowSetterService: EscrowSetterService, + @Inject(PUB_SUB) private pubSub: RedisPubSub, + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + ) {} + + @Cron(CronExpression.EVERY_5_MINUTES) + @Lock({ name: 'cacheEscrowScStorageKeys', verbose: true }) + async cacheScStorageKeys(): Promise { + this.logger.info('Start refresh cached escrow SC storage keys', { + context: 'CacheEscrow', + }); + + const profiler = new PerformanceProfiler(); + const hexValues = await this.escrowAbiService.scKeysRaw(); + const cachedKey = await this.escrowSetterService.setSCStorageKeys( + hexValues, + ); + + await this.deleteCacheKeys([cachedKey]); + + profiler.stop(); + this.logger.info( + `Finish refresh escrow SC storage keys in ${profiler.duration}`, + ); + } + + private async deleteCacheKeys(invalidatedKeys: string[]) { + await this.pubSub.publish('deleteCacheKeys', invalidatedKeys); + } +} From 8bf1113b1bf77f50eb384653e810ef6cf973b962 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 8 Aug 2024 15:38:19 +0300 Subject: [PATCH 227/313] MEX-480: Update gas limits Signed-off-by: Claudiu Lataretu --- src/config/default.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 0121df35b..f3186177e 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -241,7 +241,7 @@ }, "claimRewards": 30000000 }, - "claimBoostedRewards": 20000000 + "claimBoostedRewards": 30000000 }, "admin": { "end_produce_rewards": 200000000, @@ -390,8 +390,8 @@ }, "unstakeFarm": 18500000, "unbondFarm": 8000000, - "claimRewards": 15000000, - "claimBoostedRewards": 20000000, + "claimRewards": 40000000, + "claimBoostedRewards": 30000000, "claimRewardsWithNewValue": 7500000, "compoundRewards": 10000000, "mergeTokens": 20000000, @@ -504,7 +504,7 @@ "liquidityPosition": 57500000, "farmPosition": 87000000, "dualFarmPosition": 115000000, - "stakingPosition": 55000000 + "stakingPosition": 65000000 }, "dualTokens": { "farmPosition": 35000000, From 805dea36946027ee0fd961b34007c414a6f125e2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 8 Aug 2024 17:10:39 +0300 Subject: [PATCH 228/313] MEX-498: Add filtering of farms query Signed-off-by: Claudiu Lataretu --- src/modules/farm/farm.factory.ts | 12 ++++++++++-- src/modules/farm/farm.query.resolver.ts | 12 ++++++++++-- src/modules/farm/models/farm.args.ts | 9 +++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/modules/farm/farm.factory.ts b/src/modules/farm/farm.factory.ts index 68768c6eb..1ac2f2c45 100644 --- a/src/modules/farm/farm.factory.ts +++ b/src/modules/farm/farm.factory.ts @@ -10,6 +10,7 @@ import { FarmServiceV1_3 } from './v1.3/services/farm.v1.3.service'; import { FarmServiceV2 } from './v2/services/farm.v2.service'; import { FarmServiceBase } from './base-module/services/farm.base.service'; import { FarmModelV2 } from './models/farm.v2.model'; +import { FarmsFilter } from './models/farm.args'; @Injectable() export class FarmFactoryService { @@ -19,9 +20,16 @@ export class FarmFactoryService { private readonly farmServiceV2: FarmServiceV2, ) {} - getFarms(): Array { + getFarms(filters: FarmsFilter): Array { const farms: Array = []; - for (const address of farmsAddresses()) { + let addresses = farmsAddresses(filters.versions); + if (filters.addresses) { + addresses = addresses.filter((address) => + filters.addresses.includes(address), + ); + } + + for (const address of addresses) { const version = farmVersion(address); switch (version) { case FarmVersion.V1_2: diff --git a/src/modules/farm/farm.query.resolver.ts b/src/modules/farm/farm.query.resolver.ts index 2870d7d00..007511157 100644 --- a/src/modules/farm/farm.query.resolver.ts +++ b/src/modules/farm/farm.query.resolver.ts @@ -5,6 +5,7 @@ import { FarmFactoryService } from './farm.factory'; import { BatchFarmRewardsComputeArgs, CalculateRewardsArgs, + FarmsFilter, } from './models/farm.args'; import { ExitFarmTokensModel, RewardsModel } from './models/farm.model'; import { FarmsUnion } from './models/farm.union'; @@ -15,8 +16,15 @@ export class FarmQueryResolver { constructor(private readonly farmFactory: FarmFactoryService) {} @Query(() => [FarmsUnion]) - async farms(): Promise> { - return this.farmFactory.getFarms(); + async farms( + @Args({ + name: 'filters', + type: () => FarmsFilter, + nullable: true, + }) + filters: FarmsFilter, + ): Promise> { + return this.farmFactory.getFarms(filters); } @UseGuards(JwtOrNativeAuthGuard) diff --git a/src/modules/farm/models/farm.args.ts b/src/modules/farm/models/farm.args.ts index 479ec1d21..2724a7cde 100644 --- a/src/modules/farm/models/farm.args.ts +++ b/src/modules/farm/models/farm.args.ts @@ -1,6 +1,15 @@ import { ArgsType, Field, InputType, Int } from '@nestjs/graphql'; import { InputTokenModel } from 'src/models/inputToken.model'; +@InputType() +export class FarmsFilter { + @Field(() => [String], { nullable: true }) + versions?: string[]; + + @Field(() => [String], { nullable: true }) + addresses?: string[]; +} + @InputType() export class CalculateRewardsArgs { @Field() From 30a8d08f53d31bcaa66fa5ab4e37163190499674 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 8 Aug 2024 17:19:45 +0300 Subject: [PATCH 229/313] MEX-498: fix farm filtering with no filtering Signed-off-by: Claudiu Lataretu --- src/modules/farm/farm.factory.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/farm/farm.factory.ts b/src/modules/farm/farm.factory.ts index 1ac2f2c45..cfdb1e9da 100644 --- a/src/modules/farm/farm.factory.ts +++ b/src/modules/farm/farm.factory.ts @@ -20,10 +20,10 @@ export class FarmFactoryService { private readonly farmServiceV2: FarmServiceV2, ) {} - getFarms(filters: FarmsFilter): Array { + getFarms(filters?: FarmsFilter): Array { const farms: Array = []; - let addresses = farmsAddresses(filters.versions); - if (filters.addresses) { + let addresses = farmsAddresses(filters?.versions); + if (filters?.addresses) { addresses = addresses.filter((address) => filters.addresses.includes(address), ); From d97dc5779deb8b11505495b155f9c2d88e7d3be2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 8 Aug 2024 18:07:20 +0300 Subject: [PATCH 230/313] MEX-499: compute token value locked based on current liquidity Signed-off-by: Claudiu Lataretu --- .../tokens/services/token.compute.service.ts | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index d6b6af242..41c7f5c60 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -26,6 +26,8 @@ import moment from 'moment'; import { ESLogsService } from 'src/services/elastic-search/services/es.logs.service'; import { PendingExecutor } from 'src/utils/pending.executor'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { TokenService } from './token.service'; +import { computeValueUSD } from 'src/utils/token.converters'; @Injectable() export class TokenComputeService implements ITokenComputeService { @@ -39,6 +41,8 @@ export class TokenComputeService implements ITokenComputeService { >; constructor( + @Inject(forwardRef(() => TokenService)) + private readonly tokenService: TokenService, private readonly pairAbi: PairAbiService, @Inject(forwardRef(() => PairComputeService)) private readonly pairCompute: PairComputeService, @@ -466,17 +470,33 @@ export class TokenComputeService implements ITokenComputeService { } async computeTokenLiquidityUSD(tokenID: string): Promise { - const values24h = await this.analyticsQuery.getLatestCompleteValues({ - series: tokenID, - metric: 'lockedValueUSD', - time: '1 day', - }); - - if (!values24h || values24h.length === 0) { - return undefined; + const pairs = await this.routerAbi.pairsMetadata(); + const priceUSD = await this.tokenPriceDerivedUSD(tokenID); + const tokenMetadata = await this.tokenService.tokenMetadata(tokenID); + const promises = []; + for (const pair of pairs) { + switch (tokenID) { + case pair.firstTokenID: + promises.push(this.pairAbi.firstTokenReserve(pair.address)); + break; + case pair.secondTokenID: + promises.push( + this.pairAbi.secondTokenReserve(pair.address), + ); + break; + } } + const allLockedValues = await Promise.all(promises); + let newLockedValue = new BigNumber(0); + allLockedValues.forEach((value) => { + newLockedValue = newLockedValue.plus(value); + }); - return values24h[values24h.length - 1]?.value ?? undefined; + return computeValueUSD( + newLockedValue.toFixed(), + tokenMetadata.decimals, + priceUSD, + ).toFixed(); } @ErrorLoggerAsync({ From 01a739acca02e8d3b9b82e605462cd1bbf29e97f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 8 Aug 2024 18:14:36 +0300 Subject: [PATCH 231/313] MEX-480: Update gas limits Signed-off-by: Claudiu Lataretu --- src/config/default.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index f3186177e..ef65576a5 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -507,9 +507,9 @@ "stakingPosition": 65000000 }, "dualTokens": { - "farmPosition": 35000000, - "farmPositionProxy": 50000000, - "dualFarmPosition": 67000000, + "farmPosition": 50000000, + "farmPositionProxy": 60000000, + "dualFarmPosition": 70000000, "exitFarm": 50000000 }, "energyPosition": 13000000 From 6096aaebdb1da2d3df8f55a80b57825f5eb95d3c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 9 Aug 2024 13:38:16 +0300 Subject: [PATCH 232/313] MEX-480: Update gas limits Signed-off-by: Claudiu Lataretu --- src/config/default.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index ef65576a5..af1a5da3b 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -216,8 +216,8 @@ }, "v2": { "enterFarm": { - "default": 16500000, - "withTokenMerge": 18000000 + "default": 20000000, + "withTokenMerge": 25000000 }, "unlockedRewards": { "exitFarm": { @@ -413,7 +413,7 @@ }, "stakeProxy": { "stakeFarmTokens": { - "default": 27000000, + "default": 40000000, "withTokenMerge": 60000000 }, "claimDualYield": 55000000, From 2fe05814abe70da2407fc14efbeacda669c68738 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 9 Aug 2024 17:10:42 +0300 Subject: [PATCH 233/313] MEX-500: Fix create position single tokens with LP tokens Signed-off-by: Claudiu Lataretu --- .../position.creator.transaction.resolver.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/modules/position-creator/position.creator.transaction.resolver.ts b/src/modules/position-creator/position.creator.transaction.resolver.ts index c15de5175..adc82ff5d 100644 --- a/src/modules/position-creator/position.creator.transaction.resolver.ts +++ b/src/modules/position-creator/position.creator.transaction.resolver.ts @@ -21,6 +21,7 @@ import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; import { constantsConfig } from 'src/config'; import { StakingAbiService } from '../staking/services/staking.abi.service'; +import { PairAbiService } from '../pair/services/pair.abi.service'; @Resolver(() => LiquidityPositionSingleTokenModel) export class LiquidityPositionSingleTokenResolver { @@ -57,6 +58,7 @@ export class FarmPositionSingleTokenResolver { constructor( private readonly posCreatorTransaction: PositionCreatorTransactionService, private readonly farmAbi: FarmAbiServiceV2, + private readonly pairAbi: PairAbiService, ) {} @ResolveField(() => [TransactionModel]) @@ -73,10 +75,12 @@ export class FarmPositionSingleTokenResolver { @Args('lockEpochs', { nullable: true }) lockEpochs: number, ): Promise { const pairAddress = await this.farmAbi.pairContractAddress(farmAddress); + const lpTokenID = await this.pairAbi.lpTokenID(pairAddress); if ( + parent.payment.tokenIdentifier !== lpTokenID && pairAddress !== - parent.swaps[parent.swaps.length - 1].pairs[0].address + parent.swaps[parent.swaps.length - 1].pairs[0].address ) { throw new GraphQLError('Invalid farm address', { extensions: { @@ -110,6 +114,7 @@ export class DualFarmPositionSingleTokenResolver { constructor( private readonly posCreatorTransaction: PositionCreatorTransactionService, private readonly stakingProxyAbi: StakingProxyAbiService, + private readonly pairAbi: PairAbiService, ) {} @ResolveField(() => [TransactionModel]) @@ -127,9 +132,12 @@ export class DualFarmPositionSingleTokenResolver { const pairAddress = await this.stakingProxyAbi.pairAddress( dualFarmAddress, ); + const lpTokenID = await this.pairAbi.lpTokenID(pairAddress); + if ( + parent.payment.tokenIdentifier !== lpTokenID && pairAddress !== - parent.swaps[parent.swaps.length - 1].pairs[0].address + parent.swaps[parent.swaps.length - 1].pairs[0].address ) { throw new GraphQLError('Invalid farm address', { extensions: { From a6981960e7f32b91b7122077c893ca3997ad325d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 15:09:41 +0300 Subject: [PATCH 234/313] MEX-480: Update gas limits Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 0dc9c00fc..311fd0af9 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -504,7 +504,7 @@ "liquidityPosition": 57500000, "farmPosition": 87000000, "dualFarmPosition": 115000000, - "stakingPosition": 65000000 + "stakingPosition": 70000000 }, "dualTokens": { "farmPosition": 50000000, From 165ad3cc7e44607bed51238ed9f1d75a73d6ff42 Mon Sep 17 00:00:00 2001 From: hschiau Date: Mon, 12 Aug 2024 15:22:03 +0300 Subject: [PATCH 235/313] SERVICES-2538: add trending score config for battlenet --- src/config/shadowfork2.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/shadowfork2.json b/src/config/shadowfork2.json index 59a6b67b5..776e0441d 100644 --- a/src/config/shadowfork2.json +++ b/src/config/shadowfork2.json @@ -120,6 +120,13 @@ "USDC-c76f1f": "5000000", "WEGLD-bd4d79": "100000000000000000", "MEX-455c57": "1000000000000000000000000" + }, + "trendingScore": { + "MIN_24H_VOLUME": 10000, + "MIN_24H_TRADE_COUNT": 100, + "PRICE_WEIGHT": 0.5, + "VOLUME_WEIGHT": 0.5, + "TRADES_COUNT_WEIGHT": 0 } } } From 7ef25228d3622ee4a5242a7d8854751c6fda382b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 15:28:43 +0300 Subject: [PATCH 236/313] MEX-451: Fix migrate dual farm positions Signed-off-by: Claudiu Lataretu --- src/modules/staking-proxy/staking.proxy.resolver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/staking-proxy/staking.proxy.resolver.ts b/src/modules/staking-proxy/staking.proxy.resolver.ts index 5e8b8a9f6..9eabce956 100644 --- a/src/modules/staking-proxy/staking.proxy.resolver.ts +++ b/src/modules/staking-proxy/staking.proxy.resolver.ts @@ -179,8 +179,7 @@ export class StakingProxyResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => TransactionModel, { - nullable: true, + @Query(() => [TransactionModel], { description: 'Update staking / farm positions for total farm position from dual yield token', }) From e2ab07a1e4bd0fb3f7044d0173f61b83151759af Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 15:54:05 +0300 Subject: [PATCH 237/313] MEX-502: Fix compute staking total value locked in USD Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.compute.service.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index f37e2f377..7eb495562 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -8,7 +8,7 @@ import { StakingService } from './staking.service'; import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; -import { denominateAmount } from 'src/utils/token.converters'; +import { computeValueUSD, denominateAmount } from 'src/utils/token.converters'; import { OptimalCompoundModel } from '../models/staking.model'; import { TokenService } from 'src/modules/tokens/services/token.service'; import { TokenComputeService } from 'src/modules/tokens/services/token.compute.service'; @@ -176,17 +176,17 @@ export class StakingComputeService { async computeStakedValueUSD(stakeAddress: string): Promise { const [farmTokenSupply, farmingToken] = await Promise.all([ this.stakingAbi.farmTokenSupply(stakeAddress), - this.tokenService.tokenMetadata(constantsConfig.MEX_TOKEN_ID), this.stakingService.getFarmingToken(stakeAddress), ]); const farmingTokenPrice = await this.tokenCompute.tokenPriceDerivedUSD( farmingToken.identifier, ); - return new BigNumber(farmTokenSupply) - .multipliedBy(farmingTokenPrice) - .multipliedBy(`1e-${farmingToken.decimals}`) - .toFixed(); + return computeValueUSD( + farmTokenSupply, + farmingToken.decimals, + farmingTokenPrice, + ).toFixed(); } @ErrorLoggerAsync({ From d1c6c2ba028cfc71f9b465ad56375091b07fec2c Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 16:11:42 +0300 Subject: [PATCH 238/313] MEX-502: Staking APR should be 0 when no rewards are distributed Signed-off-by: Claudiu Lataretu --- src/modules/pair/pair.resolver.ts | 2 +- .../pair/services/pair.compute.service.ts | 2 +- .../services/staking.compute.service.ts | 84 ++++++++++--------- src/modules/staking/staking.resolver.ts | 2 +- .../crons/staking.cache.warmer.service.ts | 2 +- 5 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 57d2d9e91..8d9a5fa24 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -161,7 +161,7 @@ export class PairCompoundedAPRResolver extends GenericResolver { return '0'; } - return await this.stakingCompute.boostedApr(stakingAddress); + return await this.stakingCompute.boostedAPR(stakingAddress); } } diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 936fe95fb..534bca2c8 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -830,7 +830,7 @@ export class PairComputeService implements IPairComputeService { if (stakingFarmAddress) { const [dualFarmBaseAPR, dualFarmBoostedAPR] = await Promise.all([ this.stakingCompute.stakeFarmAPR(stakingFarmAddress), - this.stakingCompute.boostedApr(stakingFarmAddress), + this.stakingCompute.boostedAPR(stakingFarmAddress), ]); dualFarmBaseAprBN = new BigNumber(dualFarmBaseAPR); diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 7eb495562..9eb76eb7a 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -202,6 +202,20 @@ export class StakingComputeService { } async computeStakeFarmAPR(stakeAddress: string): Promise { + const [accumulatedRewards, rewardsCapacity, produceRewardsEnabled] = + await Promise.all([ + this.stakingAbi.accumulatedRewards(stakeAddress), + this.stakingAbi.rewardCapacity(stakeAddress), + this.stakingAbi.produceRewardsEnabled(stakeAddress), + ]); + + if ( + !produceRewardsEnabled || + new BigNumber(accumulatedRewards).isEqualTo(rewardsCapacity) + ) { + return '0'; + } + const [ annualPercentageRewards, perBlockRewardAmount, @@ -234,24 +248,45 @@ export class StakingComputeService { @ErrorLoggerAsync({ logArgs: true, }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) async stakeFarmBaseAPR(stakeAddress: string): Promise { return await this.computeStakeFarmBaseAPR(stakeAddress); } async computeStakeFarmBaseAPR(stakeAddress: string): Promise { - const [apr, rawBoostedApr] = await Promise.all([ + const [apr, boostedApr] = await Promise.all([ this.stakeFarmAPR(stakeAddress), - this.boostedApr(stakeAddress), + this.boostedAPR(stakeAddress), ]); - const baseApr = new BigNumber(apr).minus(rawBoostedApr); + const baseAPR = new BigNumber(apr).minus(boostedApr); - return baseApr.toFixed(); + return baseAPR.toFixed(); + } + + @ErrorLoggerAsync({ + logArgs: true, + }) + async boostedAPR(stakeAddress: string): Promise { + return await this.computeBoostedAPR(stakeAddress); + } + + async computeBoostedAPR(stakeAddress: string): Promise { + const [boostedYieldsRewardsPercentage, apr] = await Promise.all([ + this.stakingAbi.boostedYieldsRewardsPercenatage(stakeAddress), + this.stakeFarmAPR(stakeAddress), + ]); + + const bnBoostedRewardsPercentage = new BigNumber( + boostedYieldsRewardsPercentage, + ) + .dividedBy(constantsConfig.MAX_PERCENT) + .multipliedBy(100); + + const boostedAPR = new BigNumber(apr).multipliedBy( + bnBoostedRewardsPercentage.dividedBy(100), + ); + + return boostedAPR.toFixed(); } async computeRewardsRemainingDays(stakeAddress: string): Promise { @@ -778,35 +813,4 @@ export class StakingComputeService { ); return deployedAt ?? undefined; } - - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async boostedApr(stakeAddress: string): Promise { - return await this.computeBoostedApr(stakeAddress); - } - - async computeBoostedApr(stakeAddress: string): Promise { - const [boostedYieldsRewardsPercentage, apr] = await Promise.all([ - this.stakingAbi.boostedYieldsRewardsPercenatage(stakeAddress), - this.stakeFarmAPR(stakeAddress), - ]); - - const bnBoostedRewardsPercentage = new BigNumber( - boostedYieldsRewardsPercentage, - ) - .dividedBy(constantsConfig.MAX_PERCENT) - .multipliedBy(100); - - const rawBoostedApr = new BigNumber(apr).multipliedBy( - bnBoostedRewardsPercentage.dividedBy(100), - ); - - return rawBoostedApr.toFixed(); - } } diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 8dcf81a03..a0c07619a 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -102,7 +102,7 @@ export class StakingResolver { @ResolveField() async boostedApr(@Parent() parent: StakingModel) { - return this.stakingCompute.boostedApr(parent.address); + return this.stakingCompute.boostedAPR(parent.address); } @ResolveField() diff --git a/src/services/crons/staking.cache.warmer.service.ts b/src/services/crons/staking.cache.warmer.service.ts index 114b1afce..ebe5a3250 100644 --- a/src/services/crons/staking.cache.warmer.service.ts +++ b/src/services/crons/staking.cache.warmer.service.ts @@ -65,7 +65,7 @@ export class StakingCacheWarmerService { this.stakingAbi.getStateRaw(address), this.stakeCompute.computeStakeFarmAPR(address), this.stakeCompute.computeStakeFarmBaseAPR(address), - this.stakeCompute.computeBoostedApr(address), + this.stakeCompute.computeBoostedAPR(address), ]); const cacheKeys = await Promise.all([ From d1c90af7b5be06e79e895cef9348c2ef21180c6d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 16:13:29 +0300 Subject: [PATCH 239/313] MEX-502: remove unnecessary caching on staking information Signed-off-by: Claudiu Lataretu --- .../services/staking.setter.service.ts | 24 ------------------- .../crons/staking.cache.warmer.service.ts | 9 ------- 2 files changed, 33 deletions(-) diff --git a/src/modules/staking/services/staking.setter.service.ts b/src/modules/staking/services/staking.setter.service.ts index d92deaada..407ccdd51 100644 --- a/src/modules/staking/services/staking.setter.service.ts +++ b/src/modules/staking/services/staking.setter.service.ts @@ -213,28 +213,4 @@ export class StakingSetterService extends GenericSetterService { CacheTtlInfo.ContractBalance.localTtl, ); } - - async setStakeFarmBaseAPR( - stakeAddress: string, - value: string, - ): Promise { - return await this.setData( - this.getCacheKey('stakeFarmBaseAPR', stakeAddress), - value, - CacheTtlInfo.ContractState.remoteTtl, - CacheTtlInfo.ContractState.localTtl, - ); - } - - async setStakeFarmBoostedAPR( - stakeAddress: string, - value: string, - ): Promise { - return await this.setData( - this.getCacheKey('boostedApr', stakeAddress), - value, - CacheTtlInfo.ContractState.remoteTtl, - CacheTtlInfo.ContractState.localTtl, - ); - } } diff --git a/src/services/crons/staking.cache.warmer.service.ts b/src/services/crons/staking.cache.warmer.service.ts index ebe5a3250..0a74e227a 100644 --- a/src/services/crons/staking.cache.warmer.service.ts +++ b/src/services/crons/staking.cache.warmer.service.ts @@ -56,16 +56,12 @@ export class StakingCacheWarmerService { divisionSafetyConstant, state, apr, - baseApr, - boostedApr, ] = await Promise.all([ this.stakingAbi.getAnnualPercentageRewardsRaw(address), this.stakingAbi.getMinUnbondEpochsRaw(address), this.stakingAbi.getDivisionSafetyConstantRaw(address), this.stakingAbi.getStateRaw(address), this.stakeCompute.computeStakeFarmAPR(address), - this.stakeCompute.computeStakeFarmBaseAPR(address), - this.stakeCompute.computeBoostedAPR(address), ]); const cacheKeys = await Promise.all([ @@ -83,11 +79,6 @@ export class StakingCacheWarmerService { ), this.stakeSetterService.setState(address, state), this.stakeSetterService.setStakeFarmAPR(address, apr), - this.stakeSetterService.setStakeFarmBaseAPR(address, baseApr), - this.stakeSetterService.setStakeFarmBoostedAPR( - address, - boostedApr, - ), ]); await this.deleteCacheKeys(cacheKeys); From 730ca17db454835619cc350fd8abe7ff88132eb0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 17:28:58 +0300 Subject: [PATCH 240/313] MEX-503: Add user energy model based on energy model Signed-off-by: Claudiu Lataretu --- src/modules/energy/energy.module.ts | 3 +- src/modules/energy/energy.resolver.ts | 34 +++++++++++++------ src/modules/energy/models/energy.model.ts | 11 ++++++ src/modules/energy/services/energy.service.ts | 8 ++--- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/modules/energy/energy.module.ts b/src/modules/energy/energy.module.ts index ab8a67ca4..83ef33569 100644 --- a/src/modules/energy/energy.module.ts +++ b/src/modules/energy/energy.module.ts @@ -3,7 +3,7 @@ import { CommonAppModule } from 'src/common.app.module'; import { ContextModule } from 'src/services/context/context.module'; import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; import { TokenModule } from '../tokens/token.module'; -import { EnergyResolver } from './energy.resolver'; +import { EnergyResolver, UserEnergyResolver } from './energy.resolver'; import { EnergyAbiService } from './services/energy.abi.service'; import { EnergyComputeService } from './services/energy.compute.service'; import { EnergyService } from './services/energy.service'; @@ -26,6 +26,7 @@ import { EnergyUpdateResolver } from './energy.update.resolver'; EnergyTransactionService, EnergyResolver, EnergyUpdateResolver, + UserEnergyResolver, ], exports: [ EnergyAbiService, diff --git a/src/modules/energy/energy.resolver.ts b/src/modules/energy/energy.resolver.ts index 3835fc0ea..8eb4bd2da 100644 --- a/src/modules/energy/energy.resolver.ts +++ b/src/modules/energy/energy.resolver.ts @@ -1,5 +1,12 @@ import { UseGuards } from '@nestjs/common'; -import { Args, Int, Query, ResolveField, Resolver } from '@nestjs/graphql'; +import { + Args, + Int, + Parent, + Query, + ResolveField, + Resolver, +} from '@nestjs/graphql'; import { scAddress } from 'src/config'; import { AuthUser } from '../auth/auth.user'; import { UserAuthResult } from '../auth/user.auth.result'; @@ -8,7 +15,7 @@ import { TransactionModel } from 'src/models/transaction.model'; import { JwtOrNativeAuthGuard } from '../auth/jwt.or.native.auth.guard'; import { EsdtToken } from '../tokens/models/esdtToken.model'; import { NftCollection } from '../tokens/models/nftCollection.model'; -import { EnergyModel, UnlockType } from './models/energy.model'; +import { UnlockType, UserEnergyModel } from './models/energy.model'; import { LockOption, SimpleLockEnergyModel, @@ -21,6 +28,20 @@ import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; +@Resolver(() => UserEnergyModel) +export class UserEnergyResolver { + constructor(private readonly energyService: EnergyService) {} + + @UseGuards(JwtOrNativeAuthGuard) + @Query(() => UserEnergyModel) + async userEnergy( + @AuthUser() user: UserAuthResult, + @Args('vmQuery', { nullable: true }) vmQuery: boolean, + ): Promise { + return this.energyService.getUserEnergy(user.address, vmQuery); + } +} + @Resolver(() => SimpleLockEnergyModel) export class EnergyResolver { constructor( @@ -66,15 +87,6 @@ export class EnergyResolver { }); } - @UseGuards(JwtOrNativeAuthGuard) - @Query(() => EnergyModel) - async userEnergy( - @AuthUser() user: UserAuthResult, - @Args('vmQuery', { nullable: true }) vmQuery: boolean, - ): Promise { - return this.energyService.getUserEnergy(user.address, vmQuery); - } - @Query(() => String) async penaltyAmount( @Args('inputToken') inputToken: InputTokenModel, diff --git a/src/modules/energy/models/energy.model.ts b/src/modules/energy/models/energy.model.ts index 938a511c2..45e21ec35 100644 --- a/src/modules/energy/models/energy.model.ts +++ b/src/modules/energy/models/energy.model.ts @@ -24,3 +24,14 @@ export class EnergyModel { Object.assign(this, init); } } + +@ObjectType() +export class UserEnergyModel extends EnergyModel { + @Field(() => String) + league: string; + + constructor(init?: Partial) { + super(init); + Object.assign(this, init); + } +} diff --git a/src/modules/energy/services/energy.service.ts b/src/modules/energy/services/energy.service.ts index 5ca894aa9..16e473f31 100644 --- a/src/modules/energy/services/energy.service.ts +++ b/src/modules/energy/services/energy.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; import { InputTokenModel } from 'src/models/inputToken.model'; import { ContextGetterService } from 'src/services/context/context.getter.service'; -import { EnergyModel } from '../models/energy.model'; +import { UserEnergyModel } from '../models/energy.model'; import { EnergyAbiService } from './energy.abi.service'; import { EnergyComputeService } from './energy.compute.service'; import { constantsConfig } from '../../../config'; @@ -38,11 +38,11 @@ export class EnergyService { async getUserEnergy( userAddress: string, vmQuery = false, - ): Promise { + ): Promise { if (vmQuery) { const userEnergyEntry = await this.energyAbi.getEnergyEntryForUserRaw(userAddress); - return new EnergyModel(userEnergyEntry); + return new UserEnergyModel(userEnergyEntry); } const [userEnergyEntry, currentEpoch] = await Promise.all([ this.energyAbi.energyEntryForUser(userAddress), @@ -54,7 +54,7 @@ export class EnergyService { currentEpoch, ); - return new EnergyModel(depletedEnergy); + return new UserEnergyModel(depletedEnergy); } async getPenaltyAmount( From cf1e759879aeff2b5052f333a9c2c066cec94cfe Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 17:30:09 +0300 Subject: [PATCH 241/313] MEX-503: add user energy league - added leagues configuration - added compute for league - added resolver for user energy league field Signed-off-by: Claudiu Lataretu --- src/config/default.json | 31 ++++++++++++++++++- src/config/index.ts | 2 ++ src/modules/energy/energy.resolver.ts | 10 +++++- .../energy/services/energy.compute.service.ts | 20 +++++++++++- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 311fd0af9..e40627df3 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -629,5 +629,34 @@ }, "dataApi": { "tableName": "XEXCHANGE_ANALYTICS" - } + }, + "leagues": [ + { + "name": "Bronze", + "minEnergy": "720000000", + "maxEnergy": "7200000000" + }, + { + "name": "Silver", + "minEnergy": "7200000000", + "maxEnergy": "72000000000" + + }, + { + "name": "Gold", + "minEnergy": "72000000000", + "maxEnergy": "720000000000" + + }, + { + "name": "Platinum", + "minEnergy": "720000000000", + "maxEnergy": "7200000000000" + + }, + { + "name": "Diamond", + "minEnergy": "7200000000000" + } + ] } diff --git a/src/config/index.ts b/src/config/index.ts index 514acb926..909163d8b 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -32,3 +32,5 @@ export const cachedTokensPriceConfig = config.get('cachedTokensPrice'); export const constantsConfig = config.get('constants'); export const dataApiConfig = config.get('dataApi'); + +export const leaguesConfig = config.get('leagues'); diff --git a/src/modules/energy/energy.resolver.ts b/src/modules/energy/energy.resolver.ts index 8eb4bd2da..163181216 100644 --- a/src/modules/energy/energy.resolver.ts +++ b/src/modules/energy/energy.resolver.ts @@ -30,7 +30,15 @@ import { ApolloServerErrorCode } from '@apollo/server/errors'; @Resolver(() => UserEnergyModel) export class UserEnergyResolver { - constructor(private readonly energyService: EnergyService) {} + constructor( + private readonly energyService: EnergyService, + private readonly energyCompute: EnergyComputeService, + ) {} + + @ResolveField() + async league(@Parent() parent: UserEnergyModel): Promise { + return this.energyCompute.computeLeagueByEnergy(parent.amount); + } @UseGuards(JwtOrNativeAuthGuard) @Query(() => UserEnergyModel) diff --git a/src/modules/energy/services/energy.compute.service.ts b/src/modules/energy/services/energy.compute.service.ts index 64f2e8023..96bce9545 100644 --- a/src/modules/energy/services/energy.compute.service.ts +++ b/src/modules/energy/services/energy.compute.service.ts @@ -1,10 +1,11 @@ import { EnergyType } from '@multiversx/sdk-exchange'; import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; -import { constantsConfig } from 'src/config'; +import { constantsConfig, leaguesConfig } from 'src/config'; import { LockOption } from '../models/simple.lock.energy.model'; import { IEnergyComputeService } from './interfaces'; import { EnergyAbiService } from './energy.abi.service'; +import { denominateAmount } from 'src/utils/token.converters'; @Injectable() export class EnergyComputeService implements IEnergyComputeService { @@ -57,6 +58,23 @@ export class EnergyComputeService implements IEnergyComputeService { .dividedBy(constantsConfig.MAX_PENALTY_PERCENT); } + public computeLeagueByEnergy(energyRaw: string): string { + const energy = denominateAmount(energyRaw, 18); + + for (const league of leaguesConfig) { + const min = league.minEnergy ?? new BigNumber(0); + const max = league.maxEnergy ?? new BigNumber(Infinity); + + const inInterval = + energy.isGreaterThanOrEqualTo(min) && energy.isLessThan(max); + if (inInterval) { + return league.name; + } + } + + return ''; + } + private async computePenaltyPercentageFullUnlock( lockEpochsRemaining: number, ): Promise { From 4e4676188898fde356b93278dcf70504a09463d0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 12 Aug 2024 18:27:57 +0300 Subject: [PATCH 242/313] MEX-504: remove unnecessary caching on user weekly rewards Signed-off-by: Claudiu Lataretu --- .../v2/services/farm.v2.compute.service.ts | 39 +---------------- src/modules/farm/v2/services/interfaces.ts | 11 ----- .../fees-collector.compute.service.ts | 42 +------------------ .../services/staking.compute.service.ts | 38 +---------------- .../user/user.info-by-week.resolver.ts | 12 +++--- ...eekly-rewards-splitting.compute.service.ts | 4 +- 6 files changed, 10 insertions(+), 136 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index e8bf394e9..cbdffa1f3 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -135,33 +135,12 @@ export class FarmComputeServiceV2 return totalFarmRewards; } - @ErrorLoggerAsync({ - logArgs: true, - }) - async userRewardsDistributionForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise { - return await this.cachingService.getOrSet( - `farm.userRewardsDistributionForWeek.${scAddress}.${userAddress}.${week}`, - () => - this.computeUserRewardsDistributionForWeek( - scAddress, - userAddress, - week, - ), - CacheTtlInfo.ContractBalance.remoteTtl, - CacheTtlInfo.ContractBalance.localTtl, - ); - } - async computeUserRewardsDistributionForWeek( scAddress: string, userAddress: string, week: number, ): Promise { - const userRewardsForWeek = await this.userRewardsForWeek( + const userRewardsForWeek = await this.computeUserRewardsForWeek( scAddress, userAddress, week, @@ -276,22 +255,6 @@ export class FarmComputeServiceV2 return paymentAmount.integerValue().toFixed(); } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'farm', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) - async userRewardsForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise { - return this.computeUserRewardsForWeek(scAddress, userAddress, week); - } - async computeUserRewardsForWeek( scAddress: string, userAddress: string, diff --git a/src/modules/farm/v2/services/interfaces.ts b/src/modules/farm/v2/services/interfaces.ts index d7c253e63..69a9f7fe7 100644 --- a/src/modules/farm/v2/services/interfaces.ts +++ b/src/modules/farm/v2/services/interfaces.ts @@ -4,7 +4,6 @@ import { IFarmComputeService, } from '../../base-module/services/interfaces'; import { BoostedYieldsFactors } from '../../models/farm.v2.model'; -import { EsdtTokenPayment } from '../../../../models/esdtTokenPayment.model'; export interface IFarmAbiServiceV2 extends IFarmAbiService { boostedYieldsRewardsPercenatage(farmAddress: string): Promise; @@ -30,20 +29,10 @@ export interface IFarmAbiServiceV2 extends IFarmAbiService { export interface IFarmComputeServiceV2 extends IFarmComputeService { farmBaseAPR(farmAddress: string): Promise; - userRewardsDistributionForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise; userAccumulatedRewards( scAddress: string, userAddress: string, week: number, ): Promise; - userRewardsForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise; optimalEnergyPerLP(scAddress: string, week: number): Promise; } diff --git a/src/modules/fees-collector/services/fees-collector.compute.service.ts b/src/modules/fees-collector/services/fees-collector.compute.service.ts index 12c55425e..30930190a 100644 --- a/src/modules/fees-collector/services/fees-collector.compute.service.ts +++ b/src/modules/fees-collector/services/fees-collector.compute.service.ts @@ -32,26 +32,6 @@ export class FeesCollectorComputeService { private readonly tokenCompute: TokenComputeService, ) {} - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'feesCollector', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) - async userRewardsForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise { - return await this.computeUserRewardsForWeek( - scAddress, - userAddress, - week, - ); - } - async computeUserRewardsForWeek( scAddress: string, userAddress: string, @@ -102,32 +82,12 @@ export class FeesCollectorComputeService { return payments; } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'feesCollector', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, - }) - async userRewardsDistributionForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise { - return await this.computeUserRewardsDistributionForWeek( - scAddress, - userAddress, - week, - ); - } - async computeUserRewardsDistributionForWeek( scAddress: string, userAddress: string, week: number, ): Promise { - const userRewardsForWeek = await this.userRewardsForWeek( + const userRewardsForWeek = await this.computeUserRewardsForWeek( scAddress, userAddress, week, diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index f37e2f377..28bce5426 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -413,32 +413,12 @@ export class StakingComputeService { ); } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) - async userRewardsDistributionForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise { - return await this.computeUserRewardsDistributionForWeek( - scAddress, - userAddress, - week, - ); - } - async computeUserRewardsDistributionForWeek( scAddress: string, userAddress: string, week: number, ): Promise { - const userRewardsForWeek = await this.userRewardsForWeek( + const userRewardsForWeek = await this.computeUserRewardsForWeek( scAddress, userAddress, week, @@ -553,22 +533,6 @@ export class StakingComputeService { return paymentAmount.integerValue().toFixed(); } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'stake', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) - async userRewardsForWeek( - scAddress: string, - userAddress: string, - week: number, - ): Promise { - return this.computeUserRewardsForWeek(scAddress, userAddress, week); - } - async computeUserRewardsForWeek( scAddress: string, userAddress: string, diff --git a/src/modules/user/user.info-by-week.resolver.ts b/src/modules/user/user.info-by-week.resolver.ts index d219a92c9..4e578adb8 100644 --- a/src/modules/user/user.info-by-week.resolver.ts +++ b/src/modules/user/user.info-by-week.resolver.ts @@ -49,7 +49,7 @@ export class UserInfoByWeekResolver { @Parent() parent: UserInfoByWeekModel, ): Promise { if (parent.scAddress === scAddress.feesCollector) { - return this.feesCollectorCompute.userRewardsForWeek( + return this.feesCollectorCompute.computeUserRewardsForWeek( parent.scAddress, parent.userAddress, parent.week, @@ -58,14 +58,14 @@ export class UserInfoByWeekResolver { const stakingAddresses = await this.remoteConfig.getStakingAddresses(); if (stakingAddresses.includes(parent.scAddress)) { - return this.stakingCompute.userRewardsForWeek( + return this.stakingCompute.computeUserRewardsForWeek( parent.scAddress, parent.userAddress, parent.week, ); } - return this.farmComputeV2.userRewardsForWeek( + return this.farmComputeV2.computeUserRewardsForWeek( parent.scAddress, parent.userAddress, parent.week, @@ -77,7 +77,7 @@ export class UserInfoByWeekResolver { @Parent() parent: UserInfoByWeekModel, ): Promise { if (parent.scAddress === scAddress.feesCollector) { - return this.feesCollectorCompute.userRewardsDistributionForWeek( + return this.feesCollectorCompute.computeUserRewardsDistributionForWeek( parent.scAddress, parent.userAddress, parent.week, @@ -85,14 +85,14 @@ export class UserInfoByWeekResolver { } const stakingAddresses = await this.remoteConfig.getStakingAddresses(); if (stakingAddresses.includes(parent.scAddress)) { - return this.stakingCompute.userRewardsDistributionForWeek( + return this.stakingCompute.computeUserRewardsDistributionForWeek( parent.scAddress, parent.userAddress, parent.week, ); } - return this.farmComputeV2.userRewardsDistributionForWeek( + return this.farmComputeV2.computeUserRewardsDistributionForWeek( parent.scAddress, parent.userAddress, parent.week, diff --git a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts index 818c76601..b25d5a29c 100644 --- a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts +++ b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service.ts @@ -35,9 +35,7 @@ export class WeeklyRewardsSplittingComputeService const tokenDistributions = await Promise.all( payments.map(async (token) => { const tokenPriceUSD = - await this.tokenCompute.computeTokenPriceDerivedUSD( - token.tokenID, - ); + await this.tokenCompute.tokenPriceDerivedUSD(token.tokenID); const rewardsPriceUSD = new BigNumber( tokenPriceUSD, ).multipliedBy(new BigNumber(token.amount)); From e9557735b7f089dcc51d6571003e35c94040c8de Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 13 Aug 2024 11:02:36 +0300 Subject: [PATCH 243/313] MEX-503: fix energy resolver imports Signed-off-by: Claudiu Lataretu --- src/modules/energy/energy.resolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/energy/energy.resolver.ts b/src/modules/energy/energy.resolver.ts index 163181216..0e4a5f9fe 100644 --- a/src/modules/energy/energy.resolver.ts +++ b/src/modules/energy/energy.resolver.ts @@ -27,6 +27,7 @@ import { EnergyAbiService } from './services/energy.abi.service'; import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; +import { EnergyComputeService } from './services/energy.compute.service'; @Resolver(() => UserEnergyModel) export class UserEnergyResolver { From fd8e5bb5034f8b6cb3cad92593437f711c5744cc Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 13 Aug 2024 11:09:47 +0300 Subject: [PATCH 244/313] MEX-503: fix fees collector compute unit tests Signed-off-by: Claudiu Lataretu --- .../specs/fees.collector.compute.service.spec.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/modules/fees-collector/specs/fees.collector.compute.service.spec.ts b/src/modules/fees-collector/specs/fees.collector.compute.service.spec.ts index 0b666bc2b..2b99424e0 100644 --- a/src/modules/fees-collector/specs/fees.collector.compute.service.spec.ts +++ b/src/modules/fees-collector/specs/fees.collector.compute.service.spec.ts @@ -22,7 +22,10 @@ import { PairService } from 'src/modules/pair/services/pair.service'; import { WrapAbiServiceProvider } from 'src/modules/wrapping/mocks/wrap.abi.service.mock'; import { TokenServiceProvider } from 'src/modules/tokens/mocks/token.service.mock'; import { RouterAbiServiceProvider } from 'src/modules/router/mocks/router.abi.service.mock'; -import { EnergyModel } from 'src/modules/energy/models/energy.model'; +import { + EnergyModel, + UserEnergyModel, +} from 'src/modules/energy/models/energy.model'; import BigNumber from 'bignumber.js'; import { EnergyService } from 'src/modules/energy/services/energy.service'; import { EnergyComputeService } from 'src/modules/energy/services/energy.compute.service'; @@ -228,11 +231,12 @@ describe('FeesCollectorComputeService', () => { const totalLockedTokensForWeek = '1000000000000000000000000'; const user1EnergyAmount = new BigNumber(totalEnergyForWeek); - const user1Energy = new EnergyModel({ + const user1Energy = new UserEnergyModel({ amount: user1EnergyAmount.toFixed(), totalLockedTokens: new BigNumber( totalLockedTokensForWeek, ).toFixed(), + league: 'Bronze', }); const service = module.get( @@ -285,11 +289,12 @@ describe('FeesCollectorComputeService', () => { const totalLockedTokensForWeek = '1000000000000000000000000'; const user1EnergyAmount = new BigNumber(totalEnergyForWeek); - const user1Energy = new EnergyModel({ + const user1Energy = new UserEnergyModel({ amount: user1EnergyAmount.dividedBy(4).toFixed(), totalLockedTokens: new BigNumber( totalLockedTokensForWeek, ).toFixed(), + league: 'Bronze', }); const service = module.get( @@ -345,11 +350,12 @@ describe('FeesCollectorComputeService', () => { const totalLockedTokensForWeek = '1000000000000000000000000'; const user1EnergyAmount = new BigNumber(totalEnergyForWeek); - const user1Energy = new EnergyModel({ + const user1Energy = new UserEnergyModel({ amount: user1EnergyAmount.dividedBy(4).toFixed(), totalLockedTokens: new BigNumber(totalLockedTokensForWeek) .dividedBy(4) .toFixed(), + league: 'Bronze', }); const service = module.get( From 4628e64a3b4b8a057ecff78fb64800a523913f8f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 13 Aug 2024 11:20:50 +0300 Subject: [PATCH 245/313] MEX-503: return all leagues with a new query Signed-off-by: Claudiu Lataretu --- src/modules/energy/energy.resolver.ts | 22 ++++++++++++++++++++-- src/modules/energy/models/energy.model.ts | 16 +++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/modules/energy/energy.resolver.ts b/src/modules/energy/energy.resolver.ts index 0e4a5f9fe..7604ec7c1 100644 --- a/src/modules/energy/energy.resolver.ts +++ b/src/modules/energy/energy.resolver.ts @@ -7,7 +7,7 @@ import { ResolveField, Resolver, } from '@nestjs/graphql'; -import { scAddress } from 'src/config'; +import { leaguesConfig, scAddress } from 'src/config'; import { AuthUser } from '../auth/auth.user'; import { UserAuthResult } from '../auth/user.auth.result'; import { InputTokenModel } from 'src/models/inputToken.model'; @@ -15,7 +15,11 @@ import { TransactionModel } from 'src/models/transaction.model'; import { JwtOrNativeAuthGuard } from '../auth/jwt.or.native.auth.guard'; import { EsdtToken } from '../tokens/models/esdtToken.model'; import { NftCollection } from '../tokens/models/nftCollection.model'; -import { UnlockType, UserEnergyModel } from './models/energy.model'; +import { + LeagueModel, + UnlockType, + UserEnergyModel, +} from './models/energy.model'; import { LockOption, SimpleLockEnergyModel, @@ -28,6 +32,7 @@ import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; import { EnergyComputeService } from './services/energy.compute.service'; +import BigNumber from 'bignumber.js'; @Resolver(() => UserEnergyModel) export class UserEnergyResolver { @@ -268,4 +273,17 @@ export class EnergyResolver { async userEnergyAmount(@AuthUser() user: UserAuthResult): Promise { return this.energyAbi.energyAmountForUser(user.address); } + + @Query(() => [LeagueModel]) + async leagues(): Promise { + return leaguesConfig.map( + (league) => + new LeagueModel({ + name: league.name, + minEnergy: league.minEnergy ?? '0', + maxEnergy: + league.maxEnergy ?? new BigNumber(Infinity).toFixed(), + }), + ); + } } diff --git a/src/modules/energy/models/energy.model.ts b/src/modules/energy/models/energy.model.ts index 45e21ec35..8f2291e7d 100644 --- a/src/modules/energy/models/energy.model.ts +++ b/src/modules/energy/models/energy.model.ts @@ -30,8 +30,22 @@ export class UserEnergyModel extends EnergyModel { @Field(() => String) league: string; - constructor(init?: Partial) { + constructor(init?: Partial) { super(init); Object.assign(this, init); } } + +@ObjectType() +export class LeagueModel { + @Field() + name: string; + @Field() + minEnergy: string; + @Field() + maxEnergy: string; + + constructor(init?: Partial) { + Object.assign(this, init); + } +} From 26ae28ae81e66b8517250d037a6bd05a6ff81eba Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 13 Aug 2024 14:36:15 +0300 Subject: [PATCH 246/313] MEX-505: Fix compute token trending score Signed-off-by: Claudiu Lataretu --- .../tokens/services/token.compute.service.ts | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index 41c7f5c60..c36b36d70 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -232,7 +232,7 @@ export class TokenComputeService implements ITokenComputeService { metric: 'priceUSD', }); - return values24h[0]?.value ?? undefined; + return values24h.find((item) => item.value !== '0')?.value ?? undefined; } @ErrorLoggerAsync({ @@ -278,11 +278,11 @@ export class TokenComputeService implements ITokenComputeService { const currentPriceBN = new BigNumber(currentPrice); const previous24hPriceBN = new BigNumber(previous24hPrice); - if (previous24hPriceBN.isZero()) { + if (previous24hPriceBN.isZero() || previous24hPrice === undefined) { return 0; } - return currentPriceBN.dividedBy(previous24hPrice).toNumber(); + return currentPriceBN.dividedBy(previous24hPriceBN).toNumber(); } async computeTokenPriceChange7d(tokenID: string): Promise { @@ -355,10 +355,6 @@ export class TokenComputeService implements ITokenComputeService { const currentSwapsBN = new BigNumber(currentSwaps); const previous24hSwapsBN = new BigNumber(previous24hSwaps); - if (currentSwapsBN.isZero()) { - return 0; - } - const maxPrevious24hTradeCount = BigNumber.maximum( previous24hSwapsBN, constantsConfig.trendingScore.MIN_24H_TRADE_COUNT, @@ -654,20 +650,20 @@ export class TokenComputeService implements ITokenComputeService { this.tokenTradeChange24h(tokenID), ]); + const minScore = -(10 ** 9); + const volumeScore = new BigNumber( constantsConfig.trendingScore.VOLUME_WEIGHT, - ).multipliedBy(Math.log(volumeChange)); + ).multipliedBy(volumeChange > 0 ? Math.log(volumeChange) : minScore); const priceScore = new BigNumber( constantsConfig.trendingScore.PRICE_WEIGHT, - ).multipliedBy(priceChange); + ).multipliedBy(priceChange > 0 ? Math.log(priceChange) : minScore); const tradeScore = new BigNumber( constantsConfig.trendingScore.TRADES_COUNT_WEIGHT, - ).multipliedBy(Math.log(tradeChange)); + ).multipliedBy(tradeChange > 0 ? Math.log(tradeChange) : minScore); - if (volumeScore.isNaN() || priceScore.isNaN() || tradeScore.isNaN()) { - return new BigNumber('-Infinity').toFixed(); - } + const trendingScore = volumeScore.plus(priceScore).plus(tradeScore); - return volumeScore.plus(priceScore).plus(tradeScore).toFixed(); + return trendingScore.toFixed(); } } From 5750377e7a9c2f0d148da719de737d3bf306ea0f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 13 Aug 2024 14:37:00 +0300 Subject: [PATCH 247/313] MEX-505: increase gas limit for staking position creator Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index e40627df3..ec29be4ec 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -504,7 +504,7 @@ "liquidityPosition": 57500000, "farmPosition": 87000000, "dualFarmPosition": 115000000, - "stakingPosition": 70000000 + "stakingPosition": 80000000 }, "dualTokens": { "farmPosition": 50000000, From 07fb4242cf187ca023e72192e75be000e3672cfb Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 13 Aug 2024 14:49:05 +0300 Subject: [PATCH 248/313] MEX-505: revert changes on token previous 24h price Signed-off-by: Claudiu Lataretu --- src/modules/tokens/services/token.compute.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index c36b36d70..321ddfd72 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -232,7 +232,7 @@ export class TokenComputeService implements ITokenComputeService { metric: 'priceUSD', }); - return values24h.find((item) => item.value !== '0')?.value ?? undefined; + return values24h[0]?.value ?? undefined; } @ErrorLoggerAsync({ From f0cdc7fa60c8d0abdca601446edb8df9a508d2f2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 13:57:50 +0300 Subject: [PATCH 249/313] MEX-507: add pair address for user positions Signed-off-by: Claudiu Lataretu --- src/modules/user/models/user.model.ts | 8 ++++++++ src/modules/user/services/metaEsdt.compute.service.ts | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/modules/user/models/user.model.ts b/src/modules/user/models/user.model.ts index 5c80c9417..9a5dddf33 100644 --- a/src/modules/user/models/user.model.ts +++ b/src/modules/user/models/user.model.ts @@ -59,6 +59,7 @@ export class UserLockedAssetToken extends LockedAssetToken { @ObjectType() export class UserFarmToken extends FarmToken { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -69,6 +70,7 @@ export class UserFarmToken extends FarmToken { @ObjectType() export class UserLockedLPToken extends LockedLpToken { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -79,6 +81,7 @@ export class UserLockedLPToken extends LockedLpToken { @ObjectType() export class UserLockedFarmToken extends LockedFarmToken { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -89,6 +92,7 @@ export class UserLockedFarmToken extends LockedFarmToken { @ObjectType() export class UserLockedLPTokenV2 extends LockedLpTokenV2 { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -99,6 +103,7 @@ export class UserLockedLPTokenV2 extends LockedLpTokenV2 { @ObjectType() export class UserLockedFarmTokenV2 extends LockedFarmTokenV2 { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -129,6 +134,7 @@ export class UserUnbondFarmToken extends UnbondFarmToken { @ObjectType() export class UserDualYiledToken extends DualYieldToken { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -157,6 +163,7 @@ export class UserLockedEsdtToken extends LockedEsdtToken { @ObjectType() export class UserLockedSimpleLpToken extends LockedSimpleLpToken { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); @@ -167,6 +174,7 @@ export class UserLockedSimpleLpToken extends LockedSimpleLpToken { @ObjectType() export class UserLockedSimpleFarmToken extends LockedSimpleFarmToken { @Field() valueUSD: string; + @Field() pairAddress: string; constructor(init?: Partial) { super(init); diff --git a/src/modules/user/services/metaEsdt.compute.service.ts b/src/modules/user/services/metaEsdt.compute.service.ts index f97ec277e..9f660a889 100644 --- a/src/modules/user/services/metaEsdt.compute.service.ts +++ b/src/modules/user/services/metaEsdt.compute.service.ts @@ -62,6 +62,7 @@ import { StakingProxyAbiService } from 'src/modules/staking-proxy/services/staki import { FarmAbiFactory } from 'src/modules/farm/farm.abi.factory'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { TokenComputeService } from 'src/modules/tokens/services/token.compute.service'; +import { Address } from '@multiversx/sdk-core/out'; @Injectable() export class UserMetaEsdtComputeService { @@ -159,6 +160,7 @@ export class UserMetaEsdtComputeService { tokenPriceUSD, ).toFixed(), decodedAttributes: decodedFarmAttributes, + pairAddress: Address.Zero().bech32(), }); } const farmTokenBalanceUSD = @@ -170,6 +172,7 @@ export class UserMetaEsdtComputeService { ...nftToken, valueUSD: farmTokenBalanceUSD, decodedAttributes: decodedFarmAttributes, + pairAddress, }); } @@ -265,6 +268,7 @@ export class UserMetaEsdtComputeService { ...nftToken, valueUSD: valueUSD, decodedAttributes: decodedWLPTAttributes, + pairAddress, }); } } @@ -289,6 +293,7 @@ export class UserMetaEsdtComputeService { ...nftToken, valueUSD: valueUSD, decodedAttributes: decodedWLPTAttributes, + pairAddress, }); } } @@ -325,6 +330,7 @@ export class UserMetaEsdtComputeService { return new UserLockedFarmToken({ ...nftToken, valueUSD: userFarmToken.valueUSD, + pairAddress: userFarmToken.pairAddress, decodedAttributes: decodedWFMTAttributes, }); } @@ -373,6 +379,7 @@ export class UserMetaEsdtComputeService { return new UserLockedFarmTokenV2({ ...nftToken, valueUSD: userFarmToken.valueUSD, + pairAddress: userFarmToken.pairAddress, decodedAttributes: decodedWFMTAttributes, }); } catch (e) { @@ -503,6 +510,7 @@ export class UserMetaEsdtComputeService { return new UserDualYiledToken({ ...nftToken, valueUSD: farmTokenUSD.valueUSD, + pairAddress: farmTokenUSD.pairAddress, decodedAttributes: decodedAttributes[0], }); } @@ -616,6 +624,7 @@ export class UserMetaEsdtComputeService { ...nftToken, decodedAttributes, valueUSD: userEsdtToken.valueUSD, + pairAddress, }); } @@ -658,6 +667,7 @@ export class UserMetaEsdtComputeService { ...nftToken, decodedAttributes, valueUSD: userFarmToken.valueUSD, + pairAddress: userFarmToken.pairAddress, }); } From fc300f877cf718d452ad84d1c827c8111686f0b1 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 14:25:23 +0300 Subject: [PATCH 250/313] MEX-507: use filtering by an array of addresses on filtered queries Signed-off-by: Claudiu Lataretu --- .../pair/services/pair.filtering.service.ts | 6 ++--- src/modules/router/models/filter.args.ts | 8 +++--- src/modules/router/services/router.service.ts | 6 ++--- .../models/staking.proxy.args.model.ts | 16 ++++++------ .../staking.proxy.filtering.service.ts | 25 +++++++++---------- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/modules/pair/services/pair.filtering.service.ts b/src/modules/pair/services/pair.filtering.service.ts index 31b0ddd30..6212b4adb 100644 --- a/src/modules/pair/services/pair.filtering.service.ts +++ b/src/modules/pair/services/pair.filtering.service.ts @@ -50,9 +50,9 @@ export class PairFilteringService { pairFilter: PairFilterArgs | PairsFilter, pairsMetadata: PairMetadata[], ): Promise { - if (pairFilter.address) { - pairsMetadata = pairsMetadata.filter( - (pair) => pairFilter.address === pair.address, + if (pairFilter.addresses) { + pairsMetadata = pairsMetadata.filter((pair) => + pairFilter.addresses.includes(pair.address), ); } return await Promise.resolve(pairsMetadata); diff --git a/src/modules/router/models/filter.args.ts b/src/modules/router/models/filter.args.ts index 71f1a8cd9..37363d5ed 100644 --- a/src/modules/router/models/filter.args.ts +++ b/src/modules/router/models/filter.args.ts @@ -14,8 +14,8 @@ registerEnumType(PairSortableFields, { name: 'PairSortableFields' }); @ArgsType() export class PairFilterArgs { - @Field({ nullable: true }) - address: string; + @Field(() => [String], { nullable: true }) + addresses: string[]; @Field({ nullable: true }) firstTokenID: string; @Field({ nullable: true }) @@ -34,8 +34,8 @@ export class PairFilterArgs { @InputType() export class PairsFilter { - @Field({ nullable: true }) - address: string; + @Field(() => [String], { nullable: true }) + addresses: string[]; @Field({ nullable: true }) firstTokenID: string; @Field({ nullable: true }) diff --git a/src/modules/router/services/router.service.ts b/src/modules/router/services/router.service.ts index 50b1d2e35..bbc9611d1 100644 --- a/src/modules/router/services/router.service.ts +++ b/src/modules/router/services/router.service.ts @@ -122,9 +122,9 @@ export class RouterService { pairFilter: PairFilterArgs, pairsMetadata: PairMetadata[], ): PairMetadata[] { - if (pairFilter.address) { - pairsMetadata = pairsMetadata.filter( - (pair) => pairFilter.address === pair.address, + if (pairFilter.addresses) { + pairsMetadata = pairsMetadata.filter((pair) => + pairFilter.addresses.includes(pair.address), ); } return pairsMetadata; diff --git a/src/modules/staking-proxy/models/staking.proxy.args.model.ts b/src/modules/staking-proxy/models/staking.proxy.args.model.ts index 1da105666..2a1cc66ad 100644 --- a/src/modules/staking-proxy/models/staking.proxy.args.model.ts +++ b/src/modules/staking-proxy/models/staking.proxy.args.model.ts @@ -31,14 +31,14 @@ export class UnstakeFarmTokensArgs { @InputType() export class StakingProxiesFilter { - @Field({ nullable: true }) - address: string; - @Field({ nullable: true }) - pairAddress: string; - @Field({ nullable: true }) - stakingFarmAddress: string; - @Field({ nullable: true }) - lpFarmAddress: string; + @Field(() => [String], { nullable: true }) + addresses: string[]; + @Field(() => [String], { nullable: true }) + pairAddresses: string[]; + @Field(() => [String], { nullable: true }) + stakingFarmAddresses: string[]; + @Field(() => [String], { nullable: true }) + lpFarmAddresses: string[]; @Field(() => String, { nullable: true }) searchToken?: string; } diff --git a/src/modules/staking-proxy/services/staking.proxy.filtering.service.ts b/src/modules/staking-proxy/services/staking.proxy.filtering.service.ts index 9336143d2..6b2a9e9e1 100644 --- a/src/modules/staking-proxy/services/staking.proxy.filtering.service.ts +++ b/src/modules/staking-proxy/services/staking.proxy.filtering.service.ts @@ -17,9 +17,9 @@ export class StakingProxyFilteringService { filter: StakingProxiesFilter, stakingProxyAddresses: string[], ): string[] { - if (filter.address) { - stakingProxyAddresses = stakingProxyAddresses.filter( - (address) => filter.address === address, + if (filter.addresses) { + stakingProxyAddresses = stakingProxyAddresses.filter((address) => + filter.addresses.includes(address), ); } return stakingProxyAddresses; @@ -29,7 +29,7 @@ export class StakingProxyFilteringService { filter: StakingProxiesFilter, stakingProxyAddresses: string[], ): Promise { - if (!filter.pairAddress) { + if (!filter.pairAddresses) { return stakingProxyAddresses; } @@ -39,8 +39,8 @@ export class StakingProxyFilteringService { ), ); - return stakingProxyAddresses.filter( - (_, index) => pairAddresses[index] === filter.pairAddress, + return stakingProxyAddresses.filter((_, index) => + filter.pairAddresses.includes(pairAddresses[index]), ); } @@ -48,7 +48,7 @@ export class StakingProxyFilteringService { filter: StakingProxiesFilter, stakingProxyAddresses: string[], ): Promise { - if (!filter.stakingFarmAddress) { + if (!filter.stakingFarmAddresses) { return stakingProxyAddresses; } @@ -58,9 +58,8 @@ export class StakingProxyFilteringService { ), ); - return stakingProxyAddresses.filter( - (_, index) => - stakingFarmAddresses[index] === filter.stakingFarmAddress, + return stakingProxyAddresses.filter((_, index) => + filter.stakingFarmAddresses.includes(stakingFarmAddresses[index]), ); } @@ -68,7 +67,7 @@ export class StakingProxyFilteringService { filter: StakingProxiesFilter, stakingProxyAddresses: string[], ): Promise { - if (!filter.lpFarmAddress) { + if (!filter.lpFarmAddresses) { return stakingProxyAddresses; } @@ -78,8 +77,8 @@ export class StakingProxyFilteringService { ), ); - return stakingProxyAddresses.filter( - (_, index) => lpFarmAddresses[index] === filter.lpFarmAddress, + return stakingProxyAddresses.filter((_, index) => + filter.lpFarmAddresses.includes(lpFarmAddresses[index]), ); } From 509357c5828d0c220b46a015b54d8ec312ec8ac9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 14:26:27 +0300 Subject: [PATCH 251/313] MEX-507: add pairAddress information for user tokens positions Signed-off-by: Claudiu Lataretu --- src/modules/user/models/user.model.ts | 1 + src/modules/user/services/esdt.compute.service.ts | 1 + src/modules/user/services/metaEsdt.compute.service.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/modules/user/models/user.model.ts b/src/modules/user/models/user.model.ts index 9a5dddf33..b1510c807 100644 --- a/src/modules/user/models/user.model.ts +++ b/src/modules/user/models/user.model.ts @@ -29,6 +29,7 @@ export enum ContractType { @ObjectType() export class UserToken extends EsdtToken { @Field() valueUSD: string; + @Field({ nullable: true }) pairAddress: string; constructor(init?: Partial) { super(init); diff --git a/src/modules/user/services/esdt.compute.service.ts b/src/modules/user/services/esdt.compute.service.ts index b9c9a9dda..e1884dc0d 100644 --- a/src/modules/user/services/esdt.compute.service.ts +++ b/src/modules/user/services/esdt.compute.service.ts @@ -37,6 +37,7 @@ export class UserEsdtComputeService { return new UserToken({ ...esdtToken, valueUSD: valueUSD, + pairAddress, }); } } diff --git a/src/modules/user/services/metaEsdt.compute.service.ts b/src/modules/user/services/metaEsdt.compute.service.ts index 9f660a889..3aaa25199 100644 --- a/src/modules/user/services/metaEsdt.compute.service.ts +++ b/src/modules/user/services/metaEsdt.compute.service.ts @@ -114,6 +114,7 @@ export class UserMetaEsdtComputeService { return new UserToken({ ...esdtToken, valueUSD: valueUSD, + pairAddress, }); } From f0d2bd92690244a0e355cd3297aadce5ee601f0e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 14:31:19 +0300 Subject: [PATCH 252/313] MEX-507: fix unit tests Signed-off-by: Claudiu Lataretu --- src/modules/router/specs/router.service.spec.ts | 6 +++--- src/modules/user/specs/user.service.spec.ts | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/modules/router/specs/router.service.spec.ts b/src/modules/router/specs/router.service.spec.ts index 34d7e5a23..cda12a96b 100644 --- a/src/modules/router/specs/router.service.spec.ts +++ b/src/modules/router/specs/router.service.spec.ts @@ -109,7 +109,7 @@ describe('RouterService', () => { const filteredPairs = await service.getAllPairs(0, Number.MAX_VALUE, { firstTokenID: 'WEGLD-123456', issuedLpToken: true, - address: null, + addresses: null, secondTokenID: null, state: null, feeState: null, @@ -146,7 +146,7 @@ describe('RouterService', () => { const filteredPairs = await service.getAllPairs(0, Number.MAX_VALUE, { firstTokenID: 'WEGLD-123456', issuedLpToken: true, - address: null, + addresses: null, secondTokenID: null, state: null, feeState: false, @@ -168,7 +168,7 @@ describe('RouterService', () => { const filteredPairs = await service.getAllPairs(0, Number.MAX_VALUE, { firstTokenID: null, issuedLpToken: true, - address: null, + addresses: null, secondTokenID: null, state: null, feeState: null, diff --git a/src/modules/user/specs/user.service.spec.ts b/src/modules/user/specs/user.service.spec.ts index 7f6c1e659..cfb931a9b 100644 --- a/src/modules/user/specs/user.service.spec.ts +++ b/src/modules/user/specs/user.service.spec.ts @@ -233,6 +233,9 @@ describe('UserService', () => { nonce: 1, royalties: 0, valueUSD: '20', + pairAddress: Address.fromHex( + '0000000000000000000000000000000000000000000000000000000000000012', + ).bech32(), decodedAttributes: new FarmTokenAttributesModelV1_2({ aprMultiplier: 1, attributes: 'AAAABQeMCWDbAAAAAAAAAF8CAQ==', From 75d5ea9849b3aa6483949e17c3b745e4cd31718a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 15:15:38 +0300 Subject: [PATCH 253/313] MEX-504: remove caching for user claim progress Signed-off-by: Claudiu Lataretu --- .../services/weekly-rewards-splitting.abi.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts index 0c87267ba..b518dafdc 100644 --- a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts +++ b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts @@ -85,11 +85,6 @@ export class WeeklyRewardsSplittingAbiService @ErrorLoggerAsync({ logArgs: true, }) - @GetOrSetCache({ - baseKey: 'weeklyRewards', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) async userEnergyForWeek( scAddress: string, user: string, From fb93c6126c0666d8a697b2f1e4a943e3e53dcfb0 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 16:31:32 +0300 Subject: [PATCH 254/313] MEX-508: return 24 data points on analytics for the last 24 hours Signed-off-by: Claudiu Lataretu --- .../analytics/services/analytics.aws.getter.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/analytics/services/analytics.aws.getter.service.ts b/src/modules/analytics/services/analytics.aws.getter.service.ts index 407b1fc2f..6145ba51e 100644 --- a/src/modules/analytics/services/analytics.aws.getter.service.ts +++ b/src/modules/analytics/services/analytics.aws.getter.service.ts @@ -95,7 +95,7 @@ export class AnalyticsAWSGetterService { metric, ); const data = await this.getCachedData(cacheKey); - return data !== undefined ? data : []; + return data !== undefined ? data.slice(1) : []; } @ErrorLoggerAsync() @@ -105,7 +105,7 @@ export class AnalyticsAWSGetterService { ): Promise { const cacheKey = this.getAnalyticsCacheKey('values24h', series, metric); const data = await this.getCachedData(cacheKey); - return data !== undefined ? data : []; + return data !== undefined ? data.slice(1) : []; } private getAnalyticsCacheKey(...args: any) { From 6024d8cb2ef7f1ddf4d41a76cbdc17d3959c2d78 Mon Sep 17 00:00:00 2001 From: Lucas Willems Date: Mon, 19 Aug 2024 19:39:09 +0200 Subject: [PATCH 255/313] Fix leagues --- src/config/default.json | 18 +++++++++--------- .../energy/services/energy.compute.service.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index ec29be4ec..a862696ec 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -633,30 +633,30 @@ "leagues": [ { "name": "Bronze", - "minEnergy": "720000000", - "maxEnergy": "7200000000" + "minEnergy": "720000000000000000000000000", + "maxEnergy": "7200000000000000000000000000" }, { "name": "Silver", - "minEnergy": "7200000000", - "maxEnergy": "72000000000" + "minEnergy": "7200000000000000000000000000", + "maxEnergy": "72000000000000000000000000000" }, { "name": "Gold", - "minEnergy": "72000000000", - "maxEnergy": "720000000000" + "minEnergy": "72000000000000000000000000000", + "maxEnergy": "720000000000000000000000000000" }, { "name": "Platinum", - "minEnergy": "720000000000", - "maxEnergy": "7200000000000" + "minEnergy": "720000000000000000000000000000", + "maxEnergy": "7200000000000000000000000000000" }, { "name": "Diamond", - "minEnergy": "7200000000000" + "minEnergy": "7200000000000000000000000000000" } ] } diff --git a/src/modules/energy/services/energy.compute.service.ts b/src/modules/energy/services/energy.compute.service.ts index 96bce9545..5e8cf8c0e 100644 --- a/src/modules/energy/services/energy.compute.service.ts +++ b/src/modules/energy/services/energy.compute.service.ts @@ -59,7 +59,7 @@ export class EnergyComputeService implements IEnergyComputeService { } public computeLeagueByEnergy(energyRaw: string): string { - const energy = denominateAmount(energyRaw, 18); + const energy = new BigNumber(energyRaw); for (const league of leaguesConfig) { const min = league.minEnergy ?? new BigNumber(0); From 42ec83dfd6c725ef15d36ab0ee04bf9ab636742b Mon Sep 17 00:00:00 2001 From: Lucas Willems Date: Mon, 19 Aug 2024 19:39:53 +0200 Subject: [PATCH 256/313] Fix setup --- .env.example | 27 +++++++++++++++++---------- README.md | 11 +++++++++++ docker-compose.yml | 11 +++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index e6ec5ce23..ee395dd7c 100644 --- a/.env.example +++ b/.env.example @@ -18,18 +18,25 @@ REDIS_PORT=6379 REDIS_DB=0 #MongoDB -MONGODB_URL= -MONGODB_DATABASE= -MONGODB_USERNAME= -MONGODB_PASSWORD= +MONGODB_URL="mongodb://localhost:27017" +MONGODB_DATABASE="development" +MONGODB_USERNAME="admin" +MONGODB_PASSWORD="admin" + +#TimescaleDB +TIMESCALEDB_URL="localhost" +TIMESCALEDB_PORT=5432 +TIMESCALEDB_DATABASE="timescaledb" +TIMESCALEDB_USERNAME="timescaledb" +TIMESCALEDB_PASSWORD="password" #Elasticsearch server to use for logging. Optional -ELASTICSEARCH_URL="https://testnet-index.multiversx.com" +ELASTICSEARCH_URL="https://devnet-index.multiversx.com" #MultiversX resources -MX_API_URL="https://testnet-api.multiversx.com" -MX_GATEWAY_URL="https://testnet-gateway.multiversx.com" -MX_DEX_URL="https://devnet-graphql-next.maiar.exchange" +MX_API_URL="https://devnet-api.multiversx.com" +MX_GATEWAY_URL="https://devnet-gateway.multiversx.com" +MX_DEX_URL="http://localhost:3005" MX_DATA_API_URL="https://data-api.multiversx.com" #Enable or Disable modules @@ -48,8 +55,8 @@ LOG_LEVEL=info # JWT Authorization JWT_SECRET= NATIVE_AUTH_MAX_EXPIRY_SECONDS=86400 -NATIVE_AUTH_ACCEPTED_ORIGINS= -IMPERSONATE_URL= +NATIVE_AUTH_ACCEPTED_ORIGINS=https://localhost:3000,https://devnet.xexchange.com +IMPERSONATE_URL=“https://devnet-extras-api.multiversx.com/impersonate/allowed” # RabbitMQ RABBITMQ_URL= diff --git a/README.md b/README.md index 1fb6714f8..4b1e93ae8 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,17 @@ $ npm run start:dev $ npm run start ``` +4. Disable SSL for TimescaleDB in `timescaledb.module.ts`: + +``` +// ssl: true, +// extra: { +// ssl: { +// rejectUnauthorized: false, +// }, +// }, +``` + It depends on the following external systems: - gateway: diff --git a/docker-compose.yml b/docker-compose.yml index 27d9c1e91..c4273acbf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,3 +30,14 @@ services: - MONGO_INITDB_ROOT_PASSWORD=admin ports: - '27017:27017' + timescaledb: + image: timescale/timescaledb:latest-pg12 + restart: always + shm_size: 1gb + ports: + - '5432:5432' + environment: + POSTGRES_USER: timescaledb + POSTGRES_PASSWORD: password + volumes: + - /var/lib/postgresql/data From 96743e26f8a7f1bc10fab9063cd753c3a24c8069 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 12:01:34 +0300 Subject: [PATCH 257/313] MEX-504: remove caching for user last active week Signed-off-by: Claudiu Lataretu --- .../services/weekly-rewards-splitting.abi.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts index b518dafdc..be075522f 100644 --- a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts +++ b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts @@ -152,11 +152,6 @@ export class WeeklyRewardsSplittingAbiService @ErrorLoggerAsync({ logArgs: true, }) - @GetOrSetCache({ - baseKey: 'weeklyRewards', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) async lastActiveWeekForUser( scAddress: string, user: string, From 5ed63112ab4bf2e87d3dbcf5b78ed508ee94f3c4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 12:20:55 +0300 Subject: [PATCH 258/313] MEX-504: remove caching for user energy information Signed-off-by: Claudiu Lataretu --- src/modules/energy/services/energy.abi.service.ts | 9 --------- .../services/weekly-rewards-splitting.abi.service.ts | 5 ----- 2 files changed, 14 deletions(-) diff --git a/src/modules/energy/services/energy.abi.service.ts b/src/modules/energy/services/energy.abi.service.ts index fe8a1dfd6..69695059d 100644 --- a/src/modules/energy/services/energy.abi.service.ts +++ b/src/modules/energy/services/energy.abi.service.ts @@ -149,10 +149,6 @@ export class EnergyAbiService } @ErrorLoggerAsync() - @GetOrSetCache({ - baseKey: 'energy', - remoteTtl: Constants.oneMinute(), - }) async energyEntryForUser(userAddress: string): Promise { return await this.getEnergyEntryForUserRaw(userAddress); } @@ -170,11 +166,6 @@ export class EnergyAbiService } @ErrorLoggerAsync() - @GetOrSetCache({ - baseKey: 'energy', - remoteTtl: Constants.oneSecond(), - localTtl: Constants.oneSecond(), - }) async energyAmountForUser(userAddress: string): Promise { return await this.getEnergyAmountForUserRaw(userAddress); } diff --git a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts index be075522f..043c3d608 100644 --- a/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts +++ b/src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service.ts @@ -38,11 +38,6 @@ export class WeeklyRewardsSplittingAbiService @ErrorLoggerAsync({ logArgs: true, }) - @GetOrSetCache({ - baseKey: 'weeklyRewards', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) async currentClaimProgress( scAddress: string, user: string, From 9ba86f4dfcb24143eb57cd4241d22ee566850d7b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 12:24:46 +0300 Subject: [PATCH 259/313] MEX-504: remove caching for user outdated contracts Signed-off-by: Claudiu Lataretu --- .../user/services/userEnergy/user.energy.compute.service.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/user/services/userEnergy/user.energy.compute.service.ts b/src/modules/user/services/userEnergy/user.energy.compute.service.ts index d12f02124..3e101cf52 100644 --- a/src/modules/user/services/userEnergy/user.energy.compute.service.ts +++ b/src/modules/user/services/userEnergy/user.energy.compute.service.ts @@ -91,10 +91,6 @@ export class UserEnergyComputeService { ); } - @GetOrSetCache({ - baseKey: 'userEnergy', - remoteTtl: Constants.oneMinute() * 10, - }) async outdatedContract( userAddress: string, contractAddress: string, From 11f144f4ce0c1d67aebc6d58c40b24e19ff8442b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 18:04:44 +0300 Subject: [PATCH 260/313] MEX-470: update contracts addresses on battlenet config Signed-off-by: Claudiu Lataretu --- src/config/shadowfork2.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/shadowfork2.json b/src/config/shadowfork2.json index 776e0441d..a029755b2 100644 --- a/src/config/shadowfork2.json +++ b/src/config/shadowfork2.json @@ -34,9 +34,9 @@ "tokenUnstake": "erd1qqqqqqqqqqqqqpgqxv0y4p6vvszrknaztatycac77yvsxqrrkp2sghd86c", "lockedTokenWrapper": "erd1qqqqqqqqqqqqqpgq97fctvqlskzu0str05muew2qyjttp8kfkp2sl9k0gx", "escrow": "erd1qqqqqqqqqqqqqpgqeh4yv09rmyg4xgn5ma03mvm4v5gndu8w2jpsglz3cn", - "positionCreator": "erd1qqqqqqqqqqqqqpgqucxekem48mrlzcenugl7z6k38l2ead702jpsrlfg4r", - "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgq43cyf9yjfy79ey0cax55kenxgpefhayk2jps6zt793", - "composableTasks": "erd1qqqqqqqqqqqqqpgqte9qae42w7x4karshkmfdrmnv56ka2zc2jpsqg0ner" + "positionCreator": "erd1qqqqqqqqqqqqqpgq43cyf9yjfy79ey0cax55kenxgpefhayk2jps6zt793", + "lockedTokenPositionCreator": "erd1qqqqqqqqqqqqqpgqwtyu7d5qw322smp0kjq789ua0wtz5cfa2jpsu999xr", + "composableTasks": "erd1qqqqqqqqqqqqqpgqsytkvnexypp7argk02l0rasnj57sxa542jpshkl7df" }, "governance": { "oldEnergy": { From 6aae1be6b6b1256bea71eba53c1b140e9bdfc713 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 28 Aug 2024 17:16:46 +0300 Subject: [PATCH 261/313] MEX-509: added abi and compute loaders for farms Signed-off-by: Claudiu Lataretu --- package-lock.json | 11 ++ package.json | 1 + src/modules/farm/base-module/farm.resolver.ts | 59 +++++--- .../base-module/services/farm.abi.loader.ts | 136 ++++++++++++++++++ .../base-module/services/farm.abi.service.ts | 24 ++++ .../base-module/services/farm.base.service.ts | 27 ++++ .../services/farm.compute.loader.ts | 50 +++++++ .../services/farm.compute.service.ts | 24 ++++ src/modules/farm/custom/farm.custom.module.ts | 4 + .../farm/custom/farm.custom.resolver.ts | 17 ++- .../custom/services/farm.custom.abi.loader.ts | 16 +++ .../services/farm.custom.compute.loader.ts | 12 ++ .../services/farm.custom.compute.service.ts | 3 + src/modules/farm/v1.2/farm.v1.2.module.ts | 4 + src/modules/farm/v1.2/farm.v1.2.resolver.ts | 17 ++- .../v1.2/services/farm.v1.2.abi.loader.ts | 16 +++ .../v1.2/services/farm.v1.2.abi.service.ts | 4 +- .../v1.2/services/farm.v1.2.compute.loader.ts | 12 ++ .../services/farm.v1.2.compute.service.ts | 3 + src/modules/farm/v1.3/farm.v1.3.module.ts | 4 + src/modules/farm/v1.3/farm.v1.3.resolver.ts | 17 ++- .../v1.3/services/farm.v1.3.abi.loader.ts | 16 +++ .../v1.3/services/farm.v1.3.abi.service.ts | 4 +- .../v1.3/services/farm.v1.3.compute.loader.ts | 12 ++ .../services/farm.v1.3.compute.service.ts | 3 + src/modules/farm/v2/farm.v2.module.ts | 4 + src/modules/farm/v2/farm.v2.resolver.ts | 18 ++- .../farm/v2/services/farm.v2.abi.loader.ts | 16 +++ .../farm/v2/services/farm.v2.abi.service.ts | 4 +- .../v2/services/farm.v2.compute.loader.ts | 12 ++ .../v2/services/farm.v2.compute.service.ts | 3 +- src/modules/tokens/services/token.service.ts | 82 +++++++---- src/services/caching/cache.ttl.info.ts | 2 +- 33 files changed, 582 insertions(+), 55 deletions(-) create mode 100644 src/modules/farm/base-module/services/farm.abi.loader.ts create mode 100644 src/modules/farm/base-module/services/farm.compute.loader.ts create mode 100644 src/modules/farm/custom/services/farm.custom.abi.loader.ts create mode 100644 src/modules/farm/custom/services/farm.custom.compute.loader.ts create mode 100644 src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts create mode 100644 src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts create mode 100644 src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts create mode 100644 src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts create mode 100644 src/modules/farm/v2/services/farm.v2.abi.loader.ts create mode 100644 src/modules/farm/v2/services/farm.v2.compute.loader.ts diff --git a/package-lock.json b/package-lock.json index 92751501c..4e3d08bfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "class-validator": "^0.14.0", "config": "^3.3.7", "cookie-parser": "^1.4.6", + "dataloader": "^2.2.2", "express": "^4.18.1", "graphql": "^16.5.0", "graphql-redis-subscriptions": "^2.4.2", @@ -7626,6 +7627,11 @@ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" }, + "node_modules/dataloader": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", + "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==" + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -22430,6 +22436,11 @@ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" }, + "dataloader": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", + "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==" + }, "date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", diff --git a/package.json b/package.json index 6438e1306..111481e5b 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "class-validator": "^0.14.0", "config": "^3.3.7", "cookie-parser": "^1.4.6", + "dataloader": "^2.2.2", "express": "^4.18.1", "graphql": "^16.5.0", "graphql-redis-subscriptions": "^2.4.2", diff --git a/src/modules/farm/base-module/farm.resolver.ts b/src/modules/farm/base-module/farm.resolver.ts index 25a75b12d..a1b78894f 100644 --- a/src/modules/farm/base-module/farm.resolver.ts +++ b/src/modules/farm/base-module/farm.resolver.ts @@ -1,13 +1,17 @@ import { Resolver, ResolveField, Parent } from '@nestjs/graphql'; import { BaseFarmModel } from '../models/farm.model'; import { PairModel } from '../../pair/models/pair.model'; -import { LockedAssetModel } from '../../locked-asset-factory/models/locked-asset.model'; import { EsdtToken } from '../../tokens/models/esdtToken.model'; import { NftCollection } from '../../tokens/models/nftCollection.model'; import { Address } from '@multiversx/sdk-core'; import { FarmAbiService } from './services/farm.abi.service'; import { FarmServiceBase } from './services/farm.base.service'; import { FarmComputeService } from './services/farm.compute.service'; +import { Inject } from '@nestjs/common'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { Logger } from 'winston'; +import { FarmAbiLoader } from './services/farm.abi.loader'; +import { FarmComputeLoader } from './services/farm.compute.loader'; @Resolver(() => BaseFarmModel) export class FarmResolver { @@ -15,103 +19,122 @@ export class FarmResolver { protected readonly farmAbi: FarmAbiService, protected readonly farmService: FarmServiceBase, protected readonly farmCompute: FarmComputeService, + protected readonly farmAbiLoader: FarmAbiLoader, + protected readonly farmComputeLoader: FarmComputeLoader, + @Inject(WINSTON_MODULE_PROVIDER) protected readonly logger: Logger, ) {} @ResolveField() async farmedToken(@Parent() parent: BaseFarmModel): Promise { - return this.farmService.getFarmedToken(parent.address); + return this.farmAbiLoader.farmedTokenLoader.load(parent.address); } @ResolveField() async farmToken(@Parent() parent: BaseFarmModel): Promise { - return this.farmService.getFarmToken(parent.address); + return this.farmAbiLoader.farmTokenLoader.load(parent.address); } @ResolveField() async farmingToken(@Parent() parent: BaseFarmModel): Promise { - return this.farmService.getFarmingToken(parent.address); + return this.farmAbiLoader.farmingTokenLoader.load(parent.address); } @ResolveField() async produceRewardsEnabled( @Parent() parent: BaseFarmModel, ): Promise { - return this.farmAbi.produceRewardsEnabled(parent.address); + return this.farmAbiLoader.produceRewardsEnabledLoader.load( + parent.address, + ); } @ResolveField() async perBlockRewards(@Parent() parent: BaseFarmModel): Promise { - return this.farmAbi.rewardsPerBlock(parent.address); + return this.farmAbiLoader.perBlockRewardsLoader.load(parent.address); } @ResolveField() async farmTokenSupply(@Parent() parent: BaseFarmModel): Promise { - return this.farmAbi.farmTokenSupply(parent.address); + return this.farmAbiLoader.farmTokenSupplyLoader.load(parent.address); } @ResolveField() async farmedTokenPriceUSD( @Parent() parent: BaseFarmModel, ): Promise { - return this.farmCompute.farmedTokenPriceUSD(parent.address); + return this.farmComputeLoader.farmedTokenPriceUSDLoader.load( + parent.address, + ); } @ResolveField() async farmTokenPriceUSD(@Parent() parent: BaseFarmModel): Promise { - return this.farmCompute.farmTokenPriceUSD(parent.address); + return this.farmComputeLoader.farmTokenPriceUSDLoader.load( + parent.address, + ); } @ResolveField() async farmingTokenPriceUSD( @Parent() parent: BaseFarmModel, ): Promise { - return this.farmCompute.farmingTokenPriceUSD(parent.address); + return this.farmComputeLoader.farmingTokenPriceUSDLoader.load( + parent.address, + ); } @ResolveField() async penaltyPercent(@Parent() parent: BaseFarmModel): Promise { - return this.farmAbi.penaltyPercent(parent.address); + return this.farmAbiLoader.penaltyPercentLoader.load(parent.address); } @ResolveField() async minimumFarmingEpochs( @Parent() parent: BaseFarmModel, ): Promise { - return this.farmAbi.minimumFarmingEpochs(parent.address); + return this.farmAbiLoader.minimumFarmingEpochsLoader.load( + parent.address, + ); } @ResolveField() async rewardPerShare(@Parent() parent: BaseFarmModel): Promise { - return this.farmAbi.rewardPerShare(parent.address); + return this.farmAbiLoader.rewardPerShareLoader.load(parent.address); } @ResolveField() async rewardReserve(@Parent() parent: BaseFarmModel): Promise { - return this.farmAbi.rewardReserve(parent.address); + return this.farmAbiLoader.rewardReserveLoader.load(parent.address); } @ResolveField() async lastRewardBlockNonce( @Parent() parent: BaseFarmModel, ): Promise { - return this.farmAbi.lastRewardBlockNonce(parent.address); + return this.farmAbiLoader.lastRewardBlockNonceLoader.load( + parent.address, + ); } @ResolveField() async divisionSafetyConstant( @Parent() parent: BaseFarmModel, ): Promise { - return this.farmAbi.divisionSafetyConstant(parent.address); + return this.farmAbiLoader.divisionSafetyConstantLoader.load( + parent.address, + ); } @ResolveField() async totalValueLockedUSD(parent: BaseFarmModel): Promise { - return this.farmCompute.farmLockedValueUSD(parent.address); + return this.farmComputeLoader.farmLockedValueUSDLoader.load( + parent.address, + ); } @ResolveField() async state(@Parent() parent: BaseFarmModel): Promise { - return this.farmAbi.state(parent.address); + return this.farmAbiLoader.stateLoader.load(parent.address); } @ResolveField() diff --git a/src/modules/farm/base-module/services/farm.abi.loader.ts b/src/modules/farm/base-module/services/farm.abi.loader.ts new file mode 100644 index 000000000..aee62355d --- /dev/null +++ b/src/modules/farm/base-module/services/farm.abi.loader.ts @@ -0,0 +1,136 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmServiceBase } from './farm.base.service'; +import DataLoader from 'dataloader'; +import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; +import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; +import { FarmAbiService } from './farm.abi.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmAbiLoader { + constructor( + protected readonly farmAbi: FarmAbiService, + protected readonly farmService: FarmServiceBase, + ) {} + + public readonly farmedTokenLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmService.getAllFarmedTokens(addresses); + }, + ); + + public readonly farmTokenLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmService.getAllFarmTokens(addresses); + }, + ); + + public readonly farmingTokenLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmService.getAllFarmingTokens(addresses); + }, + ); + + public readonly produceRewardsEnabledLoader = new DataLoader< + string, + boolean + >(async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.produceRewardsEnabled', + this.farmAbi.produceRewardsEnabled.bind(this.farmAbi), + ); + }); + + public readonly perBlockRewardsLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.perBlockRewards', + this.farmAbi.rewardsPerBlock.bind(this.farmAbi), + ); + }, + ); + + public readonly farmTokenSupplyLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.farmTokenSupply', + this.farmAbi.farmTokenSupply.bind(this.farmAbi), + ); + }, + ); + + public readonly penaltyPercentLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.penaltyPercent', + this.farmAbi.penaltyPercent.bind(this.farmAbi), + ); + }, + ); + + public readonly minimumFarmingEpochsLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.minimumFarmingEpochs', + this.farmAbi.minimumFarmingEpochs.bind(this.farmAbi), + ); + }, + ); + + public readonly rewardPerShareLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.rewardPerShare', + this.farmAbi.rewardPerShare.bind(this.farmAbi), + ); + }, + ); + + public readonly rewardReserveLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.rewardReserve', + this.farmAbi.rewardReserve.bind(this.farmAbi), + ); + }, + ); + + public readonly lastRewardBlockNonceLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.lastRewardBlockNonce', + this.farmAbi.lastRewardBlockNonce.bind(this.farmAbi), + ); + }, + ); + + public readonly divisionSafetyConstantLoader = new DataLoader< + string, + string + >(async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.divisionSafetyConstant', + this.farmAbi.divisionSafetyConstant.bind(this.farmAbi), + ); + }); + + public readonly stateLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmAbi.getAllKeys( + addresses, + 'farm.state', + this.farmAbi.state.bind(this.farmAbi), + ); + }, + ); +} diff --git a/src/modules/farm/base-module/services/farm.abi.service.ts b/src/modules/farm/base-module/services/farm.abi.service.ts index 9e87c3a6c..0116336ce 100644 --- a/src/modules/farm/base-module/services/farm.abi.service.ts +++ b/src/modules/farm/base-module/services/farm.abi.service.ts @@ -14,6 +14,7 @@ import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { Constants } from '@multiversx/sdk-nestjs-common'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; import { IFarmAbiService } from './interfaces'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; export class FarmAbiService extends GenericAbiService @@ -23,10 +24,33 @@ export class FarmAbiService protected readonly mxProxy: MXProxyService, protected readonly gatewayService: MXGatewayService, protected readonly apiService: MXApiService, + protected readonly cacheService: CacheService, ) { super(mxProxy); } + async getAllKeys( + farmAddresses: string[], + baseKey: string, + getterMethod: (address: string) => Promise, + ): Promise { + const keys = farmAddresses.map((address) => `${baseKey}.${address}`); + const values = await this.cacheService.getMany(keys); + + const missingIndexes: number[] = []; + values.forEach((value, index) => { + if (!value) { + missingIndexes.push(index); + } + }); + + for (const missingIndex of missingIndexes) { + const tokenID = await getterMethod(farmAddresses[missingIndex]); + values[missingIndex] = tokenID; + } + return values; + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/farm/base-module/services/farm.base.service.ts b/src/modules/farm/base-module/services/farm.base.service.ts index 12d695594..da8d8e451 100644 --- a/src/modules/farm/base-module/services/farm.base.service.ts +++ b/src/modules/farm/base-module/services/farm.base.service.ts @@ -32,16 +32,43 @@ export abstract class FarmServiceBase { return this.tokenService.tokenMetadata(farmedTokenID); } + async getAllFarmedTokens(farmAddresses: string[]): Promise { + const farmedTokenIDs = await this.farmAbi.getAllKeys( + farmAddresses, + 'farm.farmedTokenID', + this.farmAbi.farmedTokenID.bind(this.farmAbi), + ); + return this.tokenService.getAllTokensMetadata(farmedTokenIDs); + } + async getFarmToken(farmAddress: string): Promise { const farmTokenID = await this.farmAbi.farmTokenID(farmAddress); return this.tokenService.getNftCollectionMetadata(farmTokenID); } + async getAllFarmTokens(farmAddresses: string[]): Promise { + const farmTokenIDs = await this.farmAbi.getAllKeys( + farmAddresses, + 'farm.farmTokenID', + this.farmAbi.farmTokenID.bind(this.farmAbi), + ); + return this.tokenService.getAllNftsCollectionMetadata(farmTokenIDs); + } + async getFarmingToken(farmAddress: string): Promise { const farmingTokenID = await this.farmAbi.farmingTokenID(farmAddress); return this.tokenService.tokenMetadata(farmingTokenID); } + async getAllFarmingTokens(farmAddresses: string[]): Promise { + const farmingTokenIDs = await this.farmAbi.getAllKeys( + farmAddresses, + 'farm.farmingTokenID', + this.farmAbi.farmingTokenID.bind(this.farmAbi), + ); + return this.tokenService.getAllTokensMetadata(farmingTokenIDs); + } + protected async getRemainingFarmingEpochs( farmAddress: string, enteringEpoch: number, diff --git a/src/modules/farm/base-module/services/farm.compute.loader.ts b/src/modules/farm/base-module/services/farm.compute.loader.ts new file mode 100644 index 000000000..10de3eb9a --- /dev/null +++ b/src/modules/farm/base-module/services/farm.compute.loader.ts @@ -0,0 +1,50 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmComputeService } from './farm.compute.service'; +import DataLoader from 'dataloader'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmComputeLoader { + constructor(protected readonly farmCompute: FarmComputeService) {} + + public readonly farmLockedValueUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmCompute.getAllKeys( + addresses, + 'farm.farmLockedValueUSD', + this.farmCompute.farmLockedValueUSD.bind(this.farmCompute), + ); + }, + ); + + public readonly farmedTokenPriceUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmCompute.getAllKeys( + addresses, + 'farm.farmedTokenPriceUSD', + this.farmCompute.farmedTokenPriceUSD.bind(this.farmCompute), + ); + }, + ); + + public readonly farmTokenPriceUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmCompute.getAllKeys( + addresses, + 'farm.farmTokenPriceUSD', + this.farmCompute.farmTokenPriceUSD.bind(this.farmCompute), + ); + }, + ); + + public readonly farmingTokenPriceUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await this.farmCompute.getAllKeys( + addresses, + 'farm.farmingTokenPriceUSD', + this.farmCompute.farmingTokenPriceUSD.bind(this.farmCompute), + ); + }, + ); +} diff --git a/src/modules/farm/base-module/services/farm.compute.service.ts b/src/modules/farm/base-module/services/farm.compute.service.ts index aedf6600b..013065548 100644 --- a/src/modules/farm/base-module/services/farm.compute.service.ts +++ b/src/modules/farm/base-module/services/farm.compute.service.ts @@ -13,6 +13,7 @@ import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { FarmServiceBase } from './farm.base.service'; import { Inject, forwardRef } from '@nestjs/common'; import { IFarmComputeService } from './interfaces'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; export abstract class FarmComputeService implements IFarmComputeService { constructor( @@ -23,8 +24,31 @@ export abstract class FarmComputeService implements IFarmComputeService { protected readonly pairCompute: PairComputeService, protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, + protected readonly cacheService: CacheService, ) {} + async getAllKeys( + farmAddresses: string[], + baseKey: string, + getterMethod: (address: string) => Promise, + ): Promise { + const keys = farmAddresses.map((address) => `${baseKey}.${address}`); + const values = await this.cacheService.getMany(keys); + + const missingIndexes: number[] = []; + values.forEach((value, index) => { + if (!value) { + missingIndexes.push(index); + } + }); + + for (const missingIndex of missingIndexes) { + const tokenID = await getterMethod(farmAddresses[missingIndex]); + values[missingIndex] = tokenID; + } + return values; + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/farm/custom/farm.custom.module.ts b/src/modules/farm/custom/farm.custom.module.ts index b99d552c4..1c5d581b4 100644 --- a/src/modules/farm/custom/farm.custom.module.ts +++ b/src/modules/farm/custom/farm.custom.module.ts @@ -8,6 +8,8 @@ import { FarmCustomTransactionService } from './services/farm.custom.transaction import { FarmCustomComputeService } from './services/farm.custom.compute.service'; import { ContextModule } from 'src/services/context/context.module'; import { FarmCustomService } from './services/farm.custom.service'; +import { FarmCustomAbiLoader } from './services/farm.custom.abi.loader'; +import { FarmCustomComputeLoader } from './services/farm.custom.compute.loader'; @Module({ imports: [ @@ -17,6 +19,8 @@ import { FarmCustomService } from './services/farm.custom.service'; forwardRef(() => PairModule), ], providers: [ + FarmCustomAbiLoader, + FarmCustomComputeLoader, FarmCustomService, FarmCustomAbiService, FarmCustomComputeService, diff --git a/src/modules/farm/custom/farm.custom.resolver.ts b/src/modules/farm/custom/farm.custom.resolver.ts index 6bac8de37..08fc4d58b 100644 --- a/src/modules/farm/custom/farm.custom.resolver.ts +++ b/src/modules/farm/custom/farm.custom.resolver.ts @@ -4,6 +4,11 @@ import { FarmCustomModel } from '../models/farm.custom.model'; import { FarmCustomAbiService } from './services/farm.custom.abi.service'; import { FarmCustomComputeService } from './services/farm.custom.compute.service'; import { FarmCustomService } from './services/farm.custom.service'; +import { Inject } from '@nestjs/common'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { Logger } from 'winston'; +import { FarmCustomAbiLoader } from './services/farm.custom.abi.loader'; +import { FarmCustomComputeLoader } from './services/farm.custom.compute.loader'; @Resolver(() => FarmCustomModel) export class FarmCustomResolver extends FarmResolver { @@ -11,8 +16,18 @@ export class FarmCustomResolver extends FarmResolver { protected readonly farmAbi: FarmCustomAbiService, protected readonly farmService: FarmCustomService, protected readonly farmCompute: FarmCustomComputeService, + protected readonly farmAbiLoader: FarmCustomAbiLoader, + protected readonly farmComputeLoader: FarmCustomComputeLoader, + @Inject(WINSTON_MODULE_PROVIDER) protected readonly logger: Logger, ) { - super(farmAbi, farmService, farmCompute); + super( + farmAbi, + farmService, + farmCompute, + farmAbiLoader, + farmComputeLoader, + logger, + ); } @ResolveField() diff --git a/src/modules/farm/custom/services/farm.custom.abi.loader.ts b/src/modules/farm/custom/services/farm.custom.abi.loader.ts new file mode 100644 index 000000000..f810e0987 --- /dev/null +++ b/src/modules/farm/custom/services/farm.custom.abi.loader.ts @@ -0,0 +1,16 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; +import { FarmCustomService } from './farm.custom.service'; +import { FarmCustomAbiService } from './farm.custom.abi.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmCustomAbiLoader extends FarmAbiLoader { + constructor( + protected readonly farmAbi: FarmCustomAbiService, + protected readonly farmService: FarmCustomService, + ) { + super(farmAbi, farmService); + } +} diff --git a/src/modules/farm/custom/services/farm.custom.compute.loader.ts b/src/modules/farm/custom/services/farm.custom.compute.loader.ts new file mode 100644 index 000000000..2a6e3ef13 --- /dev/null +++ b/src/modules/farm/custom/services/farm.custom.compute.loader.ts @@ -0,0 +1,12 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; +import { FarmCustomComputeService } from './farm.custom.compute.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmCustomComputeLoader extends FarmComputeLoader { + constructor(protected readonly farmCompute: FarmCustomComputeService) { + super(farmCompute); + } +} diff --git a/src/modules/farm/custom/services/farm.custom.compute.service.ts b/src/modules/farm/custom/services/farm.custom.compute.service.ts index 70f2fdba3..06e0fa1f7 100644 --- a/src/modules/farm/custom/services/farm.custom.compute.service.ts +++ b/src/modules/farm/custom/services/farm.custom.compute.service.ts @@ -6,6 +6,7 @@ import { PairComputeService } from 'src/modules/pair/services/pair.compute.servi import { ContextGetterService } from 'src/services/context/context.getter.service'; import { TokenComputeService } from 'src/modules/tokens/services/token.compute.service'; import { FarmCustomService } from './farm.custom.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class FarmCustomComputeService extends FarmComputeService { @@ -17,6 +18,7 @@ export class FarmCustomComputeService extends FarmComputeService { protected readonly pairCompute: PairComputeService, protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, + protected readonly cacheService: CacheService, ) { super( farmAbi, @@ -25,6 +27,7 @@ export class FarmCustomComputeService extends FarmComputeService { pairCompute, contextGetter, tokenCompute, + cacheService, ); } } diff --git a/src/modules/farm/v1.2/farm.v1.2.module.ts b/src/modules/farm/v1.2/farm.v1.2.module.ts index 3ee74b8d6..667410340 100644 --- a/src/modules/farm/v1.2/farm.v1.2.module.ts +++ b/src/modules/farm/v1.2/farm.v1.2.module.ts @@ -10,6 +10,8 @@ import { FarmTransactionServiceV1_2 } from './services/farm.v1.2.transaction.ser import { FarmServiceV1_2 } from './services/farm.v1.2.service'; import { FarmSetterService } from '../base-module/services/farm.setter.service'; import { FarmSetterServiceV1_2 } from './services/farm.v1.2.setter.service'; +import { FarmAbiLoaderV1_2 } from './services/farm.v1.2.abi.loader'; +import { FarmComputeLoaderV1_2 } from './services/farm.v1.2.compute.loader'; @Module({ imports: [ @@ -19,6 +21,8 @@ import { FarmSetterServiceV1_2 } from './services/farm.v1.2.setter.service'; forwardRef(() => PairModule), ], providers: [ + FarmAbiLoaderV1_2, + FarmComputeLoaderV1_2, FarmServiceV1_2, FarmAbiServiceV1_2, { diff --git a/src/modules/farm/v1.2/farm.v1.2.resolver.ts b/src/modules/farm/v1.2/farm.v1.2.resolver.ts index f2008a921..2cd68336d 100644 --- a/src/modules/farm/v1.2/farm.v1.2.resolver.ts +++ b/src/modules/farm/v1.2/farm.v1.2.resolver.ts @@ -6,6 +6,11 @@ import { FarmAbiServiceV1_2 } from './services/farm.v1.2.abi.service'; import { FarmServiceV1_2 } from './services/farm.v1.2.service'; import { FarmComputeServiceV1_2 } from './services/farm.v1.2.compute.service'; import { LockedAssetModel } from 'src/modules/locked-asset-factory/models/locked-asset.model'; +import { Logger } from 'winston'; +import { Inject } from '@nestjs/common'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { FarmAbiLoaderV1_2 } from './services/farm.v1.2.abi.loader'; +import { FarmComputeLoaderV1_2 } from './services/farm.v1.2.compute.loader'; @Resolver(() => FarmModelV1_2) export class FarmResolverV1_2 extends FarmResolver { @@ -13,8 +18,18 @@ export class FarmResolverV1_2 extends FarmResolver { protected readonly farmAbi: FarmAbiServiceV1_2, protected readonly farmService: FarmServiceV1_2, protected readonly farmCompute: FarmComputeServiceV1_2, + protected readonly farmAbiLoader: FarmAbiLoaderV1_2, + protected readonly farmComputeLoader: FarmComputeLoaderV1_2, + @Inject(WINSTON_MODULE_PROVIDER) protected readonly logger: Logger, ) { - super(farmAbi, farmService, farmCompute); + super( + farmAbi, + farmService, + farmCompute, + farmAbiLoader, + farmComputeLoader, + logger, + ); } @ResolveField() diff --git a/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts b/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts new file mode 100644 index 000000000..4ceb81c3c --- /dev/null +++ b/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts @@ -0,0 +1,16 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; +import { FarmServiceV1_2 } from './farm.v1.2.service'; +import { FarmAbiServiceV1_2 } from './farm.v1.2.abi.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmAbiLoaderV1_2 extends FarmAbiLoader { + constructor( + protected readonly farmAbi: FarmAbiServiceV1_2, + protected readonly farmService: FarmServiceV1_2, + ) { + super(farmAbi, farmService); + } +} diff --git a/src/modules/farm/v1.2/services/farm.v1.2.abi.service.ts b/src/modules/farm/v1.2/services/farm.v1.2.abi.service.ts index 529091ad6..99fe3956d 100644 --- a/src/modules/farm/v1.2/services/farm.v1.2.abi.service.ts +++ b/src/modules/farm/v1.2/services/farm.v1.2.abi.service.ts @@ -10,6 +10,7 @@ import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { Constants } from '@multiversx/sdk-nestjs-common'; import { IFarmAbiServiceV1_2 } from './interfaces'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class FarmAbiServiceV1_2 @@ -20,8 +21,9 @@ export class FarmAbiServiceV1_2 protected readonly mxProxy: MXProxyService, protected readonly gatewayService: MXGatewayService, protected readonly mxApi: MXApiService, + protected readonly cacheService: CacheService, ) { - super(mxProxy, gatewayService, mxApi); + super(mxProxy, gatewayService, mxApi, cacheService); } @ErrorLoggerAsync({ diff --git a/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts b/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts new file mode 100644 index 000000000..bb2916527 --- /dev/null +++ b/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts @@ -0,0 +1,12 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; +import { FarmComputeServiceV1_2 } from './farm.v1.2.compute.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmComputeLoaderV1_2 extends FarmComputeLoader { + constructor(protected readonly farmCompute: FarmComputeServiceV1_2) { + super(farmCompute); + } +} diff --git a/src/modules/farm/v1.2/services/farm.v1.2.compute.service.ts b/src/modules/farm/v1.2/services/farm.v1.2.compute.service.ts index 52a4e4441..6424185e2 100644 --- a/src/modules/farm/v1.2/services/farm.v1.2.compute.service.ts +++ b/src/modules/farm/v1.2/services/farm.v1.2.compute.service.ts @@ -13,6 +13,7 @@ import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { IFarmComputeServiceV1_2 } from './interfaces'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class FarmComputeServiceV1_2 @@ -27,6 +28,7 @@ export class FarmComputeServiceV1_2 protected readonly pairCompute: PairComputeService, protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, + protected readonly cacheService: CacheService, ) { super( farmAbi, @@ -35,6 +37,7 @@ export class FarmComputeServiceV1_2 pairCompute, contextGetter, tokenCompute, + cacheService, ); } diff --git a/src/modules/farm/v1.3/farm.v1.3.module.ts b/src/modules/farm/v1.3/farm.v1.3.module.ts index c3cc110a1..f95acc3ed 100644 --- a/src/modules/farm/v1.3/farm.v1.3.module.ts +++ b/src/modules/farm/v1.3/farm.v1.3.module.ts @@ -10,6 +10,8 @@ import { FarmTransactionServiceV1_3 } from './services/farm.v1.3.transaction.ser import { FarmServiceV1_3 } from './services/farm.v1.3.service'; import { FarmSetterService } from '../base-module/services/farm.setter.service'; import { FarmSetterServiceV1_3 } from './services/farm.v1.3.setter.service'; +import { FarmAbiLoaderV1_3 } from './services/farm.v1.3.abi.loader'; +import { FarmComputeLoaderV1_3 } from './services/farm.v1.3.compute.loader'; @Module({ imports: [ @@ -19,6 +21,8 @@ import { FarmSetterServiceV1_3 } from './services/farm.v1.3.setter.service'; forwardRef(() => PairModule), ], providers: [ + FarmAbiLoaderV1_3, + FarmComputeLoaderV1_3, FarmServiceV1_3, FarmAbiServiceV1_3, { diff --git a/src/modules/farm/v1.3/farm.v1.3.resolver.ts b/src/modules/farm/v1.3/farm.v1.3.resolver.ts index 8f6b1a25d..739a756e9 100644 --- a/src/modules/farm/v1.3/farm.v1.3.resolver.ts +++ b/src/modules/farm/v1.3/farm.v1.3.resolver.ts @@ -5,6 +5,11 @@ import { FarmServiceV1_3 } from './services/farm.v1.3.service'; import { FarmComputeServiceV1_3 } from './services/farm.v1.3.compute.service'; import { FarmAbiServiceV1_3 } from './services/farm.v1.3.abi.service'; import { LockedAssetModel } from 'src/modules/locked-asset-factory/models/locked-asset.model'; +import { Logger } from 'winston'; +import { Inject } from '@nestjs/common'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { FarmAbiLoaderV1_3 } from './services/farm.v1.3.abi.loader'; +import { FarmComputeLoaderV1_3 } from './services/farm.v1.3.compute.loader'; @Resolver(FarmModelV1_3) export class FarmResolverV1_3 extends FarmResolver { @@ -12,8 +17,18 @@ export class FarmResolverV1_3 extends FarmResolver { protected readonly farmAbi: FarmAbiServiceV1_3, protected readonly farmService: FarmServiceV1_3, protected readonly farmCompute: FarmComputeServiceV1_3, + protected readonly farmAbiLoader: FarmAbiLoaderV1_3, + protected readonly farmComputeLoader: FarmComputeLoaderV1_3, + @Inject(WINSTON_MODULE_PROVIDER) protected readonly logger: Logger, ) { - super(farmAbi, farmService, farmCompute); + super( + farmAbi, + farmService, + farmCompute, + farmAbiLoader, + farmComputeLoader, + logger, + ); } @ResolveField() diff --git a/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts b/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts new file mode 100644 index 000000000..5ddca880d --- /dev/null +++ b/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts @@ -0,0 +1,16 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; +import { FarmServiceV1_3 } from './farm.v1.3.service'; +import { FarmAbiServiceV1_3 } from './farm.v1.3.abi.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmAbiLoaderV1_3 extends FarmAbiLoader { + constructor( + protected readonly farmAbi: FarmAbiServiceV1_3, + protected readonly farmService: FarmServiceV1_3, + ) { + super(farmAbi, farmService); + } +} diff --git a/src/modules/farm/v1.3/services/farm.v1.3.abi.service.ts b/src/modules/farm/v1.3/services/farm.v1.3.abi.service.ts index fcaf4cc59..673d47755 100644 --- a/src/modules/farm/v1.3/services/farm.v1.3.abi.service.ts +++ b/src/modules/farm/v1.3/services/farm.v1.3.abi.service.ts @@ -10,6 +10,7 @@ import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { Constants } from '@multiversx/sdk-nestjs-common'; import { IFarmAbiServiceV1_3 } from './interfaces'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class FarmAbiServiceV1_3 @@ -20,8 +21,9 @@ export class FarmAbiServiceV1_3 protected readonly mxProxy: MXProxyService, protected readonly gatewayService: MXGatewayService, protected readonly mxApi: MXApiService, + protected readonly cacheService: CacheService, ) { - super(mxProxy, gatewayService, mxApi); + super(mxProxy, gatewayService, mxApi, cacheService); } @ErrorLoggerAsync({ diff --git a/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts b/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts new file mode 100644 index 000000000..76150b1ed --- /dev/null +++ b/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts @@ -0,0 +1,12 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; +import { FarmComputeServiceV1_3 } from './farm.v1.3.compute.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmComputeLoaderV1_3 extends FarmComputeLoader { + constructor(protected readonly farmCompute: FarmComputeServiceV1_3) { + super(farmCompute); + } +} diff --git a/src/modules/farm/v1.3/services/farm.v1.3.compute.service.ts b/src/modules/farm/v1.3/services/farm.v1.3.compute.service.ts index 3c1b5d046..1e13487fe 100644 --- a/src/modules/farm/v1.3/services/farm.v1.3.compute.service.ts +++ b/src/modules/farm/v1.3/services/farm.v1.3.compute.service.ts @@ -13,6 +13,7 @@ import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { Constants } from '@multiversx/sdk-nestjs-common'; import { IFarmComputeServiceV1_3 } from './interfaces'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class FarmComputeServiceV1_3 @@ -27,6 +28,7 @@ export class FarmComputeServiceV1_3 protected readonly pairCompute: PairComputeService, protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, + protected readonly cacheService: CacheService, ) { super( farmAbi, @@ -35,6 +37,7 @@ export class FarmComputeServiceV1_3 pairCompute, contextGetter, tokenCompute, + cacheService, ); } diff --git a/src/modules/farm/v2/farm.v2.module.ts b/src/modules/farm/v2/farm.v2.module.ts index af164ee71..48e182872 100644 --- a/src/modules/farm/v2/farm.v2.module.ts +++ b/src/modules/farm/v2/farm.v2.module.ts @@ -13,6 +13,8 @@ import { WeekTimekeepingModule } from '../../../submodules/week-timekeeping/week import { WeeklyRewardsSplittingModule } from '../../../submodules/weekly-rewards-splitting/weekly-rewards-splitting.module'; import { EnergyModule } from '../../energy/energy.module'; import { FarmTransactionResolverV2 } from './farm.v2.transaction.resolver'; +import { FarmAbiLoaderV2 } from './services/farm.v2.abi.loader'; +import { FarmComputeLoaderV2 } from './services/farm.v2.compute.loader'; @Module({ imports: [ @@ -25,6 +27,8 @@ import { FarmTransactionResolverV2 } from './farm.v2.transaction.resolver'; forwardRef(() => WeeklyRewardsSplittingModule), ], providers: [ + FarmAbiLoaderV2, + FarmComputeLoaderV2, FarmServiceV2, FarmAbiServiceV2, FarmSetterServiceV2, diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 7f8625610..78dce345c 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -9,7 +9,7 @@ import { constantsConfig } from '../../../config'; import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { FarmAbiServiceV2 } from './services/farm.v2.abi.service'; -import { UseGuards } from '@nestjs/common'; +import { Inject, UseGuards } from '@nestjs/common'; import { JwtOrNativeAuthGuard } from 'src/modules/auth/jwt.or.native.auth.guard'; import { UserAuthResult } from 'src/modules/auth/user.auth.result'; import { AuthUser } from 'src/modules/auth/auth.user'; @@ -21,6 +21,10 @@ import { } from '../models/farm.model'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; +import { Logger } from 'winston'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; +import { FarmAbiLoaderV2 } from './services/farm.v2.abi.loader'; +import { FarmComputeLoaderV2 } from './services/farm.v2.compute.loader'; @Resolver(() => FarmModelV2) export class FarmResolverV2 extends FarmResolver { @@ -28,10 +32,20 @@ export class FarmResolverV2 extends FarmResolver { protected readonly farmAbi: FarmAbiServiceV2, protected readonly farmService: FarmServiceV2, protected readonly farmCompute: FarmComputeServiceV2, + protected readonly farmAbiLoader: FarmAbiLoaderV2, + protected readonly farmComputeLoader: FarmComputeLoaderV2, + @Inject(WINSTON_MODULE_PROVIDER) protected readonly logger: Logger, private readonly weekTimekeepingAbi: WeekTimekeepingAbiService, private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, ) { - super(farmAbi, farmService, farmCompute); + super( + farmAbi, + farmService, + farmCompute, + farmAbiLoader, + farmComputeLoader, + logger, + ); } @ResolveField() diff --git a/src/modules/farm/v2/services/farm.v2.abi.loader.ts b/src/modules/farm/v2/services/farm.v2.abi.loader.ts new file mode 100644 index 000000000..9c80a47c0 --- /dev/null +++ b/src/modules/farm/v2/services/farm.v2.abi.loader.ts @@ -0,0 +1,16 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; +import { FarmServiceV2 } from './farm.v2.service'; +import { FarmAbiServiceV2 } from './farm.v2.abi.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmAbiLoaderV2 extends FarmAbiLoader { + constructor( + protected readonly farmAbi: FarmAbiServiceV2, + protected readonly farmService: FarmServiceV2, + ) { + super(farmAbi, farmService); + } +} diff --git a/src/modules/farm/v2/services/farm.v2.abi.service.ts b/src/modules/farm/v2/services/farm.v2.abi.service.ts index e7f49130f..ad35b541f 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.service.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.service.ts @@ -29,6 +29,7 @@ import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { IFarmAbiServiceV2 } from './interfaces'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class FarmAbiServiceV2 @@ -39,8 +40,9 @@ export class FarmAbiServiceV2 protected readonly mxProxy: MXProxyService, protected readonly gatewayService: MXGatewayService, protected readonly mxApi: MXApiService, + protected readonly cacheService: CacheService, ) { - super(mxProxy, gatewayService, mxApi); + super(mxProxy, gatewayService, mxApi, cacheService); } async getLastErrorMessageRaw(farmAddress: string): Promise { diff --git a/src/modules/farm/v2/services/farm.v2.compute.loader.ts b/src/modules/farm/v2/services/farm.v2.compute.loader.ts new file mode 100644 index 000000000..860e43c7d --- /dev/null +++ b/src/modules/farm/v2/services/farm.v2.compute.loader.ts @@ -0,0 +1,12 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; +import { FarmComputeServiceV2 } from './farm.v2.compute.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class FarmComputeLoaderV2 extends FarmComputeLoader { + constructor(protected readonly farmCompute: FarmComputeServiceV2) { + super(farmCompute); + } +} diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index cbdffa1f3..70558ed5c 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -35,10 +35,10 @@ export class FarmComputeServiceV2 protected readonly pairCompute: PairComputeService, protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, + protected readonly cachingService: CacheService, private readonly weekTimekeepingCompute: WeekTimekeepingComputeService, private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, private readonly weeklyRewardsSplittingCompute: WeeklyRewardsSplittingComputeService, - private readonly cachingService: CacheService, ) { super( farmAbi, @@ -47,6 +47,7 @@ export class FarmComputeServiceV2 pairCompute, contextGetter, tokenCompute, + cachingService, ); } diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index 407a3d172..6020f318a 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -122,45 +122,73 @@ export class TokenService { localTtl: CacheTtlInfo.Token.localTtl, }) async tokenMetadata(tokenID: string): Promise { - return await this.tokenMetadataRaw(tokenID); + return this.tokenMetadataRaw(tokenID); + } + + async getAllTokensMetadata(tokenIDs: string[]): Promise { + const keys = tokenIDs.map( + (tokenID) => `token.tokenMetadata.${tokenID}`, + ); + const values = await this.cachingService.getMany(keys); + + const missingIndexes: number[] = []; + values.forEach((value, index) => { + if (!value) { + missingIndexes.push(index); + } + }); + + for (const missingIndex of missingIndexes) { + const token = await this.tokenMetadata(tokenIDs[missingIndex]); + values[missingIndex] = token; + } + return values; } async tokenMetadataRaw(tokenID: string): Promise { - return await this.apiService.getToken(tokenID); + return this.apiService.getToken(tokenID); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'token', + remoteTtl: CacheTtlInfo.Token.remoteTtl, + localTtl: CacheTtlInfo.Token.localTtl, + }) async getNftCollectionMetadata(collection: string): Promise { - if (collection === undefined) { - return undefined; - } - const cacheKey = `token.${collection}`; - const cachedToken = await this.cachingService.get( - cacheKey, + return this.getNftCollectionMetadataRaw(collection); + } + + async getAllNftsCollectionMetadata( + collections: string[], + ): Promise { + const keys = collections.map( + (collection) => `token.getNftCollectionMetadata.${collection}`, ); - if (cachedToken && cachedToken !== undefined) { - await this.cachingService.set( - cacheKey, - cachedToken, - CacheTtlInfo.Token.remoteTtl, - CacheTtlInfo.Token.localTtl, - ); - return cachedToken; - } + const values = await this.cachingService.getMany(keys); - const token = await this.apiService.getNftCollection(collection); + const missingIndexes: number[] = []; + values.forEach((value, index) => { + if (!value) { + missingIndexes.push(index); + } + }); - if (token !== undefined) { - await this.cachingService.set( - cacheKey, - token, - CacheTtlInfo.Token.remoteTtl, - CacheTtlInfo.Token.localTtl, + for (const missingIndex of missingIndexes) { + const token = await this.getNftCollectionMetadata( + collections[missingIndex], ); - - return token; + values[missingIndex] = token; } + return values; + } - return undefined; + async getNftCollectionMetadataRaw( + collection: string, + ): Promise { + return this.apiService.getNftCollection(collection); } async getUniqueTokenIDs(activePool: boolean): Promise { diff --git a/src/services/caching/cache.ttl.info.ts b/src/services/caching/cache.ttl.info.ts index 6f541c57e..e9e8a01f5 100644 --- a/src/services/caching/cache.ttl.info.ts +++ b/src/services/caching/cache.ttl.info.ts @@ -36,7 +36,7 @@ export class CacheTtlInfo { static Price: CacheTtlInfo = new CacheTtlInfo( Constants.oneMinute(), - Constants.oneSecond() * 30, + Constants.oneSecond() * 45, ); static Analytics: CacheTtlInfo = new CacheTtlInfo( From d8f9dcf80b9344d2adc9484f6bce7e56c5137795 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 15 Aug 2024 20:01:09 +0300 Subject: [PATCH 262/313] MEX-509: extract getMany function to a separate utils file Signed-off-by: Claudiu Lataretu --- .../base-module/services/farm.abi.loader.ts | 33 +++++++---- .../base-module/services/farm.abi.service.ts | 22 -------- .../base-module/services/farm.base.service.ts | 10 +++- .../services/farm.compute.loader.ts | 19 +++++-- .../services/farm.compute.service.ts | 22 -------- .../custom/services/farm.custom.abi.loader.ts | 4 +- .../services/farm.custom.compute.loader.ts | 8 ++- .../v1.2/services/farm.v1.2.abi.loader.ts | 4 +- .../v1.2/services/farm.v1.2.compute.loader.ts | 8 ++- .../v1.3/services/farm.v1.3.abi.loader.ts | 4 +- .../v1.3/services/farm.v1.3.compute.loader.ts | 8 ++- .../farm/v2/services/farm.v2.abi.loader.ts | 4 +- .../v2/services/farm.v2.compute.loader.ts | 8 ++- src/utils/get.many.utils.ts | 56 +++++++++++++++++++ 14 files changed, 136 insertions(+), 74 deletions(-) create mode 100644 src/utils/get.many.utils.ts diff --git a/src/modules/farm/base-module/services/farm.abi.loader.ts b/src/modules/farm/base-module/services/farm.abi.loader.ts index aee62355d..93b81955a 100644 --- a/src/modules/farm/base-module/services/farm.abi.loader.ts +++ b/src/modules/farm/base-module/services/farm.abi.loader.ts @@ -4,6 +4,8 @@ import DataLoader from 'dataloader'; import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { FarmAbiService } from './farm.abi.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { getAllKeys } from 'src/utils/get.many.utils'; @Injectable({ scope: Scope.REQUEST, @@ -12,6 +14,7 @@ export class FarmAbiLoader { constructor( protected readonly farmAbi: FarmAbiService, protected readonly farmService: FarmServiceBase, + protected readonly cacheService: CacheService, ) {} public readonly farmedTokenLoader = new DataLoader( @@ -36,7 +39,8 @@ export class FarmAbiLoader { string, boolean >(async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.produceRewardsEnabled', this.farmAbi.produceRewardsEnabled.bind(this.farmAbi), @@ -45,7 +49,8 @@ export class FarmAbiLoader { public readonly perBlockRewardsLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.perBlockRewards', this.farmAbi.rewardsPerBlock.bind(this.farmAbi), @@ -55,7 +60,8 @@ export class FarmAbiLoader { public readonly farmTokenSupplyLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.farmTokenSupply', this.farmAbi.farmTokenSupply.bind(this.farmAbi), @@ -65,7 +71,8 @@ export class FarmAbiLoader { public readonly penaltyPercentLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.penaltyPercent', this.farmAbi.penaltyPercent.bind(this.farmAbi), @@ -75,7 +82,8 @@ export class FarmAbiLoader { public readonly minimumFarmingEpochsLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.minimumFarmingEpochs', this.farmAbi.minimumFarmingEpochs.bind(this.farmAbi), @@ -85,7 +93,8 @@ export class FarmAbiLoader { public readonly rewardPerShareLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.rewardPerShare', this.farmAbi.rewardPerShare.bind(this.farmAbi), @@ -95,7 +104,8 @@ export class FarmAbiLoader { public readonly rewardReserveLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.rewardReserve', this.farmAbi.rewardReserve.bind(this.farmAbi), @@ -105,7 +115,8 @@ export class FarmAbiLoader { public readonly lastRewardBlockNonceLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.lastRewardBlockNonce', this.farmAbi.lastRewardBlockNonce.bind(this.farmAbi), @@ -117,7 +128,8 @@ export class FarmAbiLoader { string, string >(async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.divisionSafetyConstant', this.farmAbi.divisionSafetyConstant.bind(this.farmAbi), @@ -126,7 +138,8 @@ export class FarmAbiLoader { public readonly stateLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmAbi.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.state', this.farmAbi.state.bind(this.farmAbi), diff --git a/src/modules/farm/base-module/services/farm.abi.service.ts b/src/modules/farm/base-module/services/farm.abi.service.ts index 0116336ce..846380ad5 100644 --- a/src/modules/farm/base-module/services/farm.abi.service.ts +++ b/src/modules/farm/base-module/services/farm.abi.service.ts @@ -29,28 +29,6 @@ export class FarmAbiService super(mxProxy); } - async getAllKeys( - farmAddresses: string[], - baseKey: string, - getterMethod: (address: string) => Promise, - ): Promise { - const keys = farmAddresses.map((address) => `${baseKey}.${address}`); - const values = await this.cacheService.getMany(keys); - - const missingIndexes: number[] = []; - values.forEach((value, index) => { - if (!value) { - missingIndexes.push(index); - } - }); - - for (const missingIndex of missingIndexes) { - const tokenID = await getterMethod(farmAddresses[missingIndex]); - values[missingIndex] = tokenID; - } - return values; - } - @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/farm/base-module/services/farm.base.service.ts b/src/modules/farm/base-module/services/farm.base.service.ts index da8d8e451..e9faf590a 100644 --- a/src/modules/farm/base-module/services/farm.base.service.ts +++ b/src/modules/farm/base-module/services/farm.base.service.ts @@ -16,6 +16,7 @@ import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { Inject, forwardRef } from '@nestjs/common'; import { TokenService } from 'src/modules/tokens/services/token.service'; +import { getAllKeys } from 'src/utils/get.many.utils'; export abstract class FarmServiceBase { constructor( @@ -33,7 +34,8 @@ export abstract class FarmServiceBase { } async getAllFarmedTokens(farmAddresses: string[]): Promise { - const farmedTokenIDs = await this.farmAbi.getAllKeys( + const farmedTokenIDs = await getAllKeys( + this.cachingService, farmAddresses, 'farm.farmedTokenID', this.farmAbi.farmedTokenID.bind(this.farmAbi), @@ -47,7 +49,8 @@ export abstract class FarmServiceBase { } async getAllFarmTokens(farmAddresses: string[]): Promise { - const farmTokenIDs = await this.farmAbi.getAllKeys( + const farmTokenIDs = await getAllKeys( + this.cachingService, farmAddresses, 'farm.farmTokenID', this.farmAbi.farmTokenID.bind(this.farmAbi), @@ -61,7 +64,8 @@ export abstract class FarmServiceBase { } async getAllFarmingTokens(farmAddresses: string[]): Promise { - const farmingTokenIDs = await this.farmAbi.getAllKeys( + const farmingTokenIDs = await getAllKeys( + this.cachingService, farmAddresses, 'farm.farmingTokenID', this.farmAbi.farmingTokenID.bind(this.farmAbi), diff --git a/src/modules/farm/base-module/services/farm.compute.loader.ts b/src/modules/farm/base-module/services/farm.compute.loader.ts index 10de3eb9a..8cce1139a 100644 --- a/src/modules/farm/base-module/services/farm.compute.loader.ts +++ b/src/modules/farm/base-module/services/farm.compute.loader.ts @@ -1,16 +1,22 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmComputeService } from './farm.compute.service'; import DataLoader from 'dataloader'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { getAllKeys } from 'src/utils/get.many.utils'; @Injectable({ scope: Scope.REQUEST, }) export class FarmComputeLoader { - constructor(protected readonly farmCompute: FarmComputeService) {} + constructor( + protected readonly farmCompute: FarmComputeService, + protected readonly cacheService: CacheService, + ) {} public readonly farmLockedValueUSDLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmCompute.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.farmLockedValueUSD', this.farmCompute.farmLockedValueUSD.bind(this.farmCompute), @@ -20,7 +26,8 @@ export class FarmComputeLoader { public readonly farmedTokenPriceUSDLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmCompute.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.farmedTokenPriceUSD', this.farmCompute.farmedTokenPriceUSD.bind(this.farmCompute), @@ -30,7 +37,8 @@ export class FarmComputeLoader { public readonly farmTokenPriceUSDLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmCompute.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.farmTokenPriceUSD', this.farmCompute.farmTokenPriceUSD.bind(this.farmCompute), @@ -40,7 +48,8 @@ export class FarmComputeLoader { public readonly farmingTokenPriceUSDLoader = new DataLoader( async (addresses: string[]) => { - return await this.farmCompute.getAllKeys( + return await getAllKeys( + this.cacheService, addresses, 'farm.farmingTokenPriceUSD', this.farmCompute.farmingTokenPriceUSD.bind(this.farmCompute), diff --git a/src/modules/farm/base-module/services/farm.compute.service.ts b/src/modules/farm/base-module/services/farm.compute.service.ts index 013065548..718d61b4b 100644 --- a/src/modules/farm/base-module/services/farm.compute.service.ts +++ b/src/modules/farm/base-module/services/farm.compute.service.ts @@ -27,28 +27,6 @@ export abstract class FarmComputeService implements IFarmComputeService { protected readonly cacheService: CacheService, ) {} - async getAllKeys( - farmAddresses: string[], - baseKey: string, - getterMethod: (address: string) => Promise, - ): Promise { - const keys = farmAddresses.map((address) => `${baseKey}.${address}`); - const values = await this.cacheService.getMany(keys); - - const missingIndexes: number[] = []; - values.forEach((value, index) => { - if (!value) { - missingIndexes.push(index); - } - }); - - for (const missingIndex of missingIndexes) { - const tokenID = await getterMethod(farmAddresses[missingIndex]); - values[missingIndex] = tokenID; - } - return values; - } - @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/farm/custom/services/farm.custom.abi.loader.ts b/src/modules/farm/custom/services/farm.custom.abi.loader.ts index f810e0987..3c0b09b5d 100644 --- a/src/modules/farm/custom/services/farm.custom.abi.loader.ts +++ b/src/modules/farm/custom/services/farm.custom.abi.loader.ts @@ -2,6 +2,7 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; import { FarmCustomService } from './farm.custom.service'; import { FarmCustomAbiService } from './farm.custom.abi.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, @@ -10,7 +11,8 @@ export class FarmCustomAbiLoader extends FarmAbiLoader { constructor( protected readonly farmAbi: FarmCustomAbiService, protected readonly farmService: FarmCustomService, + protected readonly cacheService: CacheService, ) { - super(farmAbi, farmService); + super(farmAbi, farmService, cacheService); } } diff --git a/src/modules/farm/custom/services/farm.custom.compute.loader.ts b/src/modules/farm/custom/services/farm.custom.compute.loader.ts index 2a6e3ef13..13bbca222 100644 --- a/src/modules/farm/custom/services/farm.custom.compute.loader.ts +++ b/src/modules/farm/custom/services/farm.custom.compute.loader.ts @@ -1,12 +1,16 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; import { FarmCustomComputeService } from './farm.custom.compute.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, }) export class FarmCustomComputeLoader extends FarmComputeLoader { - constructor(protected readonly farmCompute: FarmCustomComputeService) { - super(farmCompute); + constructor( + protected readonly farmCompute: FarmCustomComputeService, + protected readonly cacheService: CacheService, + ) { + super(farmCompute, cacheService); } } diff --git a/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts b/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts index 4ceb81c3c..ba3606895 100644 --- a/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts +++ b/src/modules/farm/v1.2/services/farm.v1.2.abi.loader.ts @@ -2,6 +2,7 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; import { FarmServiceV1_2 } from './farm.v1.2.service'; import { FarmAbiServiceV1_2 } from './farm.v1.2.abi.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, @@ -10,7 +11,8 @@ export class FarmAbiLoaderV1_2 extends FarmAbiLoader { constructor( protected readonly farmAbi: FarmAbiServiceV1_2, protected readonly farmService: FarmServiceV1_2, + protected readonly cacheService: CacheService, ) { - super(farmAbi, farmService); + super(farmAbi, farmService, cacheService); } } diff --git a/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts b/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts index bb2916527..b20a41177 100644 --- a/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts +++ b/src/modules/farm/v1.2/services/farm.v1.2.compute.loader.ts @@ -1,12 +1,16 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; import { FarmComputeServiceV1_2 } from './farm.v1.2.compute.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, }) export class FarmComputeLoaderV1_2 extends FarmComputeLoader { - constructor(protected readonly farmCompute: FarmComputeServiceV1_2) { - super(farmCompute); + constructor( + protected readonly farmCompute: FarmComputeServiceV1_2, + protected readonly cacheService: CacheService, + ) { + super(farmCompute, cacheService); } } diff --git a/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts b/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts index 5ddca880d..79037bbee 100644 --- a/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts +++ b/src/modules/farm/v1.3/services/farm.v1.3.abi.loader.ts @@ -2,6 +2,7 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; import { FarmServiceV1_3 } from './farm.v1.3.service'; import { FarmAbiServiceV1_3 } from './farm.v1.3.abi.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, @@ -10,7 +11,8 @@ export class FarmAbiLoaderV1_3 extends FarmAbiLoader { constructor( protected readonly farmAbi: FarmAbiServiceV1_3, protected readonly farmService: FarmServiceV1_3, + protected readonly cacheService: CacheService, ) { - super(farmAbi, farmService); + super(farmAbi, farmService, cacheService); } } diff --git a/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts b/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts index 76150b1ed..1676282a2 100644 --- a/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts +++ b/src/modules/farm/v1.3/services/farm.v1.3.compute.loader.ts @@ -1,12 +1,16 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; import { FarmComputeServiceV1_3 } from './farm.v1.3.compute.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, }) export class FarmComputeLoaderV1_3 extends FarmComputeLoader { - constructor(protected readonly farmCompute: FarmComputeServiceV1_3) { - super(farmCompute); + constructor( + protected readonly farmCompute: FarmComputeServiceV1_3, + protected readonly cacheService: CacheService, + ) { + super(farmCompute, cacheService); } } diff --git a/src/modules/farm/v2/services/farm.v2.abi.loader.ts b/src/modules/farm/v2/services/farm.v2.abi.loader.ts index 9c80a47c0..73a897fc3 100644 --- a/src/modules/farm/v2/services/farm.v2.abi.loader.ts +++ b/src/modules/farm/v2/services/farm.v2.abi.loader.ts @@ -2,6 +2,7 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmAbiLoader } from '../../base-module/services/farm.abi.loader'; import { FarmServiceV2 } from './farm.v2.service'; import { FarmAbiServiceV2 } from './farm.v2.abi.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, @@ -10,7 +11,8 @@ export class FarmAbiLoaderV2 extends FarmAbiLoader { constructor( protected readonly farmAbi: FarmAbiServiceV2, protected readonly farmService: FarmServiceV2, + protected readonly cacheService: CacheService, ) { - super(farmAbi, farmService); + super(farmAbi, farmService, cacheService); } } diff --git a/src/modules/farm/v2/services/farm.v2.compute.loader.ts b/src/modules/farm/v2/services/farm.v2.compute.loader.ts index 860e43c7d..ce72e395b 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.loader.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.loader.ts @@ -1,12 +1,16 @@ import { Injectable, Scope } from '@nestjs/common'; import { FarmComputeLoader } from '../../base-module/services/farm.compute.loader'; import { FarmComputeServiceV2 } from './farm.v2.compute.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable({ scope: Scope.REQUEST, }) export class FarmComputeLoaderV2 extends FarmComputeLoader { - constructor(protected readonly farmCompute: FarmComputeServiceV2) { - super(farmCompute); + constructor( + protected readonly farmCompute: FarmComputeServiceV2, + protected readonly cacheService: CacheService, + ) { + super(farmCompute, cacheService); } } diff --git a/src/utils/get.many.utils.ts b/src/utils/get.many.utils.ts new file mode 100644 index 000000000..4dab13185 --- /dev/null +++ b/src/utils/get.many.utils.ts @@ -0,0 +1,56 @@ +import { CacheService } from '@multiversx/sdk-nestjs-cache'; + +async function getMany( + cacheService: CacheService, + keys: string[], +): Promise<(T | undefined)[]> { + const values = await cacheService.getManyLocal(keys); + + const missingIndexes: number[] = []; + values.forEach((value, index) => { + if (!value) { + missingIndexes.push(index); + } + }); + + const missingKeys: string[] = []; + for (const missingIndex of missingIndexes) { + missingKeys.push(keys[missingIndex]); + } + + if (missingKeys.length === 0) { + return values; + } + + const remoteValues = await cacheService.getManyRemote(missingKeys); + + for (const [index, missingIndex] of missingIndexes.entries()) { + const remoteValue = remoteValues[index]; + values[missingIndex] = remoteValue ? remoteValue : undefined; + } + + return values; +} + +export async function getAllKeys( + cacheService: CacheService, + tokenIDs: string[], + baseKey: string, + getterMethod: (address: string) => Promise, +): Promise { + const keys = tokenIDs.map((tokenID) => `${baseKey}.${tokenID}`); + const values = await getMany(cacheService, keys); + + const missingIndexes: number[] = []; + values.forEach((value, index) => { + if (!value) { + missingIndexes.push(index); + } + }); + + for (const missingIndex of missingIndexes) { + const tokenID = await getterMethod(tokenIDs[missingIndex]); + values[missingIndex] = tokenID; + } + return values; +} From 8eb51450e8cf9c2b6d3f351d7e55aa01d9eb3f6a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 15 Aug 2024 20:01:41 +0300 Subject: [PATCH 263/313] MEX-510: Add token loader service Signed-off-by: Claudiu Lataretu --- src/modules/tokens/services/token.loader.ts | 153 +++++++++++++++++++ src/modules/tokens/services/token.service.ts | 45 ++---- src/modules/tokens/token.module.ts | 3 + src/modules/tokens/token.resolver.ts | 61 +++----- 4 files changed, 191 insertions(+), 71 deletions(-) create mode 100644 src/modules/tokens/services/token.loader.ts diff --git a/src/modules/tokens/services/token.loader.ts b/src/modules/tokens/services/token.loader.ts new file mode 100644 index 000000000..9dad620d5 --- /dev/null +++ b/src/modules/tokens/services/token.loader.ts @@ -0,0 +1,153 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { TokenComputeService } from './token.compute.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import DataLoader from 'dataloader'; +import { getAllKeys } from 'src/utils/get.many.utils'; +import { TokenService } from './token.service'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class TokenLoader { + constructor( + private readonly tokenService: TokenService, + private readonly tokenCompute: TokenComputeService, + private readonly cacheService: CacheService, + ) {} + + public readonly tokenTypeLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.getEsdtTokenType', + this.tokenService.getEsdtTokenType.bind(this.tokenService), + ); + }, + ); + + public readonly tokenPriceDerivedEGLDLoader = new DataLoader< + string, + string + >(async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenPriceDerivedEGLD', + this.tokenCompute.tokenPriceDerivedEGLD.bind(this.tokenCompute), + ); + }); + + public readonly tokenPriceDerivedUSDLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenPriceDerivedUSD', + this.tokenCompute.tokenPriceDerivedUSD.bind(this.tokenCompute), + ); + }, + ); + + public readonly tokenPrevious24hPriceLoader = new DataLoader< + string, + string + >(async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenPrevious24hPrice', + this.tokenCompute.tokenPrevious24hPrice.bind(this.tokenCompute), + ); + }); + + public readonly tokenPrevious7dPriceLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenPrevious7dPrice', + this.tokenCompute.tokenPrevious7dPrice.bind(this.tokenCompute), + ); + }, + ); + + public readonly tokenVolumeUSD24hLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenVolumeUSD24h', + this.tokenCompute.tokenVolumeUSD24h.bind(this.tokenCompute), + ); + }, + ); + + public readonly tokenPrevious24hVolumeUSDLoader = new DataLoader< + string, + string + >(async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenPrevious24hVolumeUSD', + this.tokenCompute.tokenPrevious24hVolumeUSD.bind(this.tokenCompute), + ); + }); + + public readonly tokenLiquidityUSDLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenLiquidityUSD', + this.tokenCompute.tokenLiquidityUSD.bind(this.tokenCompute), + ); + }, + ); + + public readonly tokenCreatedAtLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenCreatedAt', + this.tokenCompute.tokenCreatedAt.bind(this.tokenCompute), + ); + }, + ); + + public readonly tokenSwapCountLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenSwapCount', + this.tokenCompute.tokenSwapCount.bind(this.tokenCompute), + ); + }, + ); + + public readonly tokenPrevious24hSwapCountLoader = new DataLoader< + string, + number + >(async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenPrevious24hSwapCount', + this.tokenCompute.tokenPrevious24hSwapCount.bind(this.tokenCompute), + ); + }); + + public readonly tokenTrendingScoreLoader = new DataLoader( + async (tokenIDs: string[]) => { + return await getAllKeys( + this.cacheService, + tokenIDs, + 'token.tokenTrendingScore', + this.tokenCompute.tokenTrendingScore.bind(this.tokenCompute), + ); + }, + ); +} diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index 6020f318a..2888d03aa 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -21,6 +21,7 @@ import BigNumber from 'bignumber.js'; import { SortingOrder } from 'src/modules/common/page.data'; import { TokenFilteringService } from './token.filtering.service'; import { PaginationArgs } from 'src/modules/dex.model'; +import { getAllKeys } from 'src/utils/get.many.utils'; @Injectable() export class TokenService { @@ -126,23 +127,12 @@ export class TokenService { } async getAllTokensMetadata(tokenIDs: string[]): Promise { - const keys = tokenIDs.map( - (tokenID) => `token.tokenMetadata.${tokenID}`, + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenMetadata', + this.tokenMetadata.bind(this), ); - const values = await this.cachingService.getMany(keys); - - const missingIndexes: number[] = []; - values.forEach((value, index) => { - if (!value) { - missingIndexes.push(index); - } - }); - - for (const missingIndex of missingIndexes) { - const token = await this.tokenMetadata(tokenIDs[missingIndex]); - values[missingIndex] = token; - } - return values; } async tokenMetadataRaw(tokenID: string): Promise { @@ -164,25 +154,12 @@ export class TokenService { async getAllNftsCollectionMetadata( collections: string[], ): Promise { - const keys = collections.map( - (collection) => `token.getNftCollectionMetadata.${collection}`, + return getAllKeys( + this.cachingService, + collections, + 'token.getNftCollectionMetadata', + this.getNftCollectionMetadata.bind(this), ); - const values = await this.cachingService.getMany(keys); - - const missingIndexes: number[] = []; - values.forEach((value, index) => { - if (!value) { - missingIndexes.push(index); - } - }); - - for (const missingIndex of missingIndexes) { - const token = await this.getNftCollectionMetadata( - collections[missingIndex], - ); - values[missingIndex] = token; - } - return values; } async getNftCollectionMetadataRaw( diff --git a/src/modules/tokens/token.module.ts b/src/modules/tokens/token.module.ts index 293d362b0..c8b6bd73f 100644 --- a/src/modules/tokens/token.module.ts +++ b/src/modules/tokens/token.module.ts @@ -17,6 +17,7 @@ import { ElasticService } from 'src/helpers/elastic.service'; import { TokenFilteringService } from './services/token.filtering.service'; import { ElasticSearchModule } from 'src/services/elastic-search/elastic.search.module'; import { ESLogsService } from 'src/services/elastic-search/services/es.logs.service'; +import { TokenLoader } from './services/token.loader'; @Module({ imports: [ @@ -31,6 +32,7 @@ import { ESLogsService } from 'src/services/elastic-search/services/es.logs.serv ElasticSearchModule, ], providers: [ + TokenLoader, TokenService, TokenSetterService, TokenComputeService, @@ -45,6 +47,7 @@ import { ESLogsService } from 'src/services/elastic-search/services/es.logs.serv ], exports: [ TokenRepositoryService, + TokenLoader, TokenService, TokenSetterService, TokenComputeService, diff --git a/src/modules/tokens/token.resolver.ts b/src/modules/tokens/token.resolver.ts index 06ddb0a99..9116929a1 100644 --- a/src/modules/tokens/token.resolver.ts +++ b/src/modules/tokens/token.resolver.ts @@ -8,18 +8,17 @@ import { TokensFiltersArgs, } from './models/tokens.filter.args'; import { TokenService } from './services/token.service'; -import { GenericResolver } from '../../services/generics/generic.resolver'; import { GraphQLError } from 'graphql'; import { ApolloServerErrorCode } from '@apollo/server/errors'; -import { TokenComputeService } from './services/token.compute.service'; import { TokensResponse } from './models/tokens.response'; import ConnectionArgs, { getPagingParameters, } from '../common/filters/connection.args'; import PageResponse from '../common/page.response'; +import { TokenLoader } from './services/token.loader'; @Resolver(() => AssetsModel) -export class AssetsResolver extends GenericResolver { +export class AssetsResolver { @ResolveField(() => SocialModel, { nullable: true }) async social(@Parent() parent: AssetsModel): Promise { return new SocialModel(parent.social); @@ -27,40 +26,36 @@ export class AssetsResolver extends GenericResolver { } @Resolver(() => EsdtToken) -export class TokensResolver extends GenericResolver { +export class TokensResolver { constructor( private readonly tokenService: TokenService, - private readonly tokenCompute: TokenComputeService, - ) { - super(); - } + private readonly tokenLoader: TokenLoader, + ) {} @ResolveField(() => String) async derivedEGLD(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenPriceDerivedEGLD(parent.identifier), + return this.tokenLoader.tokenPriceDerivedEGLDLoader.load( + parent.identifier, ); } @ResolveField(() => String) async price(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenPriceDerivedUSD(parent.identifier), + return this.tokenLoader.tokenPriceDerivedUSDLoader.load( + parent.identifier, ); } @ResolveField(() => String, { nullable: true }) async previous24hPrice(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenPrevious24hPrice(parent.identifier), + return this.tokenLoader.tokenPrevious24hPriceLoader.load( + parent.identifier, ); } @ResolveField(() => String) async type(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenService.getEsdtTokenType(parent.identifier), - ); + return this.tokenLoader.tokenTypeLoader.load(parent.identifier); } @ResolveField(() => AssetsModel, { nullable: true }) @@ -75,57 +70,49 @@ export class TokensResolver extends GenericResolver { @ResolveField(() => String, { nullable: true }) async previous7dPrice(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenPrevious7dPrice(parent.identifier), + return this.tokenLoader.tokenPrevious7dPriceLoader.load( + parent.identifier, ); } @ResolveField(() => String, { nullable: true }) async volumeUSD24h(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenVolumeUSD24h(parent.identifier), - ); + return this.tokenLoader.tokenVolumeUSD24hLoader.load(parent.identifier); } @ResolveField(() => String, { nullable: true }) async previous24hVolume(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenPrevious24hVolumeUSD(parent.identifier), + return this.tokenLoader.tokenPrevious24hVolumeUSDLoader.load( + parent.identifier, ); } @ResolveField(() => String, { nullable: true }) async liquidityUSD(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenLiquidityUSD(parent.identifier), - ); + return this.tokenLoader.tokenLiquidityUSDLoader.load(parent.identifier); } @ResolveField(() => String, { nullable: true }) async createdAt(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenCreatedAt(parent.identifier), - ); + return this.tokenLoader.tokenCreatedAtLoader.load(parent.identifier); } @ResolveField(() => Number, { nullable: true }) async swapCount24h(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenSwapCount(parent.identifier), - ); + return this.tokenLoader.tokenSwapCountLoader.load(parent.identifier); } @ResolveField(() => Number, { nullable: true }) async previous24hSwapCount(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenPrevious24hSwapCount(parent.identifier), + return this.tokenLoader.tokenPrevious24hSwapCountLoader.load( + parent.identifier, ); } @ResolveField(() => String, { nullable: true }) async trendingScore(@Parent() parent: EsdtToken): Promise { - return await this.genericFieldResolver(() => - this.tokenCompute.tokenTrendingScore(parent.identifier), + return this.tokenLoader.tokenTrendingScoreLoader.load( + parent.identifier, ); } From d101b6411d46b907c4d29310ca029dd83b0a9f59 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 16 Aug 2024 15:15:14 +0300 Subject: [PATCH 264/313] MEX-511: added pair abi and compute loaders Signed-off-by: Claudiu Lataretu --- src/modules/pair/pair.module.ts | 6 + src/modules/pair/pair.resolver.ts | 78 ++++--- src/modules/pair/services/pair.abi.loader.ts | 123 +++++++++++ .../pair/services/pair.compute.loader.ts | 205 ++++++++++++++++++ src/modules/pair/services/pair.service.ts | 34 +++ src/utils/get.many.utils.ts | 6 +- 6 files changed, 421 insertions(+), 31 deletions(-) create mode 100644 src/modules/pair/services/pair.abi.loader.ts create mode 100644 src/modules/pair/services/pair.compute.loader.ts diff --git a/src/modules/pair/pair.module.ts b/src/modules/pair/pair.module.ts index db21a0c68..1d76128c5 100644 --- a/src/modules/pair/pair.module.ts +++ b/src/modules/pair/pair.module.ts @@ -25,6 +25,8 @@ import { FarmModuleV2 } from '../farm/v2/farm.v2.module'; import { PairFilteringService } from './services/pair.filtering.service'; import { StakingModule } from '../staking/staking.module'; import { EnergyModule } from '../energy/energy.module'; +import { PairAbiLoader } from './services/pair.abi.loader'; +import { PairComputeLoader } from './services/pair.compute.loader'; @Module({ imports: [ CommonAppModule, @@ -48,6 +50,10 @@ import { EnergyModule } from '../energy/energy.module'; PairComputeService, PairAbiService, PairTransactionService, + PairFilteringService, + PairAbiLoader, + PairComputeLoader, + ElasticService, PairResolver, ElasticService, PairFilteringService, diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index 8d9a5fa24..c3500a80a 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -28,13 +28,14 @@ import { PairAbiService } from './services/pair.abi.service'; import { PairComputeService } from './services/pair.compute.service'; import { JwtOrNativeAdminGuard } from '../auth/jwt.or.native.admin.guard'; import { FeesCollectorModel } from '../fees-collector/models/fees-collector.model'; -import { constantsConfig } from 'src/config'; import { GenericResolver } from 'src/services/generics/generic.resolver'; import { FarmComputeServiceV2 } from '../farm/v2/services/farm.v2.compute.service'; import { StakingComputeService } from '../staking/services/staking.compute.service'; import { StakingProxyService } from '../staking-proxy/services/staking.proxy.service'; import { NftCollection } from '../tokens/models/nftCollection.model'; import { EnergyService } from '../energy/services/energy.service'; +import { PairAbiLoader } from './services/pair.abi.loader'; +import { PairComputeLoader } from './services/pair.compute.loader'; @Resolver(() => PairRewardTokensModel) export class PairRewardTokensResolver extends GenericResolver { @@ -172,74 +173,92 @@ export class PairResolver { private readonly pairAbi: PairAbiService, private readonly pairCompute: PairComputeService, private readonly transactionService: PairTransactionService, + private readonly pairAbiLoader: PairAbiLoader, + private readonly pairComputeLoader: PairComputeLoader, ) {} @ResolveField() async firstToken(@Parent() parent: PairModel): Promise { - return this.pairService.getFirstToken(parent.address); + return this.pairAbiLoader.firstTokenLoader.load(parent.address); } @ResolveField() async secondToken(@Parent() parent: PairModel): Promise { - return this.pairService.getSecondToken(parent.address); + return this.pairAbiLoader.secondTokenLoader.load(parent.address); } @ResolveField() async liquidityPoolToken(@Parent() parent: PairModel): Promise { - return this.pairService.getLpToken(parent.address); + return this.pairAbiLoader.liquidityPoolTokenLoader.load(parent.address); } @ResolveField() async firstTokenPrice(@Parent() parent: PairModel): Promise { - return this.pairCompute.firstTokenPrice(parent.address); + return this.pairComputeLoader.firstTokenPriceLoader.load( + parent.address, + ); } @ResolveField() async firstTokenPriceUSD(@Parent() parent: PairModel): Promise { - return this.pairCompute.firstTokenPriceUSD(parent.address); + return this.pairComputeLoader.firstTokenPriceUSDLoader.load( + parent.address, + ); } @ResolveField() async secondTokenPriceUSD(@Parent() parent: PairModel): Promise { - return this.pairCompute.secondTokenPriceUSD(parent.address); + return this.pairComputeLoader.secondTokenPriceUSDLoader.load( + parent.address, + ); } @ResolveField() async secondTokenPrice(@Parent() parent: PairModel): Promise { - return this.pairCompute.secondTokenPrice(parent.address); + return this.pairComputeLoader.secondTokenPriceLoader.load( + parent.address, + ); } @ResolveField() async liquidityPoolTokenPriceUSD( @Parent() parent: PairModel, ): Promise { - return this.pairCompute.lpTokenPriceUSD(parent.address); + return this.pairComputeLoader.lpTokenPriceUSDLoader.load( + parent.address, + ); } @ResolveField() async firstTokenLockedValueUSD( @Parent() parent: PairModel, ): Promise { - return this.pairCompute.firstTokenLockedValueUSD(parent.address); + return this.pairComputeLoader.firstTokenLockedValueUSDLoader.load( + parent.address, + ); } @ResolveField() async secondTokenLockedValueUSD( @Parent() parent: PairModel, ): Promise { - return this.pairCompute.secondTokenLockedValueUSD(parent.address); + return this.pairComputeLoader.secondTokenLockedValueUSDLoader.load( + parent.address, + ); } @ResolveField() async lockedValueUSD(@Parent() parent: PairModel): Promise { - return this.pairCompute.lockedValueUSD(parent.address); + return this.pairComputeLoader.lockedValueUSDLoader.load(parent.address); } @ResolveField() async previous24hLockedValueUSD( @Parent() parent: PairModel, ): Promise { - return this.pairCompute.previous24hLockedValueUSD(parent.address); + return this.pairComputeLoader.previous24hLockedValueUSDLoader.load( + parent.address, + ); } @ResolveField() @@ -259,7 +278,9 @@ export class PairResolver { @ResolveField() async previous24hVolumeUSD(@Parent() parent: PairModel): Promise { - return this.pairCompute.previous24hVolumeUSD(parent.address); + return this.pairComputeLoader.previous24hVolumeUSDLoader.load( + parent.address, + ); } @ResolveField() @@ -269,42 +290,43 @@ export class PairResolver { @ResolveField() async previous24hFeesUSD(@Parent() parent: PairModel): Promise { - return this.pairCompute.previous24hFeesUSD(parent.address); + return this.pairComputeLoader.previous24hFeesUSDLoader.load( + parent.address, + ); } @ResolveField() async feesAPR(@Parent() parent: PairModel): Promise { - return this.pairCompute.feesAPR(parent.address); + return this.pairComputeLoader.feesAPRLoader.load(parent.address); } @ResolveField() async info(@Parent() parent: PairModel): Promise { - return this.pairAbi.pairInfoMetadata(parent.address); + return this.pairAbiLoader.infoMetadataLoader.load(parent.address); } @ResolveField() async totalFeePercent(@Parent() parent: PairModel): Promise { - return this.pairAbi.totalFeePercent(parent.address); + return this.pairAbiLoader.totalFeePercentLoader.load(parent.address); } @ResolveField() async specialFeePercent(@Parent() parent: PairModel): Promise { - return this.pairAbi.specialFeePercent(parent.address); + return this.pairAbiLoader.specialFeePercentLoader.load(parent.address); } @ResolveField() async feesCollectorCutPercentage( @Parent() parent: PairModel, ): Promise { - const fees = await this.pairAbi.feesCollectorCutPercentage( + return this.pairAbiLoader.feesCollectorCutPercentageLoader.load( parent.address, ); - return fees / constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS; } @ResolveField() async type(@Parent() parent: PairModel): Promise { - return this.pairCompute.type(parent.address); + return this.pairComputeLoader.typeLoader.load(parent.address); } @ResolveField() @@ -314,12 +336,12 @@ export class PairResolver { @ResolveField() async state(@Parent() parent: PairModel): Promise { - return this.pairAbi.state(parent.address); + return this.pairAbiLoader.stateLoader.load(parent.address); } @ResolveField() async feeState(@Parent() parent: PairModel): Promise { - return this.pairAbi.feeState(parent.address); + return this.pairAbiLoader.feeStateLoader.load(parent.address); } @ResolveField() @@ -365,22 +387,22 @@ export class PairResolver { @ResolveField() async hasFarms(@Parent() parent: PairModel): Promise { - return this.pairCompute.hasFarms(parent.address); + return this.pairComputeLoader.hasFarmsLoader.load(parent.address); } @ResolveField() async hasDualFarms(@Parent() parent: PairModel): Promise { - return this.pairCompute.hasDualFarms(parent.address); + return this.pairComputeLoader.hasDualFarmsLoader.load(parent.address); } @ResolveField() async tradesCount(@Parent() parent: PairModel): Promise { - return this.pairCompute.tradesCount(parent.address); + return this.pairComputeLoader.tradesCountLoader.load(parent.address); } @ResolveField() async deployedAt(@Parent() parent: PairModel): Promise { - return this.pairCompute.deployedAt(parent.address); + return this.pairComputeLoader.deployedAtLoader.load(parent.address); } @ResolveField(() => PairCompoundedAPRModel, { nullable: true }) diff --git a/src/modules/pair/services/pair.abi.loader.ts b/src/modules/pair/services/pair.abi.loader.ts new file mode 100644 index 000000000..6776ada18 --- /dev/null +++ b/src/modules/pair/services/pair.abi.loader.ts @@ -0,0 +1,123 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { PairAbiService } from './pair.abi.service'; +import DataLoader from 'dataloader'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { EsdtToken } from 'src/modules/tokens/models/esdtToken.model'; +import { PairService } from './pair.service'; +import { PairInfoModel } from '../models/pair-info.model'; +import { getAllKeys } from 'src/utils/get.many.utils'; +import { constantsConfig } from 'src/config'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class PairAbiLoader { + constructor( + private readonly pairAbi: PairAbiService, + private readonly pairService: PairService, + private readonly cacheService: CacheService, + ) {} + + public readonly firstTokenLoader = new DataLoader( + async (addresses: string[]) => { + return this.pairService.getAllFirstTokens(addresses); + }, + ); + + public readonly secondTokenLoader = new DataLoader( + async (addresses: string[]) => { + return this.pairService.getAllSecondTokens(addresses); + }, + ); + + public readonly liquidityPoolTokenLoader = new DataLoader< + string, + EsdtToken + >(async (addresses: string[]) => { + return this.pairService.getAllLpTokens(addresses); + }); + + public readonly infoMetadataLoader = new DataLoader( + async (addresses: string[]) => { + return getAllKeys( + this.cacheService, + addresses, + 'pair.pairInfoMetadata', + this.pairAbi.pairInfoMetadata.bind(this.pairAbi), + ); + }, + ); + + public readonly totalFeePercentLoader = new DataLoader( + async (addresses: string[]) => { + return getAllKeys( + this.cacheService, + addresses, + 'pair.totalFeePercent', + this.pairAbi.totalFeePercent.bind(this.pairAbi), + ); + }, + ); + + public readonly specialFeePercentLoader = new DataLoader( + async (addresses: string[]) => { + return getAllKeys( + this.cacheService, + addresses, + 'pair.specialFeePercent', + this.pairAbi.specialFeePercent.bind(this.pairAbi), + ); + }, + ); + + public readonly feesCollectorCutPercentageLoader = new DataLoader< + string, + number + >(async (addresses: string[]) => { + const percentages = await getAllKeys( + this.cacheService, + addresses, + 'pair.feesCollectorCutPercentage', + this.pairAbi.feesCollectorCutPercentage.bind(this.pairAbi), + ); + + return percentages.map( + (percentage) => + percentage / constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS, + ); + }); + + public readonly stateLoader = new DataLoader( + async (addresses: string[]) => { + return getAllKeys( + this.cacheService, + addresses, + 'pair.state', + this.pairAbi.state.bind(this.pairAbi), + ); + }, + ); + + public readonly feeStateLoader = new DataLoader( + async (addresses: string[]) => { + return getAllKeys( + this.cacheService, + addresses, + 'pair.feeState', + this.pairAbi.feeState.bind(this.pairAbi), + ); + }, + ); + + public readonly initialLiquidityAdderLoader = new DataLoader< + string, + string + >(async (addresses: string[]) => { + return getAllKeys( + this.cacheService, + addresses, + 'pair.initialLiquidityAdder', + this.pairAbi.initialLiquidityAdder.bind(this.pairAbi), + ); + }); +} diff --git a/src/modules/pair/services/pair.compute.loader.ts b/src/modules/pair/services/pair.compute.loader.ts new file mode 100644 index 000000000..d95ac8657 --- /dev/null +++ b/src/modules/pair/services/pair.compute.loader.ts @@ -0,0 +1,205 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { PairComputeService } from './pair.compute.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { getAllKeys } from 'src/utils/get.many.utils'; +import DataLoader from 'dataloader'; + +@Injectable({ + scope: Scope.REQUEST, +}) +export class PairComputeLoader { + constructor( + private readonly pairCompute: PairComputeService, + private readonly cacheService: CacheService, + ) {} + + public readonly firstTokenPriceLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.firstTokenPrice', + this.pairCompute.firstTokenPrice.bind(this.pairCompute), + ); + }, + ); + + public readonly secondTokenPriceLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.secondTokenPrice', + this.pairCompute.secondTokenPrice.bind(this.pairCompute), + ); + }, + ); + + public readonly firstTokenPriceUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.firstTokenPriceUSD', + this.pairCompute.firstTokenPriceUSD.bind(this.pairCompute), + ); + }, + ); + + public readonly secondTokenPriceUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.secondTokenPriceUSD', + this.pairCompute.secondTokenPriceUSD.bind(this.pairCompute), + ); + }, + ); + + public readonly lpTokenPriceUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.lpTokenPriceUSD', + this.pairCompute.lpTokenPriceUSD.bind(this.pairCompute), + ); + }, + ); + + public readonly firstTokenLockedValueUSDLoader = new DataLoader< + string, + string + >(async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.firstTokenLockedValueUSD', + this.pairCompute.firstTokenLockedValueUSD.bind(this.pairCompute), + ); + }); + + public readonly secondTokenLockedValueUSDLoader = new DataLoader< + string, + string + >(async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.secondTokenLockedValueUSD', + this.pairCompute.secondTokenLockedValueUSD.bind(this.pairCompute), + ); + }); + + public readonly lockedValueUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.lockedValueUSD', + this.pairCompute.lockedValueUSD.bind(this.pairCompute), + ); + }, + ); + + public readonly previous24hLockedValueUSDLoader = new DataLoader< + string, + string + >(async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.previous24hLockedValueUSD', + this.pairCompute.previous24hLockedValueUSD.bind(this.pairCompute), + ); + }); + + public readonly previous24hVolumeUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.previous24hVolumeUSD', + this.pairCompute.previous24hVolumeUSD.bind(this.pairCompute), + ); + }, + ); + + public readonly previous24hFeesUSDLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.previous24hFeesUSD', + this.pairCompute.previous24hFeesUSD.bind(this.pairCompute), + ); + }, + ); + + public readonly feesAPRLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.feesAPR', + this.pairCompute.feesAPR.bind(this.pairCompute), + ); + }, + ); + + public readonly typeLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.type', + this.pairCompute.type.bind(this.pairCompute), + ); + }, + ); + + public readonly hasFarmsLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.hasFarms', + this.pairCompute.hasFarms.bind(this.pairCompute), + ); + }, + ); + + public readonly hasDualFarmsLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.hasDualFarms', + this.pairCompute.hasDualFarms.bind(this.pairCompute), + ); + }, + ); + + public readonly tradesCountLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.tradesCount', + this.pairCompute.tradesCount.bind(this.pairCompute), + ); + }, + ); + + public readonly deployedAtLoader = new DataLoader( + async (addresses: string[]) => { + return await getAllKeys( + this.cacheService, + addresses, + 'pair.deployedAt', + this.pairCompute.deployedAt.bind(this.pairCompute), + ); + }, + ); +} diff --git a/src/modules/pair/services/pair.service.ts b/src/modules/pair/services/pair.service.ts index 12dda0baa..38509f429 100644 --- a/src/modules/pair/services/pair.service.ts +++ b/src/modules/pair/services/pair.service.ts @@ -19,6 +19,7 @@ import { ContextGetterService } from 'src/services/context/context.getter.servic import { PairComputeService } from './pair.compute.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; import { TokenService } from 'src/modules/tokens/services/token.service'; +import { getAllKeys } from 'src/utils/get.many.utils'; @Injectable() export class PairService { @@ -39,11 +40,33 @@ export class PairService { return await this.tokenService.tokenMetadata(firstTokenID); } + async getAllFirstTokens(pairAddresses: string[]): Promise { + const tokenIDs = await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.firstTokenID', + this.pairAbi.firstTokenID.bind(this.pairAbi), + ); + + return this.tokenService.getAllTokensMetadata(tokenIDs); + } + async getSecondToken(pairAddress: string): Promise { const secondTokenID = await this.pairAbi.secondTokenID(pairAddress); return await this.tokenService.tokenMetadata(secondTokenID); } + async getAllSecondTokens(pairAddresses: string[]): Promise { + const tokenIDs = await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.secondTokenID', + this.pairAbi.secondTokenID.bind(this.pairAbi), + ); + + return this.tokenService.getAllTokensMetadata(tokenIDs); + } + async getLpToken(pairAddress: string): Promise { const lpTokenID = await this.pairAbi.lpTokenID(pairAddress); return lpTokenID === undefined @@ -51,6 +74,17 @@ export class PairService { : await this.tokenService.tokenMetadata(lpTokenID); } + async getAllLpTokens(pairAddresses: string[]): Promise { + const tokenIDs = await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.lpTokenID', + this.pairAbi.lpTokenID.bind(this.pairAbi), + ); + + return this.tokenService.getAllTokensMetadata(tokenIDs); + } + async getAmountOut( pairAddress: string, tokenInID: string, diff --git a/src/utils/get.many.utils.ts b/src/utils/get.many.utils.ts index 4dab13185..2150ba771 100644 --- a/src/utils/get.many.utils.ts +++ b/src/utils/get.many.utils.ts @@ -34,11 +34,11 @@ async function getMany( export async function getAllKeys( cacheService: CacheService, - tokenIDs: string[], + rawKeys: string[], baseKey: string, getterMethod: (address: string) => Promise, ): Promise { - const keys = tokenIDs.map((tokenID) => `${baseKey}.${tokenID}`); + const keys = rawKeys.map((tokenID) => `${baseKey}.${tokenID}`); const values = await getMany(cacheService, keys); const missingIndexes: number[] = []; @@ -49,7 +49,7 @@ export async function getAllKeys( }); for (const missingIndex of missingIndexes) { - const tokenID = await getterMethod(tokenIDs[missingIndex]); + const tokenID = await getterMethod(rawKeys[missingIndex]); values[missingIndex] = tokenID; } return values; From 347e5facba7d0082bae6826f2adb529b17745966 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 16 Aug 2024 16:29:21 +0300 Subject: [PATCH 265/313] MEX-511: fix undefined value in getMany method Signed-off-by: Claudiu Lataretu --- src/modules/pair/models/pair.model.ts | 2 +- src/utils/get.many.utils.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index de9e01b6e..d97021c30 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -201,7 +201,7 @@ export class PairModel { @Field() tradesCount: number; - @Field({ nullable: true }) + @Field(() => Int, { nullable: true }) deployedAt: number; @Field(() => PairCompoundedAPRModel, { nullable: true }) diff --git a/src/utils/get.many.utils.ts b/src/utils/get.many.utils.ts index 2150ba771..98449e6ac 100644 --- a/src/utils/get.many.utils.ts +++ b/src/utils/get.many.utils.ts @@ -1,4 +1,5 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { parseCachedNullOrUndefined } from './cache.utils'; async function getMany( cacheService: CacheService, @@ -19,14 +20,16 @@ async function getMany( } if (missingKeys.length === 0) { - return values; + return values.map((value) => parseCachedNullOrUndefined(value)); } const remoteValues = await cacheService.getManyRemote(missingKeys); for (const [index, missingIndex] of missingIndexes.entries()) { const remoteValue = remoteValues[index]; - values[missingIndex] = remoteValue ? remoteValue : undefined; + values[missingIndex] = remoteValue + ? parseCachedNullOrUndefined(remoteValue) + : undefined; } return values; From d85eb4811467ff0d150000c740aeecfb713ee9ce Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 15 Aug 2024 22:39:08 +0300 Subject: [PATCH 266/313] MEX-512: remove unnecessary caching from farm compute Signed-off-by: Claudiu Lataretu --- src/modules/farm/base-module/farm.resolver.ts | 2 +- .../services/farm.compute.loader.ts | 11 ----- .../services/farm.compute.service.ts | 40 +------------------ .../services/farm.setter.service.ts | 12 ------ .../farm/base-module/services/interfaces.ts | 1 - .../farm/mocks/farm.compute.service.mock.ts | 3 -- src/services/caching/cache.ttl.info.ts | 2 +- .../crons/farm.cache.warmer.service.ts | 35 ++++++---------- 8 files changed, 16 insertions(+), 90 deletions(-) diff --git a/src/modules/farm/base-module/farm.resolver.ts b/src/modules/farm/base-module/farm.resolver.ts index a1b78894f..134d97913 100644 --- a/src/modules/farm/base-module/farm.resolver.ts +++ b/src/modules/farm/base-module/farm.resolver.ts @@ -69,7 +69,7 @@ export class FarmResolver { @ResolveField() async farmTokenPriceUSD(@Parent() parent: BaseFarmModel): Promise { - return this.farmComputeLoader.farmTokenPriceUSDLoader.load( + return this.farmComputeLoader.farmingTokenPriceUSDLoader.load( parent.address, ); } diff --git a/src/modules/farm/base-module/services/farm.compute.loader.ts b/src/modules/farm/base-module/services/farm.compute.loader.ts index 8cce1139a..9bc565fb3 100644 --- a/src/modules/farm/base-module/services/farm.compute.loader.ts +++ b/src/modules/farm/base-module/services/farm.compute.loader.ts @@ -35,17 +35,6 @@ export class FarmComputeLoader { }, ); - public readonly farmTokenPriceUSDLoader = new DataLoader( - async (addresses: string[]) => { - return await getAllKeys( - this.cacheService, - addresses, - 'farm.farmTokenPriceUSD', - this.farmCompute.farmTokenPriceUSD.bind(this.farmCompute), - ); - }, - ); - public readonly farmingTokenPriceUSDLoader = new DataLoader( async (addresses: string[]) => { return await getAllKeys( diff --git a/src/modules/farm/base-module/services/farm.compute.service.ts b/src/modules/farm/base-module/services/farm.compute.service.ts index 718d61b4b..e8f074c6c 100644 --- a/src/modules/farm/base-module/services/farm.compute.service.ts +++ b/src/modules/farm/base-module/services/farm.compute.service.ts @@ -43,43 +43,9 @@ export abstract class FarmComputeService implements IFarmComputeService { return '0'; } - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'farm', - remoteTtl: CacheTtlInfo.Price.remoteTtl, - localTtl: CacheTtlInfo.Price.localTtl, - }) async farmedTokenPriceUSD(farmAddress: string): Promise { - return await this.computeFarmedTokenPriceUSD(farmAddress); - } - - async computeFarmedTokenPriceUSD(farmAddress: string): Promise { const farmedTokenID = await this.farmAbi.farmedTokenID(farmAddress); - if (scAddress.has(farmedTokenID)) { - const tokenPriceUSD = - await this.tokenCompute.computeTokenPriceDerivedUSD( - farmedTokenID, - ); - return tokenPriceUSD; - } - - return await this.tokenCompute.computeTokenPriceDerivedUSD( - farmedTokenID, - ); - } - - @ErrorLoggerAsync({ - logArgs: true, - }) - @GetOrSetCache({ - baseKey: 'farm', - remoteTtl: CacheTtlInfo.Price.remoteTtl, - localTtl: CacheTtlInfo.Price.localTtl, - }) - async farmTokenPriceUSD(farmAddress: string): Promise { - return this.farmingTokenPriceUSD(farmAddress); + return this.tokenCompute.tokenPriceDerivedUSD(farmedTokenID); } @ErrorLoggerAsync({ @@ -97,9 +63,7 @@ export abstract class FarmComputeService implements IFarmComputeService { async computeFarmingTokenPriceUSD(farmAddress: string): Promise { const farmingTokenID = await this.farmAbi.farmingTokenID(farmAddress); if (scAddress.has(farmingTokenID)) { - return await this.tokenCompute.computeTokenPriceDerivedUSD( - farmingTokenID, - ); + return await this.tokenCompute.tokenPriceDerivedUSD(farmingTokenID); } const pairAddress = await this.pairService.getPairAddressByLpTokenID( diff --git a/src/modules/farm/base-module/services/farm.setter.service.ts b/src/modules/farm/base-module/services/farm.setter.service.ts index 901a5468c..4fb84caa9 100644 --- a/src/modules/farm/base-module/services/farm.setter.service.ts +++ b/src/modules/farm/base-module/services/farm.setter.service.ts @@ -212,18 +212,6 @@ export abstract class FarmSetterService extends GenericSetterService { ); } - async setFarmedTokenPriceUSD( - farmAddress: string, - value: string, - ): Promise { - return await this.setData( - this.getCacheKey('farmedTokenPriceUSD', farmAddress), - value, - CacheTtlInfo.Price.remoteTtl, - CacheTtlInfo.Price.localTtl, - ); - } - async setFarmingTokenPriceUSD( farmAddress: string, value: string, diff --git a/src/modules/farm/base-module/services/interfaces.ts b/src/modules/farm/base-module/services/interfaces.ts index fd6a344db..af76b1551 100644 --- a/src/modules/farm/base-module/services/interfaces.ts +++ b/src/modules/farm/base-module/services/interfaces.ts @@ -29,7 +29,6 @@ export interface IFarmAbiService { export interface IFarmComputeService { farmLockedValueUSD(farmAddress: string): Promise; farmedTokenPriceUSD(farmAddress: string): Promise; - farmTokenPriceUSD(farmAddress: string): Promise; farmingTokenPriceUSD(farmAddress: string): Promise; computeMintedRewards(farmAddress: string): Promise; } diff --git a/src/modules/farm/mocks/farm.compute.service.mock.ts b/src/modules/farm/mocks/farm.compute.service.mock.ts index 56c9fddba..b083b2827 100644 --- a/src/modules/farm/mocks/farm.compute.service.mock.ts +++ b/src/modules/farm/mocks/farm.compute.service.mock.ts @@ -9,9 +9,6 @@ export class FarmComputeServiceMock implements IFarmComputeService { async farmedTokenPriceUSD(farmAddress: string): Promise { return '100'; } - async farmTokenPriceUSD(farmAddress: string): Promise { - return '200'; - } async farmingTokenPriceUSD(farmAddress: string): Promise { return '200'; } diff --git a/src/services/caching/cache.ttl.info.ts b/src/services/caching/cache.ttl.info.ts index e9e8a01f5..095b86880 100644 --- a/src/services/caching/cache.ttl.info.ts +++ b/src/services/caching/cache.ttl.info.ts @@ -31,7 +31,7 @@ export class CacheTtlInfo { static ContractBalance: CacheTtlInfo = new CacheTtlInfo( Constants.oneMinute(), - Constants.oneSecond() * 30, + Constants.oneSecond() * 45, ); static Price: CacheTtlInfo = new CacheTtlInfo( diff --git a/src/services/crons/farm.cache.warmer.service.ts b/src/services/crons/farm.cache.warmer.service.ts index b0b6510ea..a4499edce 100644 --- a/src/services/crons/farm.cache.warmer.service.ts +++ b/src/services/crons/farm.cache.warmer.service.ts @@ -27,10 +27,10 @@ export class FarmCacheWarmerService { @Inject(PUB_SUB) private pubSub: RedisPubSub, ) {} - @Cron(CronExpression.EVERY_HOUR) + @Cron(CronExpression.EVERY_MINUTE) async cacheFarmsTokens(): Promise { const farmsAddress: string[] = farmsAddresses(); - const promises = farmsAddress.map(async (farmAddress) => { + for (const farmAddress of farmsAddress) { const [farmTokenID, farmingTokenID, farmedTokenID] = await Promise.all([ this.farmAbiFactory @@ -57,9 +57,7 @@ export class FarmCacheWarmerService { ]); this.invalidatedKeys.push(cacheKeys); await this.deleteCacheKeys(); - }); - - await Promise.all(promises); + } } @Cron(CronExpression.EVERY_30_SECONDS) @@ -236,25 +234,16 @@ export class FarmCacheWarmerService { @Cron(CronExpression.EVERY_30_SECONDS) async cacheFarmTokensPrices(): Promise { for (const farmAddress of farmsAddresses()) { - const [ - farmedTokenPriceUSD, - farmingTokenPriceUSD, - totalValueLockedUSD, - ] = await Promise.all([ - this.farmComputeFactory - .useCompute(farmAddress) - .computeFarmedTokenPriceUSD(farmAddress), - this.farmComputeFactory - .useCompute(farmAddress) - .computeFarmingTokenPriceUSD(farmAddress), - this.farmComputeFactory - .useCompute(farmAddress) - .computeFarmLockedValueUSD(farmAddress), - ]); + const [farmingTokenPriceUSD, totalValueLockedUSD] = + await Promise.all([ + this.farmComputeFactory + .useCompute(farmAddress) + .computeFarmingTokenPriceUSD(farmAddress), + this.farmComputeFactory + .useCompute(farmAddress) + .computeFarmLockedValueUSD(farmAddress), + ]); const cacheKeys = await Promise.all([ - this.farmSetterFactory - .useSetter(farmAddress) - .setFarmedTokenPriceUSD(farmAddress, farmedTokenPriceUSD), this.farmSetterFactory .useSetter(farmAddress) .setFarmingTokenPriceUSD(farmAddress, farmingTokenPriceUSD), From 99c4ec77e1c86bb52de8fa03bc15a3aea59bff5b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 16 Aug 2024 15:15:58 +0300 Subject: [PATCH 267/313] MEX-512: improvements on tokens caching - added cron job to warm up the tokens prices - updated tokens key used in setter service Signed-off-by: Claudiu Lataretu --- src/modules/tokens/services/token.service.ts | 4 +- .../tokens/services/token.setter.service.ts | 22 +++---- src/services/caching/cache.ttl.info.ts | 2 +- .../crons/tokens.cache.warmer.service.ts | 64 ++++++++++++++++--- 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index 2888d03aa..511063c9d 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -107,8 +107,8 @@ export class TokenService { }) @GetOrSetCache({ baseKey: 'token', - remoteTtl: CacheTtlInfo.ContractState.remoteTtl, - localTtl: CacheTtlInfo.ContractState.localTtl, + remoteTtl: CacheTtlInfo.Token.remoteTtl, + localTtl: CacheTtlInfo.Token.localTtl, }) async getEsdtTokenType(tokenID: string): Promise { return await this.tokenRepository.getTokenType(tokenID); diff --git a/src/modules/tokens/services/token.setter.service.ts b/src/modules/tokens/services/token.setter.service.ts index 49adf41fb..3c815dcd2 100644 --- a/src/modules/tokens/services/token.setter.service.ts +++ b/src/modules/tokens/services/token.setter.service.ts @@ -42,7 +42,7 @@ export class TokenSetterService extends GenericSetterService { async setEsdtTokenType(tokenID: string, type: string): Promise { return await this.setData( - this.getTokenCacheKey(tokenID, 'type'), + this.getTokenCacheKey(tokenID, 'getEsdtTokenType'), type, CacheTtlInfo.Token.remoteTtl, CacheTtlInfo.Token.localTtl, @@ -51,7 +51,7 @@ export class TokenSetterService extends GenericSetterService { async setDerivedEGLD(tokenID: string, value: string): Promise { return await this.setData( - this.getTokenCacheKey(tokenID, 'derivedEGLD'), + this.getTokenCacheKey(tokenID, 'tokenPriceDerivedEGLD'), value, CacheTtlInfo.Price.remoteTtl, CacheTtlInfo.Price.localTtl, @@ -60,7 +60,7 @@ export class TokenSetterService extends GenericSetterService { async setDerivedUSD(tokenID: string, value: string): Promise { return await this.setData( - this.getTokenCacheKey(tokenID, 'derivedUSD'), + this.getTokenCacheKey(tokenID, 'tokenPriceDerivedUSD'), value, CacheTtlInfo.Price.remoteTtl, CacheTtlInfo.Price.localTtl, @@ -72,7 +72,7 @@ export class TokenSetterService extends GenericSetterService { value: { current: string; previous: string }, ): Promise { return await this.setData( - `token.tokenLast2DaysVolumeUSD.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenLast2DaysVolumeUSD'), value, CacheTtlInfo.TokenAnalytics.remoteTtl, CacheTtlInfo.TokenAnalytics.localTtl, @@ -81,7 +81,7 @@ export class TokenSetterService extends GenericSetterService { async setPricePrevious24h(tokenID: string, value: string): Promise { return await this.setData( - `token.tokenPrevious24hPrice.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenPrevious24hPrice'), value, CacheTtlInfo.TokenAnalytics.remoteTtl, CacheTtlInfo.TokenAnalytics.localTtl, @@ -90,7 +90,7 @@ export class TokenSetterService extends GenericSetterService { async setPricePrevious7d(tokenID: string, value: string): Promise { return await this.setData( - `token.tokenPrevious7dPrice.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenPrevious7dPrice'), value, CacheTtlInfo.TokenAnalytics.remoteTtl, CacheTtlInfo.TokenAnalytics.localTtl, @@ -99,7 +99,7 @@ export class TokenSetterService extends GenericSetterService { async setLiquidityUSD(tokenID: string, value: string): Promise { return await this.setData( - `token.tokenLiquidityUSD.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenLiquidityUSD'), value, CacheTtlInfo.TokenAnalytics.remoteTtl, CacheTtlInfo.TokenAnalytics.localTtl, @@ -130,7 +130,7 @@ export class TokenSetterService extends GenericSetterService { async setTrendingScore(tokenID: string, value: string): Promise { return await this.setData( - `token.tokenTrendingScore.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenTrendingScore'), value, CacheTtlInfo.Token.remoteTtl, CacheTtlInfo.Token.localTtl, @@ -139,7 +139,7 @@ export class TokenSetterService extends GenericSetterService { async setMetadata(tokenID: string, value: EsdtToken): Promise { return await this.setData( - `token.tokenMetadata.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenMetadata'), value, CacheTtlInfo.Token.remoteTtl, CacheTtlInfo.Token.localTtl, @@ -148,7 +148,7 @@ export class TokenSetterService extends GenericSetterService { async setCreatedAt(tokenID: string, value: string): Promise { return await this.setData( - `token.tokenCreatedAt.${tokenID}`, + this.getTokenCacheKey(tokenID, 'tokenCreatedAt'), value, CacheTtlInfo.Token.remoteTtl, CacheTtlInfo.Token.localTtl, @@ -156,6 +156,6 @@ export class TokenSetterService extends GenericSetterService { } private getTokenCacheKey(tokenID: string, ...args: any): string { - return generateCacheKeyFromParams('token', tokenID, args); + return generateCacheKeyFromParams('token', ...args, tokenID); } } diff --git a/src/services/caching/cache.ttl.info.ts b/src/services/caching/cache.ttl.info.ts index 095b86880..b6f0a6d24 100644 --- a/src/services/caching/cache.ttl.info.ts +++ b/src/services/caching/cache.ttl.info.ts @@ -11,7 +11,7 @@ export class CacheTtlInfo { static Token: CacheTtlInfo = new CacheTtlInfo( Constants.oneMinute() * 10, - Constants.oneMinute() * 5, + Constants.oneMinute() * 7, ); static TokenAnalytics: CacheTtlInfo = new CacheTtlInfo( diff --git a/src/services/crons/tokens.cache.warmer.service.ts b/src/services/crons/tokens.cache.warmer.service.ts index a0108098d..6786a9935 100644 --- a/src/services/crons/tokens.cache.warmer.service.ts +++ b/src/services/crons/tokens.cache.warmer.service.ts @@ -10,6 +10,7 @@ import { PerformanceProfiler } from 'src/utils/performance.profiler'; import { TokenComputeService } from 'src/modules/tokens/services/token.compute.service'; import { TokenSetterService } from 'src/modules/tokens/services/token.setter.service'; import moment from 'moment'; +import { TokenRepositoryService } from 'src/modules/tokens/services/token.repository.service'; @Injectable() export class TokensCacheWarmerService { @@ -17,6 +18,7 @@ export class TokensCacheWarmerService { private readonly tokenService: TokenService, private readonly tokenComputeService: TokenComputeService, private readonly tokenSetterService: TokenSetterService, + private readonly tokenRepository: TokenRepositoryService, @Inject(PUB_SUB) private pubSub: RedisPubSub, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} @@ -30,24 +32,66 @@ export class TokensCacheWarmerService { const tokenIDs = await this.tokenService.getUniqueTokenIDs(false); const profiler = new PerformanceProfiler(); - const cachedKeys = []; for (const tokenID of tokenIDs) { const token = await this.tokenService.tokenMetadataRaw(tokenID); + const tokenType = await this.tokenRepository.getTokenType(tokenID); - const cachedKey = await this.tokenSetterService.setMetadata( - tokenID, - token, - ); + const cachedKeys = await Promise.all([ + this.tokenSetterService.setMetadata(tokenID, token), + this.tokenSetterService.setEsdtTokenType(tokenID, tokenType), + ]); - cachedKeys.push(cachedKey); + await this.deleteCacheKeys(cachedKeys); } - await this.deleteCacheKeys(cachedKeys); - profiler.stop(); this.logger.info( `Finish refresh tokens metadata in ${profiler.duration}`, + { + context: 'CacheTokens', + }, + ); + } + + @Cron(CronExpression.EVERY_30_SECONDS) + @Lock({ name: 'cacheTokensPrices', verbose: true }) + async cacheTokensPrices(): Promise { + this.logger.info('Start refresh tokens prices', { + context: 'CacheTokens', + }); + + const tokensIDs = await this.tokenService.getUniqueTokenIDs(false); + const profiler = new PerformanceProfiler(); + + for (const tokenID of tokensIDs) { + const priceDerivedEGLD = + await this.tokenComputeService.computeTokenPriceDerivedEGLD( + tokenID, + [], + ); + const priceDerivedUSD = + await this.tokenComputeService.computeTokenPriceDerivedUSD( + tokenID, + ); + + const cachedKeys = await Promise.all([ + this.tokenSetterService.setDerivedEGLD( + tokenID, + priceDerivedEGLD, + ), + this.tokenSetterService.setDerivedUSD(tokenID, priceDerivedUSD), + ]); + + await this.deleteCacheKeys(cachedKeys); + } + + profiler.stop(); + this.logger.info( + `Finish refresh tokens prices in ${profiler.duration}`, + { + context: 'CacheTokens', + }, ); } @@ -107,7 +151,9 @@ export class TokensCacheWarmerService { await this.cacheTokensTrendingScore(tokens); profiler.stop(); - this.logger.info(`Finish refresh tokens data in ${profiler.duration}`); + this.logger.info(`Finish refresh tokens data in ${profiler.duration}`, { + context: 'CacheTokens', + }); } private async cacheTokensSwapsCount(): Promise { From 1facee92c126f29b65d44db678e727dc6647c6c8 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 16 Aug 2024 20:32:42 +0300 Subject: [PATCH 268/313] MEX-512: remove unnecessary cache from router service Signed-off-by: Claudiu Lataretu --- src/modules/router/services/router.service.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/modules/router/services/router.service.ts b/src/modules/router/services/router.service.ts index bbc9611d1..224150398 100644 --- a/src/modules/router/services/router.service.ts +++ b/src/modules/router/services/router.service.ts @@ -9,16 +9,16 @@ import { PairSortingArgs, PairsFilter, } from '../models/filter.args'; -import { Constants } from '@multiversx/sdk-nestjs-common'; import { PairAbiService } from 'src/modules/pair/services/pair.abi.service'; import { RouterAbiService } from './router.abi.service'; -import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { PairComputeService } from 'src/modules/pair/services/pair.compute.service'; import BigNumber from 'bignumber.js'; import { CollectionType } from 'src/modules/common/collection.type'; import { PairsMetadataBuilder } from 'src/modules/pair/services/pair.metadata.builder'; import { PairFilteringService } from 'src/modules/pair/services/pair.filtering.service'; import { SortingOrder } from 'src/modules/common/page.data'; +import { getAllKeys } from 'src/utils/get.many.utils'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class RouterService { @@ -27,6 +27,7 @@ export class RouterService { private readonly routerAbi: RouterAbiService, private readonly pairCompute: PairComputeService, private readonly pairFilteringService: PairFilteringService, + private readonly cacheService: CacheService, ) {} async getFactory(): Promise { @@ -156,11 +157,6 @@ export class RouterService { return pairsMetadata; } - @GetOrSetCache({ - baseKey: 'router', - remoteTtl: Constants.oneSecond() * 30, - localTtl: Constants.oneSecond() * 6, - }) private async pairsByIssuedLpToken( pairsMetadata: PairMetadata[], ): Promise { @@ -170,10 +166,12 @@ export class RouterService { private async filterPairsByIssuedLpTokenRaw( pairsMetadata: PairMetadata[], ): Promise { - const promises = pairsMetadata.map((pairMetadata) => - this.pairAbi.lpTokenID(pairMetadata.address), + const lpTokensIDs = await getAllKeys( + this.cacheService, + pairsMetadata.map((pair) => pair.address), + 'pair.lpTokenID', + this.pairAbi.lpTokenID.bind(this.pairAbi), ); - const lpTokensIDs = await Promise.all(promises); const filteredPairsMetadata = []; for (let index = 0; index < lpTokensIDs.length; index++) { From 870af03e4da92af4ae519839b775744454227bd4 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 14:18:39 +0300 Subject: [PATCH 269/313] MEX-512: add more info for pair cache warmer Signed-off-by: Claudiu Lataretu --- src/services/caching/cache.ttl.info.ts | 4 ++-- .../crons/pair.cache.warmer.service.ts | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/services/caching/cache.ttl.info.ts b/src/services/caching/cache.ttl.info.ts index b6f0a6d24..992963dc5 100644 --- a/src/services/caching/cache.ttl.info.ts +++ b/src/services/caching/cache.ttl.info.ts @@ -35,8 +35,8 @@ export class CacheTtlInfo { ); static Price: CacheTtlInfo = new CacheTtlInfo( - Constants.oneMinute(), - Constants.oneSecond() * 45, + Constants.oneMinute() * 2, + Constants.oneMinute() * 1, ); static Analytics: CacheTtlInfo = new CacheTtlInfo( diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index d88a2c602..4b23a1be6 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -69,6 +69,10 @@ export class PairCacheWarmerService { return; } + this.logger.info('Start refresh cached pairs analytics', { + context: 'CachePairs', + }); + const pairsAddresses = await this.routerAbi.pairsAddress(); const time = '24h'; for (const pairAddress of pairsAddresses) { @@ -123,11 +127,19 @@ export class PairCacheWarmerService { ]); await this.deleteCacheKeys(cachedKeys); } + + this.logger.info('Finished refresh cached pairs analytics', { + context: 'CachePairs', + }); } @Cron(CronExpression.EVERY_MINUTE) @Lock({ name: 'cachePairsInfo', verbose: true }) async cachePairsInfo(): Promise { + this.logger.info('Start refresh cached pairs info', { + context: 'CachePairs', + }); + const pairsAddresses = await this.routerAbi.pairsAddress(); for (const pairAddress of pairsAddresses) { @@ -218,11 +230,19 @@ export class PairCacheWarmerService { ]); await this.deleteCacheKeys(cachedKeys); } + + this.logger.info('Finished refresh cached pairs info', { + context: 'CachePairs', + }); } @Cron('*/12 * * * * *') // Update prices and reserves every 12 seconds @Lock({ name: 'cachePairTokenPrices', verbose: true }) async cacheTokenPrices(): Promise { + this.logger.info('Start refresh cached pairs prices', { + context: 'CachePairs', + }); + const pairsMetadata = await this.routerAbi.pairsMetadata(); const invalidatedKeys = []; for (const pairAddress of pairsMetadata) { @@ -301,6 +321,10 @@ export class PairCacheWarmerService { invalidatedKeys.push(cachedKeys); } await this.deleteCacheKeys(invalidatedKeys); + + this.logger.info('Finished refresh cached pairs prices', { + context: 'CachePairs', + }); } private async deleteCacheKeys(invalidatedKeys: string[]) { From 0c9bf9942ce6daf50f730243d0390a39b2cbbfcd Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 21 Aug 2024 12:15:58 +0300 Subject: [PATCH 270/313] MEX-512: improve refresh pair prices cron job Signed-off-by: Claudiu Lataretu --- .../crons/pair.cache.warmer.service.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index 4b23a1be6..b0a79e2e4 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -14,6 +14,7 @@ import BigNumber from 'bignumber.js'; import { constantsConfig } from 'src/config'; import { Lock } from '@multiversx/sdk-nestjs-common'; import { Logger } from 'winston'; +import { PerformanceProfiler } from 'src/utils/performance.profiler'; @Injectable() export class PairCacheWarmerService { @@ -242,9 +243,10 @@ export class PairCacheWarmerService { this.logger.info('Start refresh cached pairs prices', { context: 'CachePairs', }); + const performance = new PerformanceProfiler('cacheTokenPrices'); const pairsMetadata = await this.routerAbi.pairsMetadata(); - const invalidatedKeys = []; + for (const pairAddress of pairsMetadata) { const pairInfo = await this.pairAbi.getPairInfoMetadataRaw( pairAddress.address, @@ -268,7 +270,7 @@ export class PairCacheWarmerService { pairInfo, ), ]); - invalidatedKeys.push(cachedKeys); + await this.deleteCacheKeys(cachedKeys); } for (const pairMetadata of pairsMetadata) { @@ -318,13 +320,19 @@ export class PairCacheWarmerService { lpTokenPriceUSD, ), ]); - invalidatedKeys.push(cachedKeys); + await this.deleteCacheKeys(cachedKeys); } - await this.deleteCacheKeys(invalidatedKeys); - this.logger.info('Finished refresh cached pairs prices', { - context: 'CachePairs', - }); + performance.stop(); + + this.logger.info( + `Finished refresh cached pairs prices in ${ + performance.duration / 1000 + }s`, + { + context: 'CachePairs', + }, + ); } private async deleteCacheKeys(invalidatedKeys: string[]) { From 3501b7cfe811fe77a45e521f69bd406e209372a6 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 21 Aug 2024 12:37:24 +0300 Subject: [PATCH 271/313] MEX-512: increase cahe ttl for contract balance Signed-off-by: Claudiu Lataretu --- src/services/caching/cache.ttl.info.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/caching/cache.ttl.info.ts b/src/services/caching/cache.ttl.info.ts index 992963dc5..f65a63a9d 100644 --- a/src/services/caching/cache.ttl.info.ts +++ b/src/services/caching/cache.ttl.info.ts @@ -30,8 +30,8 @@ export class CacheTtlInfo { ); static ContractBalance: CacheTtlInfo = new CacheTtlInfo( - Constants.oneMinute(), - Constants.oneSecond() * 45, + Constants.oneMinute() * 2, + Constants.oneMinute() * 1, ); static Price: CacheTtlInfo = new CacheTtlInfo( From 04bb7ef029566d85cb7ae3048031d88306bb555e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 21 Aug 2024 12:43:12 +0300 Subject: [PATCH 272/313] MEX-512: cache warm pair locked value USD Signed-off-by: Claudiu Lataretu --- .../crons/pair.cache.warmer.service.ts | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index b0a79e2e4..9f52a6e6b 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -140,6 +140,7 @@ export class PairCacheWarmerService { this.logger.info('Start refresh cached pairs info', { context: 'CachePairs', }); + const performance = new PerformanceProfiler('cachePairsInfo'); const pairsAddresses = await this.routerAbi.pairsAddress(); @@ -180,6 +181,11 @@ export class PairCacheWarmerService { this.pairAbi.getTrustedSwapPairsRaw(pairAddress), ]); + const lockedValueUSD = + await this.pairComputeService.computeLockedValueUSD( + pairAddress, + ); + const cachedKeys = await Promise.all([ this.pairSetterService.setFeesAPR(pairAddress, feesAPR), this.pairSetterService.setState(pairAddress, state), @@ -228,13 +234,24 @@ export class PairCacheWarmerService { pairAddress, trustedSwapPairs, ), + this.pairSetterService.setLockedValueUSD( + pairAddress, + lockedValueUSD.toFixed(), + ), ]); await this.deleteCacheKeys(cachedKeys); } - this.logger.info('Finished refresh cached pairs info', { - context: 'CachePairs', - }); + performance.stop(); + + this.logger.info( + `Finished refresh cached pairs info in ${ + performance.duration / 1000 + }s`, + { + context: 'CachePairs', + }, + ); } @Cron('*/12 * * * * *') // Update prices and reserves every 12 seconds From c292cf8db80a847b98fb395fb35c0f32f8cf95de Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Aug 2024 13:12:58 +0300 Subject: [PATCH 273/313] MEX-512: refresh pair tokenPriceUSD in tokens cronjob Signed-off-by: Claudiu Lataretu --- src/services/crons/tokens.cache.warmer.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/crons/tokens.cache.warmer.service.ts b/src/services/crons/tokens.cache.warmer.service.ts index 6786a9935..59702c784 100644 --- a/src/services/crons/tokens.cache.warmer.service.ts +++ b/src/services/crons/tokens.cache.warmer.service.ts @@ -11,6 +11,7 @@ import { TokenComputeService } from 'src/modules/tokens/services/token.compute.s import { TokenSetterService } from 'src/modules/tokens/services/token.setter.service'; import moment from 'moment'; import { TokenRepositoryService } from 'src/modules/tokens/services/token.repository.service'; +import { PairSetterService } from 'src/modules/pair/services/pair.setter.service'; @Injectable() export class TokensCacheWarmerService { @@ -19,6 +20,7 @@ export class TokensCacheWarmerService { private readonly tokenComputeService: TokenComputeService, private readonly tokenSetterService: TokenSetterService, private readonly tokenRepository: TokenRepositoryService, + private readonly pairSetter: PairSetterService, @Inject(PUB_SUB) private pubSub: RedisPubSub, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, ) {} @@ -81,6 +83,7 @@ export class TokensCacheWarmerService { priceDerivedEGLD, ), this.tokenSetterService.setDerivedUSD(tokenID, priceDerivedUSD), + this.pairSetter.setTokenPriceUSD(tokenID, priceDerivedUSD), ]); await this.deleteCacheKeys(cachedKeys); From 05388e441a2624d3cf8726a75bd9e0cc63502c99 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Aug 2024 13:08:25 +0300 Subject: [PATCH 274/313] MEX-512: use cached token price USD for pair token Signed-off-by: Claudiu Lataretu --- src/modules/pair/services/pair.compute.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 534bca2c8..52ef42438 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -179,7 +179,7 @@ export class PairComputeService implements IPairComputeService { localTtl: CacheTtlInfo.Price.localTtl, }) async tokenPriceUSD(tokenID: string): Promise { - return await this.tokenCompute.computeTokenPriceDerivedUSD(tokenID); + return await this.tokenCompute.tokenPriceDerivedUSD(tokenID); } @ErrorLoggerAsync({ From 875db5a882d7f82a2f9c0d9de199b8b7a961d8c2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 23 Aug 2024 13:00:00 +0300 Subject: [PATCH 275/313] MEX-512: remove caching for user unstaked tokens from token unstake Signed-off-by: Claudiu Lataretu --- .../token-unstake/services/token.unstake.abi.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/modules/token-unstake/services/token.unstake.abi.service.ts b/src/modules/token-unstake/services/token.unstake.abi.service.ts index 86b2b1079..d2b852f36 100644 --- a/src/modules/token-unstake/services/token.unstake.abi.service.ts +++ b/src/modules/token-unstake/services/token.unstake.abi.service.ts @@ -92,11 +92,6 @@ export class TokenUnstakeAbiService } @ErrorLoggerAsync({ logArgs: true }) - @GetOrSetCache({ - baseKey: 'tokenUnstake', - remoteTtl: CacheTtlInfo.ContractBalance.remoteTtl, - localTtl: CacheTtlInfo.ContractBalance.localTtl, - }) async unlockedTokensForUser( userAddress: string, ): Promise { From 71ff21ab09b6dd22969eb8bde7d11a3d560fb5ee Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 18:42:28 +0300 Subject: [PATCH 276/313] MEX-513: Add LP token type Signed-off-by: Claudiu Lataretu --- src/modules/tokens/services/token.service.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index 511063c9d..fb42a1343 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, forwardRef } from '@nestjs/common'; -import { EsdtToken } from '../models/esdtToken.model'; +import { EsdtToken, EsdtTokenType } from '../models/esdtToken.model'; import { TokenSortingArgs, TokensFilter, @@ -22,12 +22,15 @@ import { SortingOrder } from 'src/modules/common/page.data'; import { TokenFilteringService } from './token.filtering.service'; import { PaginationArgs } from 'src/modules/dex.model'; import { getAllKeys } from 'src/utils/get.many.utils'; +import { PairService } from 'src/modules/pair/services/pair.service'; @Injectable() export class TokenService { constructor( private readonly tokenRepository: TokenRepositoryService, private readonly pairAbi: PairAbiService, + @Inject(forwardRef(() => PairService)) + private readonly pairService: PairService, private readonly routerAbi: RouterAbiService, private readonly apiService: MXApiService, protected readonly cachingService: CacheService, @@ -111,6 +114,13 @@ export class TokenService { localTtl: CacheTtlInfo.Token.localTtl, }) async getEsdtTokenType(tokenID: string): Promise { + const pairAddress = await this.pairService.getPairAddressByLpTokenID( + tokenID, + ); + if (pairAddress) { + return EsdtTokenType.FungibleLpToken; + } + return await this.tokenRepository.getTokenType(tokenID); } From 239dd5a72bc1be926ace4479a13eb4033b231d8d Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 19 Aug 2024 19:07:20 +0300 Subject: [PATCH 277/313] MEX-513: add lp token compute price for tokenPriceDerivedUSD Signed-off-by: Claudiu Lataretu --- src/modules/tokens/services/token.compute.service.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index 321ddfd72..eb76249f8 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -202,6 +202,14 @@ export class TokenComputeService implements ITokenComputeService { } async computeTokenPriceDerivedUSD(tokenID: string): Promise { + const pairAddress = await this.pairService.getPairAddressByLpTokenID( + tokenID, + ); + + if (pairAddress) { + return this.pairCompute.lpTokenPriceUSD(pairAddress); + } + const [egldPriceUSD, derivedEGLD, usdcPrice] = await Promise.all([ this.getEgldPriceInUSD(), this.computeTokenPriceDerivedEGLD(tokenID, []), From 9cd8d85457ca5c094e9823781369f8a595813f00 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 14:42:58 +0300 Subject: [PATCH 278/313] MEX-513: refresh lp token type on pair cache warmer Signed-off-by: Claudiu Lataretu --- src/services/crons/pair.cache.warmer.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index 9f52a6e6b..495c11099 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -15,6 +15,8 @@ import { constantsConfig } from 'src/config'; import { Lock } from '@multiversx/sdk-nestjs-common'; import { Logger } from 'winston'; import { PerformanceProfiler } from 'src/utils/performance.profiler'; +import { TokenSetterService } from 'src/modules/tokens/services/token.setter.service'; +import { EsdtTokenType } from 'src/modules/tokens/models/esdtToken.model'; @Injectable() export class PairCacheWarmerService { @@ -24,6 +26,7 @@ export class PairCacheWarmerService { private readonly pairAbi: PairAbiService, private readonly routerAbi: RouterAbiService, private readonly analyticsQuery: AnalyticsQueryService, + private readonly tokenSetter: TokenSetterService, private readonly apiConfig: ApiConfigService, @Inject(PUB_SUB) private pubSub: RedisPubSub, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, @@ -54,6 +57,10 @@ export class PairCacheWarmerService { pairMetadata.address, lpTokenID, ), + this.tokenSetter.setEsdtTokenType( + lpTokenID, + EsdtTokenType.FungibleLpToken, + ), ]); await this.deleteCacheKeys(cachedKeys); From 95a0d40c8c176a79a437920e685442632f763740 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 10 Sep 2024 13:25:38 +0300 Subject: [PATCH 279/313] SERVICES-2575: set local cache in GetOrSet decorator - add value to local cache if found in remote cache - add extra cache metrics (remote/local cache hit, remote cache miss) --- src/helpers/decorators/caching.decorator.ts | 23 +++++++++++++- src/utils/metrics.collector.ts | 33 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/helpers/decorators/caching.decorator.ts b/src/helpers/decorators/caching.decorator.ts index dfbb5d7c7..e49fc849a 100644 --- a/src/helpers/decorators/caching.decorator.ts +++ b/src/helpers/decorators/caching.decorator.ts @@ -7,6 +7,7 @@ import { formatNullOrUndefined, parseCachedNullOrUndefined, } from 'src/utils/cache.utils'; +import { MetricsCollector } from 'src/utils/metrics.collector'; export interface ICachingOptions { baseKey: string; @@ -34,15 +35,33 @@ export function GetOrSetCache(cachingOptions: ICachingOptions) { ...args, ); + const genericCacheKey = generateCacheKeyFromParams( + cachingOptions.baseKey, + propertyKey, + ); + if (context && context.deepHistoryTimestamp) { cacheKey = `${cacheKey}.${context.deepHistoryTimestamp}`; } const cachingService: CacheService = this.cachingService; - const cachedValue = await cachingService.get(cacheKey); + const locallyCachedValue = await cachingService.getLocal(cacheKey); + if (locallyCachedValue !== undefined) { + MetricsCollector.incrementLocalCacheHit(genericCacheKey); + return parseCachedNullOrUndefined(locallyCachedValue); + } + + const cachedValue = await cachingService.getRemote(cacheKey); if (cachedValue !== undefined) { + MetricsCollector.incrementCachedApiHit(genericCacheKey); + + cachingService.setLocal( + cacheKey, + cachedValue, + cachingOptions.localTtl, + ); return parseCachedNullOrUndefined(cachedValue); } @@ -62,6 +81,8 @@ export function GetOrSetCache(cachingOptions: ICachingOptions) { localTtl, ); + MetricsCollector.incrementCacheMiss(genericCacheKey); + return value; }; return descriptor; diff --git a/src/utils/metrics.collector.ts b/src/utils/metrics.collector.ts index eb18d7884..c3f2e9801 100644 --- a/src/utils/metrics.collector.ts +++ b/src/utils/metrics.collector.ts @@ -11,6 +11,8 @@ export class MetricsCollector { private static guestQueriesGauge: Gauge; private static currentNonceGauge: Gauge; private static lastProcessedNonceGauge: Gauge; + private static localCacheHitGauge: Gauge; + private static cacheMissGauge: Gauge; private static baseMetrics = new MetricsService(); @@ -92,6 +94,22 @@ export class MetricsCollector { labelNames: ['operation'], }); } + + if (!MetricsCollector.localCacheHitGauge) { + MetricsCollector.localCacheHitGauge = new Gauge({ + name: 'local_cached_hits', + help: 'Number of hits for local cached data', + labelNames: ['key'], + }); + } + + if (!MetricsCollector.cacheMissGauge) { + MetricsCollector.cacheMissGauge = new Gauge({ + name: 'cache_misses', + help: 'Number of cache misses', + labelNames: ['key'], + }); + } } static setFieldDuration(name: string, path: string, duration: number) { @@ -196,6 +214,21 @@ export class MetricsCollector { MetricsService.setGuestHitQueries(count); } + static incrementLocalCacheHit(key: string) { + MetricsCollector.ensureIsInitialized(); + MetricsCollector.localCacheHitGauge.inc({ key }); + } + + static incrementCachedApiHit(endpoint: string) { + MetricsCollector.ensureIsInitialized(); + MetricsCollector.baseMetrics.incrementCachedApiHit(endpoint); + } + + static incrementCacheMiss(key: string) { + MetricsCollector.ensureIsInitialized(); + MetricsCollector.cacheMissGauge.inc({ key }); + } + static async getMetrics(): Promise { const baseMetrics = await MetricsCollector.baseMetrics.getMetrics(); const currentMetrics = await register.metrics(); From f842ede081b05548d5296d934ea272108e186e95 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 10 Sep 2024 13:37:27 +0300 Subject: [PATCH 280/313] SERVICES-2575: fix failing unit test --- src/modules/farm/specs/farm.compute.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/farm/specs/farm.compute.service.spec.ts b/src/modules/farm/specs/farm.compute.service.spec.ts index 7db6dccf4..2f4cd3203 100644 --- a/src/modules/farm/specs/farm.compute.service.spec.ts +++ b/src/modules/farm/specs/farm.compute.service.spec.ts @@ -67,7 +67,7 @@ describe('FarmService', () => { const service = module.get( FarmComputeServiceV1_2, ); - const farmedTokenPriceUSD = await service.computeFarmedTokenPriceUSD( + const farmedTokenPriceUSD = await service.farmedTokenPriceUSD( Address.fromHex( '0000000000000000000000000000000000000000000000000000000000000021', ).bech32(), From 67e78307ebce202efa67805656bc5e389bf40e0b Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 10 Sep 2024 15:44:41 +0300 Subject: [PATCH 281/313] SERVICES-2569: parse cached null/undefined in aws getter --- src/modules/analytics/services/analytics.aws.getter.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/analytics/services/analytics.aws.getter.service.ts b/src/modules/analytics/services/analytics.aws.getter.service.ts index 6145ba51e..91872348c 100644 --- a/src/modules/analytics/services/analytics.aws.getter.service.ts +++ b/src/modules/analytics/services/analytics.aws.getter.service.ts @@ -4,6 +4,7 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { HistoricDataModel } from '../models/analytics.model'; import moment from 'moment'; import { ErrorLoggerAsync } from '@multiversx/sdk-nestjs-common'; +import { parseCachedNullOrUndefined } from 'src/utils/cache.utils'; @Injectable() export class AnalyticsAWSGetterService { @@ -14,7 +15,7 @@ export class AnalyticsAWSGetterService { if (!data || data === undefined) { return undefined; } - return data; + return parseCachedNullOrUndefined(data); } @ErrorLoggerAsync() From 9eff1b749bf709df331e20b33a8fcfc35c6cd02a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 18:01:08 +0300 Subject: [PATCH 282/313] MEX-514: added compute methods for user staking boosted APRs Signed-off-by: Claudiu Lataretu --- .../services/staking.compute.service.ts | 294 ++++++++++-------- 1 file changed, 160 insertions(+), 134 deletions(-) diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 2da8bbcfd..b0895cca9 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -10,7 +10,6 @@ import { GetOrSetCache } from 'src/helpers/decorators/caching.decorator'; import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { computeValueUSD, denominateAmount } from 'src/utils/token.converters'; import { OptimalCompoundModel } from '../models/staking.model'; -import { TokenService } from 'src/modules/tokens/services/token.service'; import { TokenComputeService } from 'src/modules/tokens/services/token.compute.service'; import { TokenDistributionModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; @@ -18,6 +17,7 @@ import { WeekTimekeepingComputeService } from 'src/submodules/week-timekeeping/s import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { EsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; @Injectable() export class StakingComputeService { @@ -26,9 +26,9 @@ export class StakingComputeService { @Inject(forwardRef(() => StakingService)) private readonly stakingService: StakingService, private readonly contextGetter: ContextGetterService, - private readonly tokenService: TokenService, private readonly tokenCompute: TokenComputeService, private readonly weekTimekeepingCompute: WeekTimekeepingComputeService, + private readonly weekTimeKeepingAbi: WeekTimekeepingAbiService, private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, private readonly weeklyRewardsSplittingCompute: WeeklyRewardsSplittingComputeService, private readonly apiService: MXApiService, @@ -476,81 +476,101 @@ export class StakingComputeService { userAddress: string, week: number, ): Promise { - return this.computeUserAccumulatedRewards(scAddress, userAddress, week); + const rewards = await this.computeUserRewardsForWeek( + scAddress, + userAddress, + week, + ); + + return rewards[0] ? rewards[0].amount : '0'; } - async computeUserAccumulatedRewards( + async computeUserRewardsForWeek( scAddress: string, userAddress: string, week: number, - ): Promise { - const [ - boostedYieldsFactors, - boostedYieldsRewardsPercenatage, - userEnergy, - totalRewards, - rewardsPerBlock, - farmTokenSupply, - totalEnergy, - blocksInWeek, - liquidity, - ] = await Promise.all([ - this.stakingAbi.boostedYieldsFactors(scAddress), - this.stakingAbi.boostedYieldsRewardsPercenatage(scAddress), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( + rewardsPerWeek?: EsdtTokenPayment[], + ): Promise { + const userRewardsForWeek = []; + + const [currentWeek, userEnergyForWeek, totalEnergyForWeek, liquidity] = + await Promise.all([ + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + this.weeklyRewardsSplittingAbi.totalEnergyForWeek( + scAddress, + week, + ), + + this.stakingAbi.userTotalStakePosition(scAddress, userAddress), + ]); + + const rewardsForWeek = + rewardsPerWeek ?? + (await this.weeklyRewardsSplittingAbi.totalRewardsForWeek( scAddress, - userAddress, week, - ), - this.stakingAbi.accumulatedRewardsForWeek(scAddress, week), - this.stakingAbi.perBlockRewardsAmount(scAddress), - this.stakingAbi.farmTokenSupply(scAddress), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), - this.computeBlocksInWeek(scAddress, week), - this.stakingAbi.userTotalStakePosition(scAddress, userAddress), - ]); + )); - const energyAmount = userEnergy.amount; + if (rewardsForWeek.length === 0) { + return userRewardsForWeek; + } + + if (rewardsForWeek.length !== 1) { + throw new Error('Invalid boosted yields rewards'); + } - const userHasMinEnergy = new BigNumber(energyAmount).isGreaterThan( - boostedYieldsFactors.minEnergyAmount, + const boostedYieldsFactors = await this.stakingAbi.boostedYieldsFactors( + scAddress, ); + + const userHasMinEnergy = new BigNumber( + userEnergyForWeek.amount, + ).isGreaterThan(boostedYieldsFactors.minEnergyAmount); if (!userHasMinEnergy) { - return '0'; + return userRewardsForWeek; } const userMinFarmAmount = new BigNumber(liquidity).isGreaterThan( boostedYieldsFactors.minFarmAmount, ); if (!userMinFarmAmount) { - return '0'; + return userRewardsForWeek; } - if (totalRewards.length === 0) { - return '0'; + const farmTokenSupply = + week === currentWeek + ? await this.stakingAbi.farmTokenSupply(scAddress) + : await this.stakingAbi.farmSupplyForWeek(scAddress, week); + + const rewardForWeek = rewardsForWeek[0]; + + const weeklyRewardsAmount = new BigNumber(rewardForWeek.amount); + if (weeklyRewardsAmount.isZero()) { + return userRewardsForWeek; } - const userMaxBoostedRewardsPerBlock = new BigNumber(rewardsPerBlock) - .multipliedBy(boostedYieldsRewardsPercenatage) - .dividedBy(constantsConfig.MAX_PERCENT) + const userMaxRewards = weeklyRewardsAmount .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); + .multipliedBy(boostedYieldsFactors.maxRewardsFactor) + .dividedBy(farmTokenSupply) + .integerValue(); - const userRewardsForWeek = new BigNumber( - boostedYieldsFactors.maxRewardsFactor, - ) - .multipliedBy(userMaxBoostedRewardsPerBlock) - .multipliedBy(blocksInWeek); - - const boostedRewardsByEnergy = new BigNumber(totalRewards) + const boostedRewardsByEnergy = weeklyRewardsAmount .multipliedBy(boostedYieldsFactors.userRewardsEnergy) - .multipliedBy(userEnergy.amount) - .dividedBy(totalEnergy); + .multipliedBy(userEnergyForWeek.amount) + .dividedBy(totalEnergyForWeek) + .integerValue(); - const boostedRewardsByTokens = new BigNumber(totalRewards) + const boostedRewardsByTokens = weeklyRewardsAmount .multipliedBy(boostedYieldsFactors.userRewardsFarm) .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); + .dividedBy(farmTokenSupply) + .integerValue(); const constantsBase = new BigNumber( boostedYieldsFactors.userRewardsEnergy, @@ -558,116 +578,122 @@ export class StakingComputeService { const boostedRewardAmount = boostedRewardsByEnergy .plus(boostedRewardsByTokens) - .dividedBy(constantsBase); + .dividedBy(constantsBase) + .integerValue(); - const paymentAmount = - boostedRewardAmount.comparedTo(userRewardsForWeek) < 1 + const userRewardForWeek = + boostedRewardAmount.comparedTo(userMaxRewards) < 1 ? boostedRewardAmount - : userRewardsForWeek; + : userMaxRewards; + + if (userRewardForWeek.isPositive()) { + userRewardsForWeek.push( + new EsdtTokenPayment({ + tokenID: rewardForWeek.tokenID, + nonce: rewardForWeek.nonce, + amount: userRewardForWeek.toFixed(), + }), + ); + } - return paymentAmount.integerValue().toFixed(); + return userRewardsForWeek; } - async computeUserRewardsForWeek( + async computeUserCurentBoostedAPR( scAddress: string, userAddress: string, - week: number, - ): Promise { - const payments: EsdtTokenPayment[] = []; - const [ - totalRewardsForWeek, - userEnergyForWeek, - totalEnergyForWeek, - liquidity, - ] = await Promise.all([ - this.weeklyRewardsSplittingAbi.totalRewardsForWeek(scAddress, week), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( - scAddress, - userAddress, - week, - ), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), - this.stakingAbi.userTotalStakePosition(scAddress, userAddress), - ]); + ): Promise { + const [currentWeek, boostedRewardsPerWeek, userTotalStakePosition] = + await Promise.all([ + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.computeBoostedRewardsPerWeek(scAddress), + this.stakingAbi.userTotalStakePosition(scAddress, userAddress), + ]); - const boostedYieldsFactors = await this.stakingAbi.boostedYieldsFactors( + const userRewardsPerWeek = await this.computeUserRewardsForWeek( scAddress, + userAddress, + currentWeek, + boostedRewardsPerWeek, ); - const userHasMinEnergy = new BigNumber( - userEnergyForWeek.amount, - ).isGreaterThan(boostedYieldsFactors.minEnergyAmount); - if (!userHasMinEnergy) { - return payments; - } + return new BigNumber(userRewardsPerWeek[0].amount) + .multipliedBy(52) + .dividedBy(userTotalStakePosition) + .toNumber(); + } - const userMinFarmAmount = new BigNumber(liquidity).isGreaterThan( - boostedYieldsFactors.minFarmAmount, - ); - if (!userMinFarmAmount) { - return payments; - } + async computeUserMaxBoostedAPR( + scAddress: string, + userAddress: string, + ): Promise { + const [ + boostedRewardsPerWeek, + boostedYieldsFactors, + farmTokenSupply, + userTotalStakePosition, + ] = await Promise.all([ + this.computeBoostedRewardsPerWeek(scAddress), + this.stakingAbi.boostedYieldsFactors(scAddress), + this.stakingAbi.farmTokenSupply(scAddress), + this.stakingAbi.userTotalStakePosition(scAddress, userAddress), + ]); - if (totalRewardsForWeek.length === 0) { - return payments; - } + const userMaxRewardsPerWeek = new BigNumber( + boostedRewardsPerWeek[0].amount, + ) + .multipliedBy(boostedYieldsFactors.maxRewardsFactor) + .multipliedBy(userTotalStakePosition) + .dividedBy(farmTokenSupply); + return userMaxRewardsPerWeek + .multipliedBy(52) + .dividedBy(userTotalStakePosition) + .toNumber(); + } + + async computeBoostedRewardsPerWeek( + scAddress: string, + ): Promise { const [ + rewardTokenID, rewardsPerBlock, + annualPercentageRewards, farmTokenSupply, - boostedYieldsRewardsPercenatage, + boostedYieldsRewardsPercentage, ] = await Promise.all([ + this.stakingAbi.rewardTokenID(scAddress), this.stakingAbi.perBlockRewardsAmount(scAddress), + this.stakingAbi.annualPercentageRewards(scAddress), this.stakingAbi.farmTokenSupply(scAddress), this.stakingAbi.boostedYieldsRewardsPercenatage(scAddress), ]); - const userMaxBoostedRewardsPerBlock = new BigNumber(rewardsPerBlock) - .multipliedBy(boostedYieldsRewardsPercenatage) + const rewardsPerBlockAPRBound = new BigNumber(farmTokenSupply) + .multipliedBy(annualPercentageRewards) .dividedBy(constantsConfig.MAX_PERCENT) - .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); + .dividedBy(constantsConfig.BLOCKS_IN_YEAR); - const userRewardsForWeek = new BigNumber( - boostedYieldsFactors.maxRewardsFactor, + const actualRewardsPerBlock = new BigNumber(rewardsPerBlock).isLessThan( + rewardsPerBlockAPRBound, ) - .multipliedBy(userMaxBoostedRewardsPerBlock) - .multipliedBy(constantsConfig.BLOCKS_PER_WEEK); - - for (const weeklyRewards of totalRewardsForWeek) { - const boostedRewardsByEnergy = new BigNumber(weeklyRewards.amount) - .multipliedBy(boostedYieldsFactors.userRewardsEnergy) - .multipliedBy(userEnergyForWeek.amount) - .dividedBy(totalEnergyForWeek); - - const boostedRewardsByTokens = new BigNumber(weeklyRewards.amount) - .multipliedBy(boostedYieldsFactors.userRewardsFarm) - .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); - - const constantsBase = new BigNumber( - boostedYieldsFactors.userRewardsEnergy, - ).plus(boostedYieldsFactors.userRewardsFarm); - - const boostedRewardAmount = boostedRewardsByEnergy - .plus(boostedRewardsByTokens) - .dividedBy(constantsBase); - - const paymentAmount = - boostedRewardAmount.comparedTo(userRewardsForWeek) < 1 - ? boostedRewardAmount - : userRewardsForWeek; - if (paymentAmount.isPositive()) { - const payment = new EsdtTokenPayment(); - payment.amount = paymentAmount.integerValue().toFixed(); - payment.nonce = 0; - payment.tokenID = weeklyRewards.tokenID; - payment.tokenType = weeklyRewards.tokenType; - payments.push(payment); - } - } - - return payments; + ? new BigNumber(rewardsPerBlock) + : rewardsPerBlockAPRBound; + const blocksInWeek = 14440 * 7; + const totalRewardsPerWeek = + actualRewardsPerBlock.multipliedBy(blocksInWeek); + + return [ + new EsdtTokenPayment({ + tokenID: rewardTokenID, + nonce: 0, + amount: totalRewardsPerWeek + .multipliedBy(boostedYieldsRewardsPercentage) + .dividedBy(constantsConfig.MAX_PERCENT) + .integerValue() + .toFixed(), + }), + ]; } @ErrorLoggerAsync({ From 80ae381a77d677a81be48f50b68c8740b7d7dbad Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 18:01:45 +0300 Subject: [PATCH 283/313] MEX-514: Added user boosted APRs to BoostedRewardsModel Signed-off-by: Claudiu Lataretu --- src/modules/farm/models/farm.model.ts | 6 ++++ src/modules/staking/models/staking.model.ts | 8 +++++ .../staking/services/staking.service.ts | 14 +++++--- src/modules/staking/staking.module.ts | 8 +++-- src/modules/staking/staking.resolver.ts | 35 +++++++++++++++---- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/modules/farm/models/farm.model.ts b/src/modules/farm/models/farm.model.ts index 84b23d351..72bbb521e 100644 --- a/src/modules/farm/models/farm.model.ts +++ b/src/modules/farm/models/farm.model.ts @@ -47,12 +47,18 @@ export class RewardsModel { export class BoostedRewardsModel { @Field() farmAddress: string; + @Field() + userAddress: string; @Field(() => [UserInfoByWeekModel], { nullable: true }) boostedRewardsWeeklyInfo: UserInfoByWeekModel[]; @Field(() => ClaimProgress, { nullable: true }) claimProgress: ClaimProgress; @Field({ nullable: true }) accumulatedRewards: string; + @Field({ nullable: true }) + curentBoostedAPR: number; + @Field({ nullable: true }) + maximumBoostedAPR: number; constructor(init?: Partial) { Object.assign(this, init); diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 436c83eba..20a08545a 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -9,6 +9,7 @@ import { UserInfoByWeekModel, } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; +import { BoostedRewardsModel } from 'src/modules/farm/models/farm.model'; @ObjectType() export class StakingModel { @@ -109,6 +110,13 @@ export class StakingRewardsModel { } } +@ObjectType() +export class StakingBoostedRewardsModel extends BoostedRewardsModel { + constructor(init?: Partial) { + super(init); + } +} + @ObjectType() export class OptimalCompoundModel { @Field(() => Int, { diff --git a/src/modules/staking/services/staking.service.ts b/src/modules/staking/services/staking.service.ts index dbba2b6a2..4d147ae56 100644 --- a/src/modules/staking/services/staking.service.ts +++ b/src/modules/staking/services/staking.service.ts @@ -9,7 +9,11 @@ import { DecodeAttributesArgs } from 'src/modules/proxy/models/proxy.args'; import { RemoteConfigGetterService } from 'src/modules/remote-config/remote-config.getter.service'; import { ContextGetterService } from 'src/services/context/context.getter.service'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; -import { StakingModel, StakingRewardsModel } from '../models/staking.model'; +import { + StakingBoostedRewardsModel, + StakingModel, + StakingRewardsModel, +} from '../models/staking.model'; import { StakingTokenAttributesModel, UnbondTokenAttributesModel, @@ -26,7 +30,6 @@ import { import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { constantsConfig } from 'src/config'; -import { BoostedRewardsModel } from 'src/modules/farm/models/farm.model'; import { CollectionType } from 'src/modules/common/collection.type'; import { PaginationArgs } from 'src/modules/dex.model'; import { @@ -255,7 +258,7 @@ export class StakingService { async getStakingBoostedRewardsBatch( stakingAddresses: string[], userAddress: string, - ): Promise { + ): Promise { const promises = stakingAddresses.map(async (address) => { return await this.getStakingBoostedRewards(address, userAddress); }); @@ -265,7 +268,7 @@ export class StakingService { async getStakingBoostedRewards( stakingAddress: string, userAddress: string, - ): Promise { + ): Promise { const currentWeek = await this.weekTimekeepingAbi.currentWeek( stakingAddress, ); @@ -309,8 +312,9 @@ export class StakingService { currentWeek, ); - return new BoostedRewardsModel({ + return new StakingBoostedRewardsModel({ farmAddress: stakingAddress, + userAddress: userAddress, boostedRewardsWeeklyInfo: modelsList, claimProgress: currentClaimProgress, accumulatedRewards: userAccumulatedRewards, diff --git a/src/modules/staking/staking.module.ts b/src/modules/staking/staking.module.ts index a610aff6d..d45122bc7 100644 --- a/src/modules/staking/staking.module.ts +++ b/src/modules/staking/staking.module.ts @@ -9,7 +9,10 @@ import { StakingComputeService } from './services/staking.compute.service'; import { StakingService } from './services/staking.service'; import { StakingSetterService } from './services/staking.setter.service'; import { StakingTransactionService } from './services/staking.transactions.service'; -import { StakingResolver } from './staking.resolver'; +import { + StakingBoostedRewardsResolver, + StakingResolver, +} from './staking.resolver'; import { WeekTimekeepingModule } from 'src/submodules/week-timekeeping/week-timekeeping.module'; import { WeeklyRewardsSplittingModule } from 'src/submodules/weekly-rewards-splitting/weekly-rewards-splitting.module'; import { StakingFilteringService } from './services/staking.filtering.service'; @@ -30,8 +33,9 @@ import { StakingFilteringService } from './services/staking.filtering.service'; StakingSetterService, StakingComputeService, StakingTransactionService, - StakingResolver, StakingFilteringService, + StakingResolver, + StakingBoostedRewardsResolver, ], exports: [ StakingAbiService, diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index a0c07619a..64099386f 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -15,6 +15,7 @@ import { } from './models/staking.args'; import { OptimalCompoundModel, + StakingBoostedRewardsModel, StakingModel, StakingRewardsModel, } from './models/staking.model'; @@ -34,16 +35,38 @@ import { constantsConfig } from 'src/config'; import { WeeklyRewardsSplittingAbiService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.abi.service'; import { StakeAddressValidationPipe } from './validators/stake.address.validator'; import { BoostedYieldsFactors } from '../farm/models/farm.v2.model'; -import { - BoostedRewardsModel, - UserTotalBoostedPosition, -} from '../farm/models/farm.model'; +import { UserTotalBoostedPosition } from '../farm/models/farm.model'; import { StakingFarmsResponse } from './models/staking.farms.response'; import ConnectionArgs, { getPagingParameters, } from '../common/filters/connection.args'; import PageResponse from '../common/page.response'; +@Resolver(() => StakingBoostedRewardsModel) +export class StakingBoostedRewardsResolver { + constructor(private readonly stakingCompute: StakingComputeService) {} + + @ResolveField() + async curentBoostedAPR( + @Parent() parent: StakingBoostedRewardsModel, + ): Promise { + return this.stakingCompute.computeUserCurentBoostedAPR( + parent.farmAddress, + parent.userAddress, + ); + } + + @ResolveField() + async maximumBoostedAPR( + @Parent() parent: StakingBoostedRewardsModel, + ): Promise { + return this.stakingCompute.computeUserMaxBoostedAPR( + parent.farmAddress, + parent.userAddress, + ); + } +} + @Resolver(() => StakingModel) export class StakingResolver { constructor( @@ -315,14 +338,14 @@ export class StakingResolver { } @UseGuards(JwtOrNativeAuthGuard) - @Query(() => [BoostedRewardsModel], { + @Query(() => [StakingBoostedRewardsModel], { description: 'Returns staking boosted rewards for the user', }) async getStakingBoostedRewardsBatch( @Args('stakingAddresses', { type: () => [String] }) stakingAddresses: string[], @AuthUser() user: UserAuthResult, - ): Promise { + ): Promise { return this.stakingService.getStakingBoostedRewardsBatch( stakingAddresses, user.address, From d92785b2f1ee3371ace5f3af3983e5c3c82b891f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 18:27:52 +0300 Subject: [PATCH 284/313] MEX-514: add compute for user farm boosted APRs Signed-off-by: Claudiu Lataretu --- .../v2/services/farm.v2.compute.service.ts | 316 ++++++++++-------- 1 file changed, 185 insertions(+), 131 deletions(-) diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index 70558ed5c..f4db18eaa 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -19,6 +19,8 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { TokenDistributionModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { WeeklyRewardsSplittingComputeService } from 'src/submodules/weekly-rewards-splitting/services/weekly-rewards-splitting.compute.service'; import { IFarmComputeServiceV2 } from './interfaces'; +import { WeekTimekeepingAbiService } from 'src/submodules/week-timekeeping/services/week-timekeeping.abi.service'; +import { computeValueUSD } from 'src/utils/token.converters'; @Injectable() export class FarmComputeServiceV2 @@ -36,6 +38,7 @@ export class FarmComputeServiceV2 protected readonly contextGetter: ContextGetterService, protected readonly tokenCompute: TokenComputeService, protected readonly cachingService: CacheService, + private readonly weekTimeKeepingAbi: WeekTimekeepingAbiService, private readonly weekTimekeepingCompute: WeekTimekeepingComputeService, private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, private readonly weeklyRewardsSplittingCompute: WeeklyRewardsSplittingComputeService, @@ -164,81 +167,101 @@ export class FarmComputeServiceV2 userAddress: string, week: number, ): Promise { - return this.computeUserAccumulatedRewards(scAddress, userAddress, week); + const rewards = await this.computeUserRewardsForWeek( + scAddress, + userAddress, + week, + ); + + return rewards[0] ? rewards[0].amount : '0'; } - async computeUserAccumulatedRewards( + async computeUserRewardsForWeek( scAddress: string, userAddress: string, week: number, - ): Promise { - const [ - boostedYieldsFactors, - boostedYieldsRewardsPercenatage, - userEnergy, - totalRewards, - rewardsPerBlock, - farmTokenSupply, - totalEnergy, - blocksInWeek, - liquidity, - ] = await Promise.all([ - this.farmAbi.boostedYieldsFactors(scAddress), - this.farmAbi.boostedYieldsRewardsPercenatage(scAddress), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( + rewardsPerWeek?: EsdtTokenPayment[], + ): Promise { + const userRewardsForWeek = []; + + const [currentWeek, userEnergyForWeek, totalEnergyForWeek, liquidity] = + await Promise.all([ + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + this.weeklyRewardsSplittingAbi.totalEnergyForWeek( + scAddress, + week, + ), + + this.farmAbi.userTotalFarmPosition(scAddress, userAddress), + ]); + + const rewardsForWeek = + rewardsPerWeek ?? + (await this.weeklyRewardsSplittingAbi.totalRewardsForWeek( scAddress, - userAddress, week, - ), - this.farmAbi.accumulatedRewardsForWeek(scAddress, week), - this.farmAbi.rewardsPerBlock(scAddress), - this.farmAbi.farmTokenSupply(scAddress), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), - this.computeBlocksInWeek(scAddress, week), - this.farmAbi.userTotalFarmPosition(scAddress, userAddress), - ]); + )); - const energyAmount = userEnergy.amount; + if (rewardsForWeek.length === 0) { + return userRewardsForWeek; + } + + if (rewardsForWeek.length !== 1) { + throw new Error('Invalid boosted yields rewards'); + } - const userHasMinEnergy = new BigNumber(energyAmount).isGreaterThan( - boostedYieldsFactors.minEnergyAmount, + const boostedYieldsFactors = await this.farmAbi.boostedYieldsFactors( + scAddress, ); + + const userHasMinEnergy = new BigNumber( + userEnergyForWeek.amount, + ).isGreaterThan(boostedYieldsFactors.minEnergyAmount); if (!userHasMinEnergy) { - return '0'; + return userRewardsForWeek; } const userMinFarmAmount = new BigNumber(liquidity).isGreaterThan( boostedYieldsFactors.minFarmAmount, ); if (!userMinFarmAmount) { - return '0'; + return userRewardsForWeek; } - if (totalRewards.length === 0) { - return '0'; + const farmTokenSupply = + week === currentWeek + ? await this.farmAbi.farmTokenSupply(scAddress) + : await this.farmAbi.farmSupplyForWeek(scAddress, week); + + const rewardForWeek = rewardsForWeek[0]; + + const weeklyRewardsAmount = new BigNumber(rewardForWeek.amount); + if (weeklyRewardsAmount.isZero()) { + return userRewardsForWeek; } - const userMaxBoostedRewardsPerBlock = new BigNumber(rewardsPerBlock) - .multipliedBy(boostedYieldsRewardsPercenatage) - .dividedBy(constantsConfig.MAX_PERCENT) + const userMaxRewards = weeklyRewardsAmount .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); - - const userRewardsForWeek = new BigNumber( - boostedYieldsFactors.maxRewardsFactor, - ) - .multipliedBy(userMaxBoostedRewardsPerBlock) - .multipliedBy(blocksInWeek); + .multipliedBy(boostedYieldsFactors.maxRewardsFactor) + .dividedBy(farmTokenSupply) + .integerValue(); - const boostedRewardsByEnergy = new BigNumber(totalRewards) + const boostedRewardsByEnergy = weeklyRewardsAmount .multipliedBy(boostedYieldsFactors.userRewardsEnergy) - .multipliedBy(userEnergy.amount) - .dividedBy(totalEnergy); + .multipliedBy(userEnergyForWeek.amount) + .dividedBy(totalEnergyForWeek) + .integerValue(); - const boostedRewardsByTokens = new BigNumber(totalRewards) + const boostedRewardsByTokens = weeklyRewardsAmount .multipliedBy(boostedYieldsFactors.userRewardsFarm) .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); + .dividedBy(farmTokenSupply) + .integerValue(); const constantsBase = new BigNumber( boostedYieldsFactors.userRewardsEnergy, @@ -246,116 +269,147 @@ export class FarmComputeServiceV2 const boostedRewardAmount = boostedRewardsByEnergy .plus(boostedRewardsByTokens) - .dividedBy(constantsBase); + .dividedBy(constantsBase) + .integerValue(); - const paymentAmount = - boostedRewardAmount.comparedTo(userRewardsForWeek) < 1 + const userRewardForWeek = + boostedRewardAmount.comparedTo(userMaxRewards) < 1 ? boostedRewardAmount - : userRewardsForWeek; + : userMaxRewards; + + if (userRewardForWeek.isPositive()) { + userRewardsForWeek.push( + new EsdtTokenPayment({ + tokenID: rewardForWeek.tokenID, + nonce: rewardForWeek.nonce, + amount: userRewardForWeek.toFixed(), + }), + ); + } - return paymentAmount.integerValue().toFixed(); + return userRewardsForWeek; } - async computeUserRewardsForWeek( + async computeUserCurentBoostedAPR( scAddress: string, userAddress: string, - week: number, - ): Promise { - const payments: EsdtTokenPayment[] = []; + ): Promise { const [ - totalRewardsForWeek, - userEnergyForWeek, - totalEnergyForWeek, - liquidity, + currentWeek, + boostedRewardsPerWeek, + userTotalFarmPosition, + farmToken, + farmedToken, + farmingTokenPriceUSD, + farmedTokenPriceUSD, ] = await Promise.all([ - this.weeklyRewardsSplittingAbi.totalRewardsForWeek(scAddress, week), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( - scAddress, - userAddress, - week, - ), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.computeBoostedRewardsPerWeek(scAddress), this.farmAbi.userTotalFarmPosition(scAddress, userAddress), + this.farmService.getFarmToken(scAddress), + this.farmService.getFarmedToken(scAddress), + this.farmingTokenPriceUSD(scAddress), + this.farmedTokenPriceUSD(scAddress), ]); - const boostedYieldsFactors = await this.farmAbi.boostedYieldsFactors( + const userRewardsPerWeek = await this.computeUserRewardsForWeek( scAddress, + userAddress, + currentWeek, + boostedRewardsPerWeek, ); - const userHasMinEnergy = new BigNumber( - userEnergyForWeek.amount, - ).isGreaterThan(boostedYieldsFactors.minEnergyAmount); - if (!userHasMinEnergy) { - return payments; - } - - const userMinFarmAmount = new BigNumber(liquidity).isGreaterThan( - boostedYieldsFactors.minFarmAmount, + const userTotalFarmPositionUSD = computeValueUSD( + userTotalFarmPosition, + farmToken.decimals, + farmingTokenPriceUSD, + ); + const userRewardsPerWeekUSD = computeValueUSD( + userRewardsPerWeek[0].amount, + farmedToken.decimals, + farmedTokenPriceUSD, ); - if (!userMinFarmAmount) { - return payments; - } - if (totalRewardsForWeek.length === 0) { - return payments; - } + return new BigNumber(userRewardsPerWeekUSD) + .multipliedBy(52) + .dividedBy(userTotalFarmPositionUSD) + .toNumber(); + } + async computeUserMaxBoostedAPR( + scAddress: string, + userAddress: string, + ): Promise { const [ - rewardsPerBlock, + boostedRewardsPerWeek, + boostedYieldsFactors, farmTokenSupply, - boostedYieldsRewardsPercenatage, + userTotalFarmPosition, + farmToken, + farmedToken, + farmingTokenPriceUSD, + farmedTokenPriceUSD, ] = await Promise.all([ - this.farmAbi.rewardsPerBlock(scAddress), + this.computeBoostedRewardsPerWeek(scAddress), + this.farmAbi.boostedYieldsFactors(scAddress), this.farmAbi.farmTokenSupply(scAddress), - this.farmAbi.boostedYieldsRewardsPercenatage(scAddress), + this.farmAbi.userTotalFarmPosition(scAddress, userAddress), + this.farmService.getFarmToken(scAddress), + this.farmService.getFarmedToken(scAddress), + this.farmingTokenPriceUSD(scAddress), + this.farmedTokenPriceUSD(scAddress), ]); - const userMaxBoostedRewardsPerBlock = new BigNumber(rewardsPerBlock) - .multipliedBy(boostedYieldsRewardsPercenatage) - .dividedBy(constantsConfig.MAX_PERCENT) - .multipliedBy(liquidity) + const userMaxRewardsPerWeek = new BigNumber( + boostedRewardsPerWeek[0].amount, + ) + .multipliedBy(boostedYieldsFactors.maxRewardsFactor) + .multipliedBy(userTotalFarmPosition) .dividedBy(farmTokenSupply); - const userRewardsForWeek = new BigNumber( - boostedYieldsFactors.maxRewardsFactor, - ) - .multipliedBy(userMaxBoostedRewardsPerBlock) - .multipliedBy(constantsConfig.BLOCKS_PER_WEEK); - - for (const weeklyRewards of totalRewardsForWeek) { - const boostedRewardsByEnergy = new BigNumber(weeklyRewards.amount) - .multipliedBy(boostedYieldsFactors.userRewardsEnergy) - .multipliedBy(userEnergyForWeek.amount) - .dividedBy(totalEnergyForWeek); - - const boostedRewardsByTokens = new BigNumber(weeklyRewards.amount) - .multipliedBy(boostedYieldsFactors.userRewardsFarm) - .multipliedBy(liquidity) - .dividedBy(farmTokenSupply); - - const constantsBase = new BigNumber( - boostedYieldsFactors.userRewardsEnergy, - ).plus(boostedYieldsFactors.userRewardsFarm); - - const boostedRewardAmount = boostedRewardsByEnergy - .plus(boostedRewardsByTokens) - .dividedBy(constantsBase); - - const paymentAmount = - boostedRewardAmount.comparedTo(userRewardsForWeek) < 1 - ? boostedRewardAmount - : userRewardsForWeek; - if (paymentAmount.isPositive()) { - const payment = new EsdtTokenPayment(); - payment.amount = paymentAmount.integerValue().toFixed(); - payment.nonce = 0; - payment.tokenID = weeklyRewards.tokenID; - payment.tokenType = weeklyRewards.tokenType; - payments.push(payment); - } - } + const userTotalFarmPositionUSD = computeValueUSD( + userTotalFarmPosition, + farmToken.decimals, + farmingTokenPriceUSD, + ); + const userMaxRewardsPerWeekUSD = computeValueUSD( + userMaxRewardsPerWeek.toFixed(), + farmedToken.decimals, + farmedTokenPriceUSD, + ); + + return userMaxRewardsPerWeekUSD + .multipliedBy(52) + .dividedBy(userTotalFarmPositionUSD) + .toNumber(); + } + + async computeBoostedRewardsPerWeek( + scAddress: string, + ): Promise { + const [rewardTokenID, rewardsPerBlock, boostedYieldsRewardsPercentage] = + await Promise.all([ + this.farmAbi.farmedTokenID(scAddress), + this.farmAbi.rewardsPerBlock(scAddress), + this.farmAbi.boostedYieldsRewardsPercenatage(scAddress), + ]); + + const blocksInWeek = 14440 * 7; + const totalRewardsPerWeek = new BigNumber(rewardsPerBlock).multipliedBy( + blocksInWeek, + ); - return payments; + return [ + new EsdtTokenPayment({ + tokenID: rewardTokenID, + nonce: 0, + amount: totalRewardsPerWeek + .multipliedBy(boostedYieldsRewardsPercentage) + .dividedBy(constantsConfig.MAX_PERCENT) + .integerValue() + .toFixed(), + }), + ]; } @ErrorLoggerAsync({ From 363c3a86ea9bef822bcb0fbc6089175777891e0f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 18:30:12 +0300 Subject: [PATCH 285/313] MEX-514: added resolver for farm boosted rewards model Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.module.ts | 3 ++- src/modules/farm/v2/farm.v2.resolver.ts | 25 +++++++++++++++++++ .../farm/v2/services/farm.v2.service.ts | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/modules/farm/v2/farm.v2.module.ts b/src/modules/farm/v2/farm.v2.module.ts index 48e182872..60db256c5 100644 --- a/src/modules/farm/v2/farm.v2.module.ts +++ b/src/modules/farm/v2/farm.v2.module.ts @@ -3,7 +3,7 @@ import { TokenModule } from 'src/modules/tokens/token.module'; import { ContextModule } from 'src/services/context/context.module'; import { MXCommunicationModule } from 'src/services/multiversx-communication/mx.communication.module'; import { FarmAbiServiceV2 } from './services/farm.v2.abi.service'; -import { FarmResolverV2 } from './farm.v2.resolver'; +import { FarmBoostedRewardsResolver, FarmResolverV2 } from './farm.v2.resolver'; import { FarmServiceV2 } from './services/farm.v2.service'; import { FarmComputeServiceV2 } from './services/farm.v2.compute.service'; import { PairModule } from 'src/modules/pair/pair.module'; @@ -36,6 +36,7 @@ import { FarmComputeLoaderV2 } from './services/farm.v2.compute.loader'; FarmTransactionServiceV2, FarmResolverV2, FarmTransactionResolverV2, + FarmBoostedRewardsResolver, ], exports: [ FarmServiceV2, diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 78dce345c..0b4d926e2 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -26,6 +26,31 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { FarmAbiLoaderV2 } from './services/farm.v2.abi.loader'; import { FarmComputeLoaderV2 } from './services/farm.v2.compute.loader'; +@Resolver(() => BoostedRewardsModel) +export class FarmBoostedRewardsResolver { + constructor(private readonly farmCompute: FarmComputeServiceV2) {} + + @ResolveField() + async curentBoostedAPR( + @Parent() parent: BoostedRewardsModel, + ): Promise { + return this.farmCompute.computeUserCurentBoostedAPR( + parent.farmAddress, + parent.userAddress, + ); + } + + @ResolveField() + async maximumBoostedAPR( + @Parent() parent: BoostedRewardsModel, + ): Promise { + return this.farmCompute.computeUserMaxBoostedAPR( + parent.farmAddress, + parent.userAddress, + ); + } +} + @Resolver(() => FarmModelV2) export class FarmResolverV2 extends FarmResolver { constructor( diff --git a/src/modules/farm/v2/services/farm.v2.service.ts b/src/modules/farm/v2/services/farm.v2.service.ts index 8dda3aa8c..bd4183548 100644 --- a/src/modules/farm/v2/services/farm.v2.service.ts +++ b/src/modules/farm/v2/services/farm.v2.service.ts @@ -207,6 +207,7 @@ export class FarmServiceV2 extends FarmServiceBase { return new BoostedRewardsModel({ farmAddress, + userAddress, boostedRewardsWeeklyInfo: modelsList, claimProgress: currentClaimProgress, accumulatedRewards: userAccumulatedRewards, From 31ce12f99c349942161ac65af844c06134725fea Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Sep 2024 17:40:32 +0300 Subject: [PATCH 286/313] MEX-514: update compute farm boosted rewards with custom user information Signed-off-by: Claudiu Lataretu --- src/modules/farm/v2/farm.v2.resolver.ts | 21 +++++ .../v2/services/farm.v2.compute.service.ts | 85 ++++++++++++------- 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/modules/farm/v2/farm.v2.resolver.ts b/src/modules/farm/v2/farm.v2.resolver.ts index 0b4d926e2..da230239b 100644 --- a/src/modules/farm/v2/farm.v2.resolver.ts +++ b/src/modules/farm/v2/farm.v2.resolver.ts @@ -33,20 +33,41 @@ export class FarmBoostedRewardsResolver { @ResolveField() async curentBoostedAPR( @Parent() parent: BoostedRewardsModel, + @Args('additionalUserFarmAmount', { + type: () => String, + nullable: true, + defaultValue: '0', + }) + additionalUserFarmAmount: string, + @Args('additionalUserEnergy', { + type: () => String, + nullable: true, + defaultValue: '0', + }) + additionalUserEnergy: string, ): Promise { return this.farmCompute.computeUserCurentBoostedAPR( parent.farmAddress, parent.userAddress, + additionalUserFarmAmount, + additionalUserEnergy, ); } @ResolveField() async maximumBoostedAPR( @Parent() parent: BoostedRewardsModel, + @Args('additionalUserFarmAmount', { + type: () => String, + nullable: true, + defaultValue: '0', + }) + additionalUserFarmAmount: string, ): Promise { return this.farmCompute.computeUserMaxBoostedAPR( parent.farmAddress, parent.userAddress, + additionalUserFarmAmount, ); } } diff --git a/src/modules/farm/v2/services/farm.v2.compute.service.ts b/src/modules/farm/v2/services/farm.v2.compute.service.ts index f4db18eaa..a6cd0f786 100644 --- a/src/modules/farm/v2/services/farm.v2.compute.service.ts +++ b/src/modules/farm/v2/services/farm.v2.compute.service.ts @@ -180,26 +180,12 @@ export class FarmComputeServiceV2 scAddress: string, userAddress: string, week: number, + additionalUserFarmAmount = '0', + additionalUserEnergyAmount = '0', rewardsPerWeek?: EsdtTokenPayment[], ): Promise { const userRewardsForWeek = []; - const [currentWeek, userEnergyForWeek, totalEnergyForWeek, liquidity] = - await Promise.all([ - this.weekTimeKeepingAbi.currentWeek(scAddress), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( - scAddress, - userAddress, - week, - ), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek( - scAddress, - week, - ), - - this.farmAbi.userTotalFarmPosition(scAddress, userAddress), - ]); - const rewardsForWeek = rewardsPerWeek ?? (await this.weeklyRewardsSplittingAbi.totalRewardsForWeek( @@ -215,9 +201,35 @@ export class FarmComputeServiceV2 throw new Error('Invalid boosted yields rewards'); } - const boostedYieldsFactors = await this.farmAbi.boostedYieldsFactors( - scAddress, - ); + const [currentWeek, boostedYieldsFactors, userEnergyForWeek] = + await Promise.all([ + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.farmAbi.boostedYieldsFactors(scAddress), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + ]); + + let [totalEnergyForWeek, liquidity] = await Promise.all([ + this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), + this.farmAbi.userTotalFarmPosition(scAddress, userAddress), + ]); + let farmTokenSupply = + week === currentWeek + ? await this.farmAbi.farmTokenSupply(scAddress) + : await this.farmAbi.farmSupplyForWeek(scAddress, week); + + totalEnergyForWeek = new BigNumber(totalEnergyForWeek) + .plus(additionalUserEnergyAmount) + .toFixed(); + liquidity = new BigNumber(liquidity) + .plus(additionalUserFarmAmount) + .toFixed(); + farmTokenSupply = new BigNumber(farmTokenSupply) + .plus(additionalUserFarmAmount) + .toFixed(); const userHasMinEnergy = new BigNumber( userEnergyForWeek.amount, @@ -233,11 +245,6 @@ export class FarmComputeServiceV2 return userRewardsForWeek; } - const farmTokenSupply = - week === currentWeek - ? await this.farmAbi.farmTokenSupply(scAddress) - : await this.farmAbi.farmSupplyForWeek(scAddress, week); - const rewardForWeek = rewardsForWeek[0]; const weeklyRewardsAmount = new BigNumber(rewardForWeek.amount); @@ -293,11 +300,12 @@ export class FarmComputeServiceV2 async computeUserCurentBoostedAPR( scAddress: string, userAddress: string, + additionalUserFarmAmount = '0', + additionalUserEnergy = '0', ): Promise { const [ currentWeek, boostedRewardsPerWeek, - userTotalFarmPosition, farmToken, farmedToken, farmingTokenPriceUSD, @@ -305,17 +313,26 @@ export class FarmComputeServiceV2 ] = await Promise.all([ this.weekTimeKeepingAbi.currentWeek(scAddress), this.computeBoostedRewardsPerWeek(scAddress), - this.farmAbi.userTotalFarmPosition(scAddress, userAddress), this.farmService.getFarmToken(scAddress), this.farmService.getFarmedToken(scAddress), this.farmingTokenPriceUSD(scAddress), this.farmedTokenPriceUSD(scAddress), ]); + let userTotalFarmPosition = await this.farmAbi.userTotalFarmPosition( + scAddress, + userAddress, + ); + userTotalFarmPosition = new BigNumber(userTotalFarmPosition) + .plus(additionalUserFarmAmount) + .toFixed(); + const userRewardsPerWeek = await this.computeUserRewardsForWeek( scAddress, userAddress, currentWeek, + additionalUserFarmAmount, + additionalUserEnergy, boostedRewardsPerWeek, ); @@ -339,12 +356,11 @@ export class FarmComputeServiceV2 async computeUserMaxBoostedAPR( scAddress: string, userAddress: string, + additionalUserFarmAmount = '0', ): Promise { const [ boostedRewardsPerWeek, boostedYieldsFactors, - farmTokenSupply, - userTotalFarmPosition, farmToken, farmedToken, farmingTokenPriceUSD, @@ -352,14 +368,23 @@ export class FarmComputeServiceV2 ] = await Promise.all([ this.computeBoostedRewardsPerWeek(scAddress), this.farmAbi.boostedYieldsFactors(scAddress), - this.farmAbi.farmTokenSupply(scAddress), - this.farmAbi.userTotalFarmPosition(scAddress, userAddress), this.farmService.getFarmToken(scAddress), this.farmService.getFarmedToken(scAddress), this.farmingTokenPriceUSD(scAddress), this.farmedTokenPriceUSD(scAddress), ]); + let [farmTokenSupply, userTotalFarmPosition] = await Promise.all([ + this.farmAbi.farmTokenSupply(scAddress), + this.farmAbi.userTotalFarmPosition(scAddress, userAddress), + ]); + farmTokenSupply = new BigNumber(farmTokenSupply) + .plus(additionalUserFarmAmount) + .toFixed(); + userTotalFarmPosition = new BigNumber(userTotalFarmPosition) + .plus(additionalUserFarmAmount) + .toFixed(); + const userMaxRewardsPerWeek = new BigNumber( boostedRewardsPerWeek[0].amount, ) From 585cdd84ad291e65e0425ce82224ca4952a117a3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Sep 2024 17:41:32 +0300 Subject: [PATCH 287/313] MEX-514: update compute staking boosted rewards with custom user information Signed-off-by: Claudiu Lataretu --- .../services/staking.compute.service.ts | 118 ++++++++++++------ src/modules/staking/staking.resolver.ts | 21 ++++ 2 files changed, 99 insertions(+), 40 deletions(-) diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index b0895cca9..c23b33118 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -489,26 +489,12 @@ export class StakingComputeService { scAddress: string, userAddress: string, week: number, + additionalUserStakeAmount = '0', + additionalUserEnergy = '0', rewardsPerWeek?: EsdtTokenPayment[], ): Promise { const userRewardsForWeek = []; - const [currentWeek, userEnergyForWeek, totalEnergyForWeek, liquidity] = - await Promise.all([ - this.weekTimeKeepingAbi.currentWeek(scAddress), - this.weeklyRewardsSplittingAbi.userEnergyForWeek( - scAddress, - userAddress, - week, - ), - this.weeklyRewardsSplittingAbi.totalEnergyForWeek( - scAddress, - week, - ), - - this.stakingAbi.userTotalStakePosition(scAddress, userAddress), - ]); - const rewardsForWeek = rewardsPerWeek ?? (await this.weeklyRewardsSplittingAbi.totalRewardsForWeek( @@ -524,9 +510,39 @@ export class StakingComputeService { throw new Error('Invalid boosted yields rewards'); } - const boostedYieldsFactors = await this.stakingAbi.boostedYieldsFactors( - scAddress, - ); + const [currentWeek, boostedYieldsFactors, userEnergyForWeek] = + await Promise.all([ + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.stakingAbi.boostedYieldsFactors(scAddress), + this.weeklyRewardsSplittingAbi.userEnergyForWeek( + scAddress, + userAddress, + week, + ), + ]); + + let [totalEnergyForWeek, liquidity] = await Promise.all([ + this.weeklyRewardsSplittingAbi.totalEnergyForWeek(scAddress, week), + + this.stakingAbi.userTotalStakePosition(scAddress, userAddress), + ]); + let farmTokenSupply = + week === currentWeek + ? await this.stakingAbi.farmTokenSupply(scAddress) + : await this.stakingAbi.farmSupplyForWeek(scAddress, week); + + userEnergyForWeek.amount = new BigNumber(userEnergyForWeek.amount) + .plus(additionalUserEnergy) + .toFixed(); + totalEnergyForWeek = new BigNumber(totalEnergyForWeek) + .plus(additionalUserEnergy) + .toFixed(); + liquidity = new BigNumber(liquidity) + .plus(additionalUserStakeAmount) + .toFixed(); + farmTokenSupply = new BigNumber(farmTokenSupply) + .plus(additionalUserStakeAmount) + .toFixed(); const userHasMinEnergy = new BigNumber( userEnergyForWeek.amount, @@ -542,11 +558,6 @@ export class StakingComputeService { return userRewardsForWeek; } - const farmTokenSupply = - week === currentWeek - ? await this.stakingAbi.farmTokenSupply(scAddress) - : await this.stakingAbi.farmSupplyForWeek(scAddress, week); - const rewardForWeek = rewardsForWeek[0]; const weeklyRewardsAmount = new BigNumber(rewardForWeek.amount); @@ -602,18 +613,31 @@ export class StakingComputeService { async computeUserCurentBoostedAPR( scAddress: string, userAddress: string, + additionalUserStakeAmount = '0', + additionalUserEnergy = '0', ): Promise { - const [currentWeek, boostedRewardsPerWeek, userTotalStakePosition] = - await Promise.all([ - this.weekTimeKeepingAbi.currentWeek(scAddress), - this.computeBoostedRewardsPerWeek(scAddress), - this.stakingAbi.userTotalStakePosition(scAddress, userAddress), - ]); + const [currentWeek, boostedRewardsPerWeek] = await Promise.all([ + this.weekTimeKeepingAbi.currentWeek(scAddress), + this.computeBoostedRewardsPerWeek( + scAddress, + additionalUserStakeAmount, + ), + ]); + let userTotalStakePosition = + await this.stakingAbi.userTotalStakePosition( + scAddress, + userAddress, + ); + userTotalStakePosition = new BigNumber(userTotalStakePosition) + .plus(additionalUserStakeAmount) + .toFixed(); const userRewardsPerWeek = await this.computeUserRewardsForWeek( scAddress, userAddress, currentWeek, + additionalUserStakeAmount, + additionalUserEnergy, boostedRewardsPerWeek, ); @@ -626,18 +650,28 @@ export class StakingComputeService { async computeUserMaxBoostedAPR( scAddress: string, userAddress: string, + additionalUserStakeAmount = '0', ): Promise { - const [ - boostedRewardsPerWeek, - boostedYieldsFactors, - farmTokenSupply, - userTotalStakePosition, - ] = await Promise.all([ - this.computeBoostedRewardsPerWeek(scAddress), - this.stakingAbi.boostedYieldsFactors(scAddress), + const [boostedRewardsPerWeek, boostedYieldsFactors] = await Promise.all( + [ + this.computeBoostedRewardsPerWeek( + scAddress, + additionalUserStakeAmount, + ), + this.stakingAbi.boostedYieldsFactors(scAddress), + ], + ); + + let [farmTokenSupply, userTotalStakePosition] = await Promise.all([ this.stakingAbi.farmTokenSupply(scAddress), this.stakingAbi.userTotalStakePosition(scAddress, userAddress), ]); + farmTokenSupply = new BigNumber(farmTokenSupply) + .plus(additionalUserStakeAmount) + .toFixed(); + userTotalStakePosition = new BigNumber(userTotalStakePosition) + .plus(additionalUserStakeAmount) + .toFixed(); const userMaxRewardsPerWeek = new BigNumber( boostedRewardsPerWeek[0].amount, @@ -654,21 +688,25 @@ export class StakingComputeService { async computeBoostedRewardsPerWeek( scAddress: string, + additionalUserStakeAmount = '0', ): Promise { const [ rewardTokenID, rewardsPerBlock, annualPercentageRewards, - farmTokenSupply, boostedYieldsRewardsPercentage, ] = await Promise.all([ this.stakingAbi.rewardTokenID(scAddress), this.stakingAbi.perBlockRewardsAmount(scAddress), this.stakingAbi.annualPercentageRewards(scAddress), - this.stakingAbi.farmTokenSupply(scAddress), this.stakingAbi.boostedYieldsRewardsPercenatage(scAddress), ]); + let farmTokenSupply = await this.stakingAbi.farmTokenSupply(scAddress); + farmTokenSupply = new BigNumber(farmTokenSupply) + .plus(additionalUserStakeAmount) + .toFixed(); + const rewardsPerBlockAPRBound = new BigNumber(farmTokenSupply) .multipliedBy(annualPercentageRewards) .dividedBy(constantsConfig.MAX_PERCENT) diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index 64099386f..e4907bee6 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -49,20 +49,41 @@ export class StakingBoostedRewardsResolver { @ResolveField() async curentBoostedAPR( @Parent() parent: StakingBoostedRewardsModel, + @Args('additionalUserFarmAmount', { + type: () => String, + nullable: true, + defaultValue: '0', + }) + additionalUserFarmAmount: string, + @Args('additionalUserEnergy', { + type: () => String, + nullable: true, + defaultValue: '0', + }) + additionalUserEnergy: string, ): Promise { return this.stakingCompute.computeUserCurentBoostedAPR( parent.farmAddress, parent.userAddress, + additionalUserFarmAmount, + additionalUserEnergy, ); } @ResolveField() async maximumBoostedAPR( @Parent() parent: StakingBoostedRewardsModel, + @Args('additionalUserFarmAmount', { + type: () => String, + nullable: true, + defaultValue: '0', + }) + additionalUserFarmAmount: string, ): Promise { return this.stakingCompute.computeUserMaxBoostedAPR( parent.farmAddress, parent.userAddress, + additionalUserFarmAmount, ); } } From 3555fa2e4c6113594837fab645029b2c7faafbb6 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 10 Sep 2024 18:14:52 +0300 Subject: [PATCH 288/313] SERVICES-2569: add bulk getter methods for various pair fields - bulk getter methods for pairs state, fee state, locked value, deployed at, trades count, hasFarms, hasDualFarms --- src/modules/pair/services/pair.service.ts | 71 ++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/modules/pair/services/pair.service.ts b/src/modules/pair/services/pair.service.ts index 38509f429..a0e3a746d 100644 --- a/src/modules/pair/services/pair.service.ts +++ b/src/modules/pair/services/pair.service.ts @@ -74,17 +74,84 @@ export class PairService { : await this.tokenService.tokenMetadata(lpTokenID); } - async getAllLpTokens(pairAddresses: string[]): Promise { - const tokenIDs = await getAllKeys( + async getAllLpTokensIds(pairAddresses: string[]): Promise { + return await getAllKeys( this.cachingService, pairAddresses, 'pair.lpTokenID', this.pairAbi.lpTokenID.bind(this.pairAbi), ); + } + + async getAllLpTokens(pairAddresses: string[]): Promise { + const tokenIDs = await this.getAllLpTokensIds(pairAddresses); return this.tokenService.getAllTokensMetadata(tokenIDs); } + async getAllStates(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.state', + this.pairAbi.state.bind(this.pairAbi), + ); + } + + async getAllFeeStates(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.feeState', + this.pairAbi.feeState.bind(this.pairAbi), + ); + } + + async getAllLockedValueUSD(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.lockedValueUSD', + this.pairCompute.lockedValueUSD.bind(this.pairCompute), + ); + } + + async getAllDeployedAt(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.deployedAt', + this.pairCompute.deployedAt.bind(this.pairCompute), + ); + } + + async getAllTradesCount(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.tradesCount', + this.pairCompute.tradesCount.bind(this.pairCompute), + ); + } + + async getAllHasFarms(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.hasFarms', + this.pairCompute.hasFarms.bind(this.pairCompute), + ); + } + + async getAllHasDualFarms(pairAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + pairAddresses, + 'pair.hasDualFarms', + this.pairCompute.hasDualFarms.bind(this.pairCompute), + ); + } + async getAmountOut( pairAddress: string, tokenInID: string, From aab29e9b86670e82943fb43ba1ecb1835875711d Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 10 Sep 2024 18:16:39 +0300 Subject: [PATCH 289/313] SERVICES-2569: refactor pair abi and compute loader services - use bulk getter methods inside pair abi and compute dataloaders --- src/modules/pair/services/pair.abi.loader.ts | 14 +------ .../pair/services/pair.compute.loader.ts | 37 ++++--------------- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/src/modules/pair/services/pair.abi.loader.ts b/src/modules/pair/services/pair.abi.loader.ts index 6776ada18..c12bd8772 100644 --- a/src/modules/pair/services/pair.abi.loader.ts +++ b/src/modules/pair/services/pair.abi.loader.ts @@ -89,23 +89,13 @@ export class PairAbiLoader { public readonly stateLoader = new DataLoader( async (addresses: string[]) => { - return getAllKeys( - this.cacheService, - addresses, - 'pair.state', - this.pairAbi.state.bind(this.pairAbi), - ); + return this.pairService.getAllStates(addresses); }, ); public readonly feeStateLoader = new DataLoader( async (addresses: string[]) => { - return getAllKeys( - this.cacheService, - addresses, - 'pair.feeState', - this.pairAbi.feeState.bind(this.pairAbi), - ); + return this.pairService.getAllFeeStates(addresses); }, ); diff --git a/src/modules/pair/services/pair.compute.loader.ts b/src/modules/pair/services/pair.compute.loader.ts index d95ac8657..e631338e0 100644 --- a/src/modules/pair/services/pair.compute.loader.ts +++ b/src/modules/pair/services/pair.compute.loader.ts @@ -3,6 +3,7 @@ import { PairComputeService } from './pair.compute.service'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { getAllKeys } from 'src/utils/get.many.utils'; import DataLoader from 'dataloader'; +import { PairService } from './pair.service'; @Injectable({ scope: Scope.REQUEST, @@ -10,6 +11,7 @@ import DataLoader from 'dataloader'; export class PairComputeLoader { constructor( private readonly pairCompute: PairComputeService, + private readonly pairService: PairService, private readonly cacheService: CacheService, ) {} @@ -94,12 +96,7 @@ export class PairComputeLoader { public readonly lockedValueUSDLoader = new DataLoader( async (addresses: string[]) => { - return await getAllKeys( - this.cacheService, - addresses, - 'pair.lockedValueUSD', - this.pairCompute.lockedValueUSD.bind(this.pairCompute), - ); + return await this.pairService.getAllLockedValueUSD(addresses); }, ); @@ -161,45 +158,25 @@ export class PairComputeLoader { public readonly hasFarmsLoader = new DataLoader( async (addresses: string[]) => { - return await getAllKeys( - this.cacheService, - addresses, - 'pair.hasFarms', - this.pairCompute.hasFarms.bind(this.pairCompute), - ); + return await this.pairService.getAllHasFarms(addresses); }, ); public readonly hasDualFarmsLoader = new DataLoader( async (addresses: string[]) => { - return await getAllKeys( - this.cacheService, - addresses, - 'pair.hasDualFarms', - this.pairCompute.hasDualFarms.bind(this.pairCompute), - ); + return await this.pairService.getAllHasDualFarms(addresses); }, ); public readonly tradesCountLoader = new DataLoader( async (addresses: string[]) => { - return await getAllKeys( - this.cacheService, - addresses, - 'pair.tradesCount', - this.pairCompute.tradesCount.bind(this.pairCompute), - ); + return await this.pairService.getAllTradesCount(addresses); }, ); public readonly deployedAtLoader = new DataLoader( async (addresses: string[]) => { - return await getAllKeys( - this.cacheService, - addresses, - 'pair.deployedAt', - this.pairCompute.deployedAt.bind(this.pairCompute), - ); + return await this.pairService.getAllDeployedAt(addresses); }, ); } From b603d88b205f04500a3afe633d0d9cd89c740a72 Mon Sep 17 00:00:00 2001 From: hschiau Date: Tue, 10 Sep 2024 18:20:43 +0300 Subject: [PATCH 290/313] SERVICES-2569: refactor pairs filtering and sorting to use bulk getters --- .../pair/services/pair.filtering.service.ts | 69 +++++++------------ src/modules/router/services/router.service.ts | 43 ++++-------- 2 files changed, 40 insertions(+), 72 deletions(-) diff --git a/src/modules/pair/services/pair.filtering.service.ts b/src/modules/pair/services/pair.filtering.service.ts index 6212b4adb..6a980a724 100644 --- a/src/modules/pair/services/pair.filtering.service.ts +++ b/src/modules/pair/services/pair.filtering.service.ts @@ -25,10 +25,8 @@ export class PairFilteringService { return pairsMetadata; } - const lpTokensIDs = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairAbi.lpTokenID(pairMetadata.address), - ), + const lpTokensIDs = await this.pairService.getAllLpTokensIds( + pairsMetadata.map((pairMetadata) => pairMetadata.address), ); const filteredPairsMetadata = []; @@ -96,16 +94,15 @@ export class PairFilteringService { } const searchTerm = pairFilter.searchToken.toUpperCase().trim(); + const pairsAddresses = pairsMetadata.map( + (pairMetadata) => pairMetadata.address, + ); - const pairsFirstToken = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairService.getFirstToken(pairMetadata.address), - ), + const pairsFirstToken = await this.pairService.getAllFirstTokens( + pairsAddresses, ); - const pairsSecondToken = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairService.getSecondToken(pairMetadata.address), - ), + const pairsSecondToken = await this.pairService.getAllSecondTokens( + pairsAddresses, ); const filteredPairs: PairMetadata[] = []; @@ -136,10 +133,8 @@ export class PairFilteringService { return pairsMetadata; } - const lpTokensIDs = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairAbi.lpTokenID(pairMetadata.address), - ), + const lpTokensIDs = await this.pairService.getAllLpTokensIds( + pairsMetadata.map((pairMetadata) => pairMetadata.address), ); return pairsMetadata.filter((_, index) => @@ -176,10 +171,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsStates = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairAbi.state(pairMetadata.address), - ), + const pairsStates = await this.pairService.getAllStates( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter((_, index) => { @@ -202,10 +195,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsFeeStates = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairAbi.feeState(pairMetadata.address), - ), + const pairsFeeStates = await this.pairService.getAllFeeStates( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter( @@ -241,10 +232,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsLiquidityUSD = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairCompute.lockedValueUSD(pairMetadata.address), - ), + const pairsLiquidityUSD = await this.pairService.getAllLockedValueUSD( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter((_, index) => { @@ -261,10 +250,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsTradesCount = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairCompute.tradesCount(pairMetadata.address), - ), + const pairsTradesCount = await this.pairService.getAllTradesCount( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter( @@ -283,10 +270,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsHasFarms = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairCompute.hasFarms(pairMetadata.address), - ), + const pairsHasFarms = await this.pairService.getAllHasFarms( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter( @@ -305,10 +290,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsHasDualFarms = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairCompute.hasDualFarms(pairMetadata.address), - ), + const pairsHasDualFarms = await this.pairService.getAllHasDualFarms( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter( @@ -324,10 +307,8 @@ export class PairFilteringService { return pairsMetadata; } - const pairsDeployedAt = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairCompute.deployedAt(pairMetadata.address), - ), + const pairsDeployedAt = await this.pairService.getAllDeployedAt( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter( diff --git a/src/modules/router/services/router.service.ts b/src/modules/router/services/router.service.ts index 224150398..bcc56d13c 100644 --- a/src/modules/router/services/router.service.ts +++ b/src/modules/router/services/router.service.ts @@ -17,8 +17,8 @@ import { CollectionType } from 'src/modules/common/collection.type'; import { PairsMetadataBuilder } from 'src/modules/pair/services/pair.metadata.builder'; import { PairFilteringService } from 'src/modules/pair/services/pair.filtering.service'; import { SortingOrder } from 'src/modules/common/page.data'; -import { getAllKeys } from 'src/utils/get.many.utils'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { PairService } from 'src/modules/pair/services/pair.service'; @Injectable() export class RouterService { @@ -28,6 +28,7 @@ export class RouterService { private readonly pairCompute: PairComputeService, private readonly pairFilteringService: PairFilteringService, private readonly cacheService: CacheService, + private readonly pairService: PairService, ) {} async getFactory(): Promise { @@ -166,11 +167,8 @@ export class RouterService { private async filterPairsByIssuedLpTokenRaw( pairsMetadata: PairMetadata[], ): Promise { - const lpTokensIDs = await getAllKeys( - this.cacheService, + const lpTokensIDs = await this.pairService.getAllLpTokensIds( pairsMetadata.map((pair) => pair.address), - 'pair.lpTokenID', - this.pairAbi.lpTokenID.bind(this.pairAbi), ); const filteredPairsMetadata = []; @@ -196,10 +194,9 @@ export class RouterService { return pairsMetadata; } - const promises = pairsMetadata.map((pairMetadata) => - this.pairAbi.state(pairMetadata.address), + const pairsStates = await this.pairService.getAllStates( + pairsMetadata.map((pair) => pair.address), ); - const pairsStates = await Promise.all(promises); const filteredPairsMetadata = []; for (let index = 0; index < pairsStates.length; index++) { @@ -222,10 +219,8 @@ export class RouterService { return pairsMetadata; } - const pairsFeeStates = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairAbi.feeState(pairMetadata.address), - ), + const pairsFeeStates = await this.pairService.getAllFeeStates( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter( @@ -261,10 +256,8 @@ export class RouterService { return pairsMetadata; } - const pairsLiquidityUSD = await Promise.all( - pairsMetadata.map((pairMetadata) => - this.pairCompute.lockedValueUSD(pairMetadata.address), - ), + const pairsLiquidityUSD = await this.pairService.getAllLockedValueUSD( + pairsMetadata.map((pair) => pair.address), ); return pairsMetadata.filter((_, index) => { @@ -291,10 +284,8 @@ export class RouterService { switch (sortField) { case PairSortableFields.DEPLOYED_AT: - sortFieldData = await Promise.all( - pairsMetadata.map((pair) => - this.pairCompute.deployedAt(pair.address), - ), + sortFieldData = await this.pairService.getAllDeployedAt( + pairsMetadata.map((pair) => pair.address), ); break; case PairSortableFields.FEES_24: @@ -305,17 +296,13 @@ export class RouterService { ); break; case PairSortableFields.TRADES_COUNT: - sortFieldData = await Promise.all( - pairsMetadata.map((pair) => - this.pairCompute.tradesCount(pair.address), - ), + sortFieldData = await this.pairService.getAllTradesCount( + pairsMetadata.map((pair) => pair.address), ); break; case PairSortableFields.TVL: - sortFieldData = await Promise.all( - pairsMetadata.map((pair) => - this.pairCompute.lockedValueUSD(pair.address), - ), + sortFieldData = await this.pairService.getAllLockedValueUSD( + pairsMetadata.map((pair) => pair.address), ); break; case PairSortableFields.VOLUME_24: From 6d99a57ebb888eea7b558ff79182a022d9da7701 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 10 Sep 2024 18:40:56 +0300 Subject: [PATCH 291/313] MEX-514: fix farm v2 compute service unit tests Signed-off-by: Claudiu Lataretu --- src/modules/farm/mocks/farm.v2.abi.service.mock.ts | 7 +++++++ .../farm/specs/farm.v2.compute.service.spec.ts | 14 ++++++++++++-- src/modules/farm/v2/services/interfaces.ts | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts index 559085e87..5796882bb 100644 --- a/src/modules/farm/mocks/farm.v2.abi.service.mock.ts +++ b/src/modules/farm/mocks/farm.v2.abi.service.mock.ts @@ -73,6 +73,13 @@ export class FarmAbiServiceMockV2 async farmPositionMigrationNonce(farmAddress: string): Promise { return 10; } + + async farmSupplyForWeek( + farmAddress: string, + week: number, + ): Promise { + return '2'; + } } export const FarmAbiServiceProviderV2 = { diff --git a/src/modules/farm/specs/farm.v2.compute.service.spec.ts b/src/modules/farm/specs/farm.v2.compute.service.spec.ts index 9b3d45d2d..b0fbef5be 100644 --- a/src/modules/farm/specs/farm.v2.compute.service.spec.ts +++ b/src/modules/farm/specs/farm.v2.compute.service.spec.ts @@ -29,6 +29,7 @@ import { FarmAbiServiceV2 } from '../v2/services/farm.v2.abi.service'; import { Address } from '@multiversx/sdk-core/out'; import { ContextGetterService } from 'src/services/context/context.getter.service'; import { ElasticSearchModule } from 'src/services/elastic-search/elastic.search.module'; +import { EsdtTokenPayment } from 'src/models/esdtTokenPayment.model'; describe('FarmServiceV2', () => { let module: TestingModule; @@ -131,13 +132,22 @@ describe('FarmServiceV2', () => { totalLockedTokens: '1', lastUpdateEpoch: 256, }); + jest.spyOn( + weeklyRewardsSplittingAbi, + 'totalRewardsForWeek', + ).mockResolvedValue([ + new EsdtTokenPayment({ + amount: '60480000000000000000000', + tokenID: 'MEX-123456', + }), + ]); jest.spyOn(farmAbi, 'farmTokenSupply').mockResolvedValue('2'); jest.spyOn(farmAbi, 'rewardsPerBlock').mockResolvedValue( '1000000000000000000', ); jest.spyOn(farmAbi, 'userTotalFarmPosition').mockResolvedValue('2'); - const accumulatedRewards = await service.computeUserAccumulatedRewards( + const accumulatedRewards = await service.computeUserRewardsForWeek( Address.fromBech32( 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqsdtp6mh', ).bech32(), @@ -145,6 +155,6 @@ describe('FarmServiceV2', () => { 1, ); - expect(accumulatedRewards).toEqual('60480000000000000000000'); + expect(accumulatedRewards[0].amount).toEqual('60480000000000000000000'); }); }); diff --git a/src/modules/farm/v2/services/interfaces.ts b/src/modules/farm/v2/services/interfaces.ts index 69a9f7fe7..0436e3da4 100644 --- a/src/modules/farm/v2/services/interfaces.ts +++ b/src/modules/farm/v2/services/interfaces.ts @@ -1,4 +1,3 @@ -import { TokenDistributionModel } from 'src/submodules/weekly-rewards-splitting/models/weekly-rewards-splitting.model'; import { IFarmAbiService, IFarmComputeService, @@ -25,6 +24,7 @@ export interface IFarmAbiServiceV2 extends IFarmAbiService { userAddress: string, ): Promise; farmPositionMigrationNonce(farmAddress: string): Promise; + farmSupplyForWeek(farmAddress: string, week: number): Promise; } export interface IFarmComputeServiceV2 extends IFarmComputeService { From 7e411b6eebe446fe218ab02a2defa1c67b7d9a9a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 21 Aug 2024 15:34:30 +0300 Subject: [PATCH 292/313] MEX-480: increase gas limits for failed transactions Signed-off-by: Claudiu Lataretu --- src/config/default.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index a862696ec..08c24ff87 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -416,7 +416,7 @@ "default": 40000000, "withTokenMerge": 60000000 }, - "claimDualYield": 55000000, + "claimDualYield": 75000000, "unstakeFarmTokens": 75000000 }, "metabonding": { @@ -501,10 +501,10 @@ }, "positionCreator": { "singleToken": { - "liquidityPosition": 57500000, + "liquidityPosition": 70000000, "farmPosition": 87000000, - "dualFarmPosition": 115000000, - "stakingPosition": 80000000 + "dualFarmPosition": 130000000, + "stakingPosition": 100000000 }, "dualTokens": { "farmPosition": 50000000, From ea1530d4319d1646b909c4361d6cd6ce2417ee8e Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Aug 2024 11:44:45 +0300 Subject: [PATCH 293/313] MEX-480: not enough gas for enterFarmProxy Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 08c24ff87..88020d222 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -341,7 +341,7 @@ }, "v2": { "enterFarm": { - "default": 25000000, + "default": 35000000, "withTokenMerge": 55000000 }, "compoundRewards": 21600000, From 2e9f8a560b73a7fb512f49b76c11589c0a4b8be9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 18:54:57 +0300 Subject: [PATCH 294/313] MEX-480: use dynamic gas limit for position creation single tokens Signed-off-by: Claudiu Lataretu --- src/config/default.json | 12 +++--- .../services/position.creator.transaction.ts | 40 +++++++++++++++---- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/config/default.json b/src/config/default.json index 88020d222..cce149c98 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -388,7 +388,7 @@ "default": 20000000, "withTokenMerge": 23000000 }, - "unstakeFarm": 18500000, + "unstakeFarm": 25000000, "unbondFarm": 8000000, "claimRewards": 40000000, "claimBoostedRewards": 30000000, @@ -470,7 +470,7 @@ }, "feesCollector": { "baseClaimRewards": 20000000, - "claimRewardsPerWeek": 20000000, + "claimRewardsPerWeek": 25000000, "addKnownContracts": 10000000, "addKnownTokens": 10000000 }, @@ -501,10 +501,10 @@ }, "positionCreator": { "singleToken": { - "liquidityPosition": 70000000, - "farmPosition": 87000000, - "dualFarmPosition": 130000000, - "stakingPosition": 100000000 + "liquidityPosition": 10000000, + "farmPosition": 15000000, + "dualFarmPosition": 20000000, + "stakingPosition": 10000000 }, "dualTokens": { "farmPosition": 50000000, diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index 6cb7c5130..e9c9e574f 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -28,6 +28,7 @@ import { WrapTransactionsService } from 'src/modules/wrapping/services/wrap.tran import { ProxyFarmAbiService } from 'src/modules/proxy/services/proxy-farm/proxy.farm.abi.service'; import { EnergyAbiService } from 'src/modules/energy/services/energy.abi.service'; import { WrapAbiService } from 'src/modules/wrapping/services/wrap.abi.service'; +import { FarmVersion } from 'src/modules/farm/models/farm.model'; @Injectable() export class PositionCreatorTransactionService { @@ -98,11 +99,15 @@ export class PositionCreatorTransactionService { ]); } + const gasLimit = + gasConfig.positionCreator.singleToken.liquidityPosition + + gasConfig.pairs.addLiquidity + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + interaction = interaction .withSender(Address.fromBech32(sender)) - .withGasLimit( - gasConfig.positionCreator.singleToken.liquidityPosition, - ) + .withGasLimit(gasLimit) .withChainID(mxConfig.chainID); if (payment.tokenIdentifier === mxConfig.EGLDIdentifier) { @@ -189,11 +194,18 @@ export class PositionCreatorTransactionService { ]; } + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + let interaction = contract.methodsExplicit .createFarmPosFromSingleToken(endpointArgs) .withSender(Address.fromBech32(sender)) .withChainID(mxConfig.chainID) - .withGasLimit(gasConfig.positionCreator.singleToken.farmPosition); + .withGasLimit(gasLimit); if ( payments[0].tokenIdentifier === mxConfig.EGLDIdentifier && @@ -280,6 +292,14 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); + const gasLimit = + gasConfig.positionCreator.singleToken.dualFarmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + let interaction = contract.methodsExplicit .createMetastakingPosFromSingleToken([ new AddressValue(Address.fromBech32(stakingProxyAddress)), @@ -288,9 +308,7 @@ export class PositionCreatorTransactionService { ...swapRouteArgs, ]) .withSender(Address.fromBech32(sender)) - .withGasLimit( - gasConfig.positionCreator.singleToken.dualFarmPosition, - ) + .withGasLimit(gasLimit) .withChainID(mxConfig.chainID); if ( @@ -358,6 +376,12 @@ export class PositionCreatorTransactionService { const multiSwapArgs = this.serializeSwapRouteArgs(swapRoute); + const gasLimit = + gasConfig.positionCreator.singleToken.stakingPosition + + gasConfig.stake.stakeFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoute.pairs.length; + const contract = await this.mxProxy.getPostitionCreatorContract(); let interaction = contract.methodsExplicit .createFarmStakingPosFromSingleToken([ @@ -372,7 +396,7 @@ export class PositionCreatorTransactionService { ...multiSwapArgs, ]) .withSender(Address.fromBech32(sender)) - .withGasLimit(gasConfig.positionCreator.singleToken.stakingPosition) + .withGasLimit(gasLimit) .withChainID(mxConfig.chainID); if ( From 91555af0b32f8a6d08812abbabc5694c27974c86 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 18:56:08 +0300 Subject: [PATCH 295/313] MEX-480: increase gas limit for position creator dual tokens Signed-off-by: Claudiu Lataretu --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index cce149c98..e409aac4a 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -509,7 +509,7 @@ "dualTokens": { "farmPosition": 50000000, "farmPositionProxy": 60000000, - "dualFarmPosition": 70000000, + "dualFarmPosition": 100000000, "exitFarm": 50000000 }, "energyPosition": 13000000 From de3bd81b98e5619319d4fed0c277fc4fe07901b2 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Tue, 20 Aug 2024 14:59:32 +0300 Subject: [PATCH 296/313] MEX-513: Add lp token derived USD refresh into pair cache warmer Signed-off-by: Claudiu Lataretu --- src/services/crons/pair.cache.warmer.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index 495c11099..b20b59734 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -298,6 +298,9 @@ export class PairCacheWarmerService { } for (const pairMetadata of pairsMetadata) { + const lpTokenID = await this.pairAbi.lpTokenID( + pairMetadata.address, + ); const [ firstTokenPrice, firstTokenPriceUSD, @@ -343,6 +346,7 @@ export class PairCacheWarmerService { pairMetadata.address, lpTokenPriceUSD, ), + this.tokenSetter.setDerivedUSD(lpTokenID, lpTokenPriceUSD), ]); await this.deleteCacheKeys(cachedKeys); } From c7d48f835cccdcbe3f302c062542308e4c5f8b01 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 09:48:09 +0300 Subject: [PATCH 297/313] SERVICES-2577: add rewardsDepleted filter to filteredStakingFarms query --- src/modules/staking/models/staking.args.ts | 2 + .../services/staking.filtering.service.ts | 38 +++++++++++-- .../staking/services/staking.service.ts | 57 +++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/modules/staking/models/staking.args.ts b/src/modules/staking/models/staking.args.ts index a72342290..45eeb407c 100644 --- a/src/modules/staking/models/staking.args.ts +++ b/src/modules/staking/models/staking.args.ts @@ -39,6 +39,8 @@ export class ClaimRewardsWithNewValueArgs extends GenericStakeFarmArgs { export class StakingFarmsFilter { @Field(() => String, { nullable: true }) searchToken?: string; + @Field({ nullable: true }) + rewardsDepleted?: boolean; } @InputType() diff --git a/src/modules/staking/services/staking.filtering.service.ts b/src/modules/staking/services/staking.filtering.service.ts index 84b8695ff..7885821e0 100644 --- a/src/modules/staking/services/staking.filtering.service.ts +++ b/src/modules/staking/services/staking.filtering.service.ts @@ -22,10 +22,8 @@ export class StakingFilteringService { const searchTerm = stakingFilter.searchToken.toUpperCase().trim(); - const farmingTokens = await Promise.all( - stakeAddresses.map((address) => - this.stakingService.getFarmingToken(address), - ), + const farmingTokens = await this.stakingService.getAllFarmingTokens( + stakeAddresses, ); const filteredAddresses: string[] = []; @@ -43,4 +41,36 @@ export class StakingFilteringService { return filteredAddresses; } + + async stakingFarmsByRewardsDepleted( + stakingFilter: StakingFarmsFilter, + stakeAddresses: string[], + ): Promise { + if ( + typeof stakingFilter.rewardsDepleted === 'undefined' || + stakingFilter.rewardsDepleted === null + ) { + return stakeAddresses; + } + + const allProduceRewardsEnabled = + await this.stakingService.getAllProduceRewardsEnabled( + stakeAddresses, + ); + const allAccumulatedRewards = + await this.stakingService.getAllAccumulatedRewards(stakeAddresses); + const allRewardCapacity = + await this.stakingService.getAllRewardCapacity(stakeAddresses); + + const rewardsDepleted = stakeAddresses.map( + (_, index) => + allAccumulatedRewards[index] === allRewardCapacity[index] || + !allProduceRewardsEnabled[index], + ); + + return stakeAddresses.filter( + (_, index) => + rewardsDepleted[index] === stakingFilter.rewardsDepleted, + ); + } } diff --git a/src/modules/staking/services/staking.service.ts b/src/modules/staking/services/staking.service.ts index dbba2b6a2..0e796c041 100644 --- a/src/modules/staking/services/staking.service.ts +++ b/src/modules/staking/services/staking.service.ts @@ -36,6 +36,8 @@ import { } from '../models/staking.args'; import { SortingOrder } from 'src/modules/common/page.data'; import { StakingFilteringService } from './staking.filtering.service'; +import { getAllKeys } from 'src/utils/get.many.utils'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class StakingService { @@ -51,6 +53,7 @@ export class StakingService { private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, @Inject(forwardRef(() => StakingFilteringService)) private readonly stakingFilteringService: StakingFilteringService, + private readonly cachingService: CacheService, ) {} async getFarmsStaking(): Promise { @@ -83,6 +86,12 @@ export class StakingService { farmsStakingAddresses, ); + farmsStakingAddresses = + await this.stakingFilteringService.stakingFarmsByRewardsDepleted( + filters, + farmsStakingAddresses, + ); + if (sorting) { farmsStakingAddresses = await this.sortFarms( farmsStakingAddresses, @@ -106,6 +115,37 @@ export class StakingService { }); } + async getAllAccumulatedRewards( + stakeAddresses: string[], + ): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.accumulatedRewards', + this.stakingAbi.accumulatedRewards.bind(this.stakingAbi), + ); + } + + async getAllRewardCapacity(stakeAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.rewardCapacity', + this.stakingAbi.rewardCapacity.bind(this.stakingAbi), + ); + } + + async getAllProduceRewardsEnabled( + stakeAddresses: string[], + ): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.produceRewardsEnabled', + this.stakingAbi.produceRewardsEnabled.bind(this.stakingAbi), + ); + } + async getFarmToken(stakeAddress: string): Promise { const farmTokenID = await this.stakingAbi.farmTokenID(stakeAddress); return await this.tokenService.getNftCollectionMetadata(farmTokenID); @@ -118,6 +158,23 @@ export class StakingService { return await this.tokenService.tokenMetadata(farmingTokenID); } + async getAllFarmingTokensIds(stakeAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.farmingTokenID', + this.stakingAbi.farmingTokenID.bind(this.stakingAbi), + ); + } + + async getAllFarmingTokens(stakeAddresses: string[]): Promise { + const farmingTokenIDs = await this.getAllFarmingTokensIds( + stakeAddresses, + ); + + return await this.tokenService.getAllTokensMetadata(farmingTokenIDs); + } + async getRewardToken(stakeAddress: string): Promise { const rewardTokenID = await this.stakingAbi.rewardTokenID(stakeAddress); return await this.tokenService.tokenMetadata(rewardTokenID); From 1f197050748089f9f9bfc6948b8cc036a50aacb6 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 10:19:07 +0300 Subject: [PATCH 298/313] SERVICES-2577: fixes after review - rename filter field --- src/modules/staking/models/staking.args.ts | 2 +- src/modules/staking/services/staking.filtering.service.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/staking/models/staking.args.ts b/src/modules/staking/models/staking.args.ts index 45eeb407c..5e73aa925 100644 --- a/src/modules/staking/models/staking.args.ts +++ b/src/modules/staking/models/staking.args.ts @@ -40,7 +40,7 @@ export class StakingFarmsFilter { @Field(() => String, { nullable: true }) searchToken?: string; @Field({ nullable: true }) - rewardsDepleted?: boolean; + rewardsDepletedPerFarm?: boolean; } @InputType() diff --git a/src/modules/staking/services/staking.filtering.service.ts b/src/modules/staking/services/staking.filtering.service.ts index 7885821e0..5ddc5aeb6 100644 --- a/src/modules/staking/services/staking.filtering.service.ts +++ b/src/modules/staking/services/staking.filtering.service.ts @@ -47,8 +47,8 @@ export class StakingFilteringService { stakeAddresses: string[], ): Promise { if ( - typeof stakingFilter.rewardsDepleted === 'undefined' || - stakingFilter.rewardsDepleted === null + typeof stakingFilter.rewardsDepletedPerFarm === 'undefined' || + stakingFilter.rewardsDepletedPerFarm === null ) { return stakeAddresses; } @@ -70,7 +70,7 @@ export class StakingFilteringService { return stakeAddresses.filter( (_, index) => - rewardsDepleted[index] === stakingFilter.rewardsDepleted, + rewardsDepleted[index] === stakingFilter.rewardsDepletedPerFarm, ); } } From ad26fb52cedd6a9e1368c8f3f9a2269888fcf706 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Sep 2024 13:46:38 +0300 Subject: [PATCH 299/313] MEX-480: fix position creator unit tests Signed-off-by: Claudiu Lataretu --- ...fees-collector.transaction.service.spec.ts | 4 +- .../services/position.creator.transaction.ts | 13 +- .../position.creator.transaction.spec.ts | 177 ++++++++++++++---- 3 files changed, 152 insertions(+), 42 deletions(-) diff --git a/src/modules/fees-collector/specs/fees-collector.transaction.service.spec.ts b/src/modules/fees-collector/specs/fees-collector.transaction.service.spec.ts index afccce951..9923e62e7 100644 --- a/src/modules/fees-collector/specs/fees-collector.transaction.service.spec.ts +++ b/src/modules/fees-collector/specs/fees-collector.transaction.service.spec.ts @@ -83,7 +83,7 @@ describe('FeesCollectorTransactionService', () => { new FeesCollectorTransactionModel({ transaction: { chainID: mxConfig.chainID, - gasLimit: 100000000, + gasLimit: 120000000, gasPrice: 1000000000, nonce: 0, receiver: scAddress.feesCollector, @@ -124,7 +124,7 @@ describe('FeesCollectorTransactionService', () => { new FeesCollectorTransactionModel({ transaction: { chainID: mxConfig.chainID, - gasLimit: 100000000, + gasLimit: 120000000, gasPrice: 1000000000, nonce: 0, receiver: scAddress.feesCollector, diff --git a/src/modules/position-creator/services/position.creator.transaction.ts b/src/modules/position-creator/services/position.creator.transaction.ts index e9c9e574f..dcfe160ab 100644 --- a/src/modules/position-creator/services/position.creator.transaction.ts +++ b/src/modules/position-creator/services/position.creator.transaction.ts @@ -292,13 +292,18 @@ export class PositionCreatorTransactionService { const contract = await this.mxProxy.getPostitionCreatorContract(); - const gasLimit = + let gasLimit = gasConfig.positionCreator.singleToken.dualFarmPosition + gasConfig.pairs.addLiquidity + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + - gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge + - gasConfig.pairs.swapTokensFixedInput.withFeeSwap * - swapRoutes[0].pairs.length; + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge; + + gasLimit = + swapRoutes.length < 1 + ? gasLimit + : gasLimit + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; let interaction = contract.methodsExplicit .createMetastakingPosFromSingleToken([ diff --git a/src/modules/position-creator/specs/position.creator.transaction.spec.ts b/src/modules/position-creator/specs/position.creator.transaction.spec.ts index 65d925c54..2ce27d1fc 100644 --- a/src/modules/position-creator/specs/position.creator.transaction.spec.ts +++ b/src/modules/position-creator/specs/position.creator.transaction.spec.ts @@ -37,6 +37,7 @@ import { StakingAbiService } from 'src/modules/staking/services/staking.abi.serv import { MXApiServiceProvider } from 'src/services/multiversx-communication/mx.api.service.mock'; import { SwapRouteModel } from 'src/modules/auto-router/models/auto-route.model'; import { PairFilteringService } from 'src/modules/pair/services/pair.filtering.service'; +import { FarmVersion } from 'src/modules/farm/models/farm.model'; describe('PositionCreatorTransaction', () => { let module: TestingModule; @@ -143,6 +144,12 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.liquidityPosition + + gasConfig.pairs.addLiquidity + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -153,8 +160,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.liquidityPosition, + gasLimit: gasLimit, data: encodeTransactionData( `ESDTTransfer@USDC-123456@100000000000000000000@createLpPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000012@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -206,6 +212,12 @@ describe('PositionCreatorTransaction', () => { 1440, ); + const gasLimit = + gasConfig.positionCreator.singleToken.liquidityPosition + + gasConfig.pairs.addLiquidity + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -216,8 +228,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.liquidityPosition, + gasLimit: gasLimit, data: encodeTransactionData( `ESDTTransfer@USDC-123456@100000000000000000000@createPairPosFromSingleToken@1440@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -267,6 +278,12 @@ describe('PositionCreatorTransaction', () => { 1440, ); + const gasLimit = + gasConfig.positionCreator.singleToken.liquidityPosition + + gasConfig.pairs.addLiquidity + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -277,8 +294,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.liquidityPosition, + gasLimit: gasLimit, data: encodeTransactionData( `createPairPosFromSingleToken@1440@47008144020574367766@47008144020574367766823`, ), @@ -381,6 +397,13 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -391,8 +414,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.farmPosition, + gasLimit: gasLimit, data: encodeTransactionData( `createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@47008144020574367766@47008144020574367766823`, ), @@ -442,6 +464,13 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -451,8 +480,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.farmPosition, + gasLimit: gasLimit, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -507,6 +535,13 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -534,8 +569,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.farmPosition, + gasLimit: gasLimit, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@47008144020574367766@47008144020574367766823`, ), @@ -590,6 +624,13 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -599,8 +640,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.farmPosition, + gasLimit: gasLimit, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@EGLDMEXFL-abcdef@01@100000000000000000000@createFarmPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000021@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -653,6 +693,13 @@ describe('PositionCreatorTransaction', () => { 1440, ); + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transactions).toEqual([ { nonce: 0, @@ -662,8 +709,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.farmPosition, + gasLimit: gasLimit, data: encodeTransactionData( `MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmPosFromSingleToken@1440@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327`, ), @@ -714,6 +760,13 @@ describe('PositionCreatorTransaction', () => { 1440, ); + const gasLimit = + gasConfig.positionCreator.singleToken.farmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transaction).toEqual([ { nonce: 0, @@ -724,8 +777,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.farmPosition, + gasLimit: gasLimit, data: encodeTransactionData( `createFarmPosFromSingleToken@1440@47008144020574367766@47008144020574367766823`, ), @@ -830,6 +882,14 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.dualFarmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transaction).toEqual([ { nonce: 0, @@ -839,8 +899,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.dualFarmPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@47008144020574367766@47008144020574367766823', ), @@ -897,6 +956,14 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.dualFarmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transaction).toEqual([ { nonce: 0, @@ -906,8 +973,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.dualFarmPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -969,6 +1035,14 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.dualFarmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transaction).toEqual([ { nonce: 0, @@ -996,8 +1070,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.dualFarmPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@47008144020574367766@47008144020574367766823', ), @@ -1059,6 +1132,14 @@ describe('PositionCreatorTransaction', () => { swapRoutes, ); + const gasLimit = + gasConfig.positionCreator.singleToken.dualFarmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes[0].pairs.length; + expect(transaction).toEqual([ { nonce: 0, @@ -1068,8 +1149,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.dualFarmPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@329339339317295273252@329339339317295273252718@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -1114,6 +1194,12 @@ describe('PositionCreatorTransaction', () => { [], ); + const gasLimit = + gasConfig.positionCreator.singleToken.dualFarmPosition + + gasConfig.pairs.addLiquidity + + gasConfig.farms[FarmVersion.V2].enterFarm.withTokenMerge + + gasConfig.stakeProxy.stakeFarmTokens.withTokenMerge; + expect(transaction).toEqual([ { nonce: 0, @@ -1123,8 +1209,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.dualFarmPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@EGLDMEXLP-abcdef@@100000000000000000000@METASTAKE-123456@01@100000000000000000000@createMetastakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@@', ), @@ -1222,6 +1307,12 @@ describe('PositionCreatorTransaction', () => { ], ); + const gasLimit = + gasConfig.positionCreator.singleToken.stakingPosition + + gasConfig.stake.stakeFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes.swaps.length; + expect(transaction).toEqual([ { nonce: 0, @@ -1231,8 +1322,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.stakingPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', ), @@ -1277,6 +1367,12 @@ describe('PositionCreatorTransaction', () => { ], ); + const gasLimit = + gasConfig.positionCreator.singleToken.stakingPosition + + gasConfig.stake.stakeFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes.swaps.length; + expect(transaction).toEqual([ { nonce: 0, @@ -1286,8 +1382,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.stakingPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@01@USDC-123456@@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), @@ -1343,6 +1438,12 @@ describe('PositionCreatorTransaction', () => { ], ); + const gasLimit = + gasConfig.positionCreator.singleToken.stakingPosition + + gasConfig.stake.stakeFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes.swaps.length; + expect(transaction).toEqual([ { nonce: 0, @@ -1370,8 +1471,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.stakingPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@WEGLD-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@90661089388014913158134@0000000000000000000000000000000000000000000000000000000000000012@swapTokensFixedInput@MEX-123456@89754478494134764026552', ), @@ -1422,6 +1522,12 @@ describe('PositionCreatorTransaction', () => { ], ); + const gasLimit = + gasConfig.positionCreator.singleToken.stakingPosition + + gasConfig.stake.stakeFarm.withTokenMerge + + gasConfig.pairs.swapTokensFixedInput.withFeeSwap * + swapRoutes.swaps.length; + expect(transaction).toEqual([ { nonce: 0, @@ -1431,8 +1537,7 @@ describe('PositionCreatorTransaction', () => { senderUsername: undefined, receiverUsername: undefined, gasPrice: 1000000000, - gasLimit: - gasConfig.positionCreator.singleToken.stakingPosition, + gasLimit: gasLimit, data: encodeTransactionData( 'MultiESDTNFTTransfer@00000000000000000500bc458e2cd68bb69665812137dcdd988d9f69901e7ceb@02@USDC-123456@@100000000000000000000@STAKETOK-1111@01@100000000000000000000@createFarmStakingPosFromSingleToken@0000000000000000000000000000000000000000000000000000000000000000@999999999899699097301@0000000000000000000000000000000000000000000000000000000000000013@swapTokensFixedInput@WEGLD-123456@989999999900702106327', ), From a0cc4b9d755ccdb739b50f893c3adcf72560040f Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Sep 2024 13:51:06 +0300 Subject: [PATCH 300/313] MEX-513: fix tokens service unit tests imports Signed-off-by: Claudiu Lataretu --- src/modules/tokens/specs/token.service.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/tokens/specs/token.service.spec.ts b/src/modules/tokens/specs/token.service.spec.ts index 7d1c2504e..91808de67 100644 --- a/src/modules/tokens/specs/token.service.spec.ts +++ b/src/modules/tokens/specs/token.service.spec.ts @@ -14,6 +14,10 @@ import { DynamicModuleUtils } from 'src/utils/dynamic.module.utils'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { TokenComputeServiceProvider } from '../mocks/token.compute.service.mock'; import { TokenFilteringService } from '../services/token.filtering.service'; +import { PairService } from 'src/modules/pair/services/pair.service'; +import { PairComputeServiceProvider } from 'src/modules/pair/mocks/pair.compute.service.mock'; +import { WrapAbiServiceProvider } from 'src/modules/wrapping/mocks/wrap.abi.service.mock'; +import { ContextGetterServiceProvider } from 'src/services/context/mocks/context.getter.service.mock'; describe('TokenService', () => { let module: TestingModule; @@ -29,13 +33,17 @@ describe('TokenService', () => { ], providers: [ PairAbiServiceProvider, + PairComputeServiceProvider, + PairService, RouterAbiServiceProvider, + WrapAbiServiceProvider, TokenRepositoryServiceProvider, MXApiServiceProvider, TokenService, ApiConfigService, TokenComputeServiceProvider, TokenFilteringService, + ContextGetterServiceProvider, ], }).compile(); }); From 245894a2237b2f877116b7f5270a197afc3ad2d9 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Thu, 22 Aug 2024 12:11:52 +0300 Subject: [PATCH 301/313] MEX-495: fix migrate staking positions transactions Signed-off-by: Claudiu Lataretu --- .../staking/services/staking.transactions.service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/staking/services/staking.transactions.service.ts b/src/modules/staking/services/staking.transactions.service.ts index f01dcdc83..96982c9b4 100644 --- a/src/modules/staking/services/staking.transactions.service.ts +++ b/src/modules/staking/services/staking.transactions.service.ts @@ -9,7 +9,7 @@ import { } from '@multiversx/sdk-core'; import { Injectable } from '@nestjs/common'; import { BigNumber } from 'bignumber.js'; -import { mxConfig, gasConfig } from 'src/config'; +import { mxConfig, gasConfig, constantsConfig } from 'src/config'; import { InputTokenModel } from 'src/models/inputToken.model'; import { TransactionModel } from 'src/models/transaction.model'; import { MXProxyService } from 'src/services/multiversx-communication/mx.proxy.service'; @@ -226,7 +226,11 @@ export class StakingTransactionService { const promises: Promise[] = []; userNfts.forEach((nft) => { - if (nft.nonce < migrationNonce) { + if ( + nft.nonce < migrationNonce && + nft.attributes.length > + constantsConfig.STAKING_UNBOND_ATTRIBUTES_LEN + ) { promises.push( this.claimRewards(userAddress, stakingAddress, { tokenID: nft.collection, From 2a3b7ac5730bcfe5c7a5e6d66d0621b49772f19a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Mon, 26 Aug 2024 19:02:58 +0300 Subject: [PATCH 302/313] MEX-495: fix feesAPR NaN value Signed-off-by: Claudiu Lataretu --- src/modules/pair/services/pair.compute.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/pair/services/pair.compute.service.ts b/src/modules/pair/services/pair.compute.service.ts index 52ef42438..56f11b3fb 100644 --- a/src/modules/pair/services/pair.compute.service.ts +++ b/src/modules/pair/services/pair.compute.service.ts @@ -531,7 +531,9 @@ export class PairComputeService implements IPairComputeService { ), ); - return actualFees24hBig.times(365).div(lockedValueUSD).toFixed(); + const feesAPR = actualFees24hBig.times(365).div(lockedValueUSD); + + return !feesAPR.isNaN() ? feesAPR.toFixed() : '0'; } @ErrorLoggerAsync({ From e7091c79ca8fe7466f3da13aca508a7cba3cee1a Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Fri, 16 Aug 2024 20:12:59 +0300 Subject: [PATCH 303/313] MEX-515: Add query metrics plugin for apollo server Signed-off-by: Claudiu Lataretu --- src/public.app.module.ts | 2 ++ src/utils/logging.interceptor.ts | 33 +------------------- src/utils/query.metrics.plugin.ts | 50 +++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 32 deletions(-) create mode 100644 src/utils/query.metrics.plugin.ts diff --git a/src/public.app.module.ts b/src/public.app.module.ts index fca93c64b..2370f000c 100644 --- a/src/public.app.module.ts +++ b/src/public.app.module.ts @@ -39,6 +39,7 @@ import '@multiversx/sdk-nestjs-common/lib/utils/extensions/array.extensions'; import { PositionCreatorModule } from './modules/position-creator/position.creator.module'; import { ComposableTasksModule } from './modules/composable-tasks/composable.tasks.module'; import { TradingViewModule } from './modules/trading-view/trading.view.module'; +import { QueryMetricsPlugin } from './utils/query.metrics.plugin'; @Module({ imports: [ @@ -103,6 +104,7 @@ import { TradingViewModule } from './modules/trading-view/trading.view.module'; DynamicModuleUtils.getCacheModule(), TradingViewModule, ], + providers: [QueryMetricsPlugin], }) export class PublicAppModule { configure(consumer: MiddlewareConsumer) { diff --git a/src/utils/logging.interceptor.ts b/src/utils/logging.interceptor.ts index 033343c70..8e1edb3ee 100644 --- a/src/utils/logging.interceptor.ts +++ b/src/utils/logging.interceptor.ts @@ -6,10 +6,6 @@ import { } from '@nestjs/common'; import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql'; import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { CpuProfiler } from '@multiversx/sdk-nestjs-monitoring'; -import { MetricsCollector } from './metrics.collector'; -import { PerformanceProfiler } from './performance.profiler'; import { ContextTracker } from '@multiversx/sdk-nestjs-common'; @Injectable() @@ -17,44 +13,17 @@ export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { if (context.getType() === 'graphql') { const gqlContext = GqlExecutionContext.create(context); - const info = gqlContext.getInfo(); - const parentType = info.parentType.name; - const fieldName = info.fieldName; const { req } = gqlContext.getContext(); - let origin = 'Unknown'; let timestamp: number = undefined; if (req !== undefined) { - origin = req?.headers?.['origin'] ?? 'Unknown'; timestamp = req?.headers?.['timestamp']; ContextTracker.assign({ deepHistoryTimestamp: timestamp, }); } - - const profiler = new PerformanceProfiler(); - const cpuProfiler = new CpuProfiler(); - - return next.handle().pipe( - tap(() => { - profiler.stop(); - const cpuTime = cpuProfiler.stop(); - if (parentType === 'Query') { - MetricsCollector.setQueryDuration( - fieldName, - origin, - profiler.duration, - ); - - MetricsCollector.setQueryCpu( - fieldName, - origin, - cpuTime, - ); - } - }), - ); + return next.handle().pipe(); } return next.handle(); } diff --git a/src/utils/query.metrics.plugin.ts b/src/utils/query.metrics.plugin.ts new file mode 100644 index 000000000..cbe4d8bfd --- /dev/null +++ b/src/utils/query.metrics.plugin.ts @@ -0,0 +1,50 @@ +import { + ApolloServerPlugin, + GraphQLRequestContext, + GraphQLRequestExecutionListener, + GraphQLRequestListener, +} from '@apollo/server'; +import { Plugin } from '@nestjs/apollo'; +import { PerformanceProfiler } from './performance.profiler'; +import { CpuProfiler } from '@multiversx/sdk-nestjs-monitoring'; +import { MetricsCollector } from './metrics.collector'; + +@Plugin() +export class QueryMetricsPlugin implements ApolloServerPlugin { + async requestDidStart(): Promise> { + let profiler: PerformanceProfiler; + let cpuProfiler: CpuProfiler; + let operationName: string; + let origin: string; + + return { + async executionDidStart( + requestContext: GraphQLRequestContext, + ): Promise> { + operationName = requestContext.operationName; + if (!operationName) { + operationName = requestContext.queryHash; + } + origin = + requestContext.request.http?.headers.get('origin') ?? + 'Unknown'; + + profiler = new PerformanceProfiler(); + cpuProfiler = new CpuProfiler(); + profiler.start(operationName); + }, + async willSendResponse(): Promise { + profiler.stop(operationName); + const cpuTime = cpuProfiler.stop(); + + MetricsCollector.setQueryDuration( + operationName, + origin, + profiler.duration, + ); + + MetricsCollector.setQueryCpu(operationName, origin, cpuTime); + }, + }; + } +} From 8a09f049f8b99cd29d2a67ba85ed29ec7c8e5230 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 15:45:41 +0300 Subject: [PATCH 304/313] SERVICES-2577: fixes after review 2 - move bulk getter methods to staking.abi service - rename filter field --- src/modules/staking/models/staking.args.ts | 2 +- .../staking/services/staking.abi.service.ts | 43 ++++++++++++++++++ .../services/staking.filtering.service.ts | 20 ++++----- .../staking/services/staking.service.ts | 45 +------------------ 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/modules/staking/models/staking.args.ts b/src/modules/staking/models/staking.args.ts index 5e73aa925..b18e0b117 100644 --- a/src/modules/staking/models/staking.args.ts +++ b/src/modules/staking/models/staking.args.ts @@ -40,7 +40,7 @@ export class StakingFarmsFilter { @Field(() => String, { nullable: true }) searchToken?: string; @Field({ nullable: true }) - rewardsDepletedPerFarm?: boolean; + rewardsEnded?: boolean; } @InputType() diff --git a/src/modules/staking/services/staking.abi.service.ts b/src/modules/staking/services/staking.abi.service.ts index 665993259..cdf8f83d0 100644 --- a/src/modules/staking/services/staking.abi.service.ts +++ b/src/modules/staking/services/staking.abi.service.ts @@ -20,6 +20,8 @@ import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; import { IStakingAbiService } from './interfaces'; import { BoostedYieldsFactors } from 'src/modules/farm/models/farm.v2.model'; import { MXApiService } from 'src/services/multiversx-communication/mx.api.service'; +import { CacheService } from '@multiversx/sdk-nestjs-cache'; +import { getAllKeys } from 'src/utils/get.many.utils'; @Injectable() export class StakingAbiService @@ -30,6 +32,7 @@ export class StakingAbiService protected readonly mxProxy: MXProxyService, private readonly gatewayService: MXGatewayService, private readonly apiService: MXApiService, + private readonly cachingService: CacheService, ) { super(mxProxy); } @@ -78,6 +81,15 @@ export class StakingAbiService return response.firstValue.valueOf().toString(); } + async getAllFarmingTokensIds(stakeAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.farmingTokenID', + this.farmingTokenID.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -166,6 +178,17 @@ export class StakingAbiService return response.firstValue.valueOf().toFixed(); } + async getAllAccumulatedRewards( + stakeAddresses: string[], + ): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.accumulatedRewards', + this.accumulatedRewards.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -188,6 +211,15 @@ export class StakingAbiService return response.firstValue.valueOf().toFixed(); } + async getAllRewardCapacity(stakeAddresses: string[]): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.rewardCapacity', + this.rewardCapacity.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -318,6 +350,17 @@ export class StakingAbiService return response === '01'; } + async getAllProduceRewardsEnabled( + stakeAddresses: string[], + ): Promise { + return await getAllKeys( + this.cachingService, + stakeAddresses, + 'stake.produceRewardsEnabled', + this.produceRewardsEnabled.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) diff --git a/src/modules/staking/services/staking.filtering.service.ts b/src/modules/staking/services/staking.filtering.service.ts index 5ddc5aeb6..7ecf62491 100644 --- a/src/modules/staking/services/staking.filtering.service.ts +++ b/src/modules/staking/services/staking.filtering.service.ts @@ -1,12 +1,14 @@ import { Inject, Injectable, forwardRef } from '@nestjs/common'; import { StakingFarmsFilter } from '../models/staking.args'; import { StakingService } from './staking.service'; +import { StakingAbiService } from './staking.abi.service'; @Injectable() export class StakingFilteringService { constructor( @Inject(forwardRef(() => StakingService)) private readonly stakingService: StakingService, + private readonly stakingAbi: StakingAbiService, ) {} async stakingFarmsByToken( @@ -47,20 +49,19 @@ export class StakingFilteringService { stakeAddresses: string[], ): Promise { if ( - typeof stakingFilter.rewardsDepletedPerFarm === 'undefined' || - stakingFilter.rewardsDepletedPerFarm === null + typeof stakingFilter.rewardsEnded === 'undefined' || + stakingFilter.rewardsEnded === null ) { return stakeAddresses; } const allProduceRewardsEnabled = - await this.stakingService.getAllProduceRewardsEnabled( - stakeAddresses, - ); + await this.stakingAbi.getAllProduceRewardsEnabled(stakeAddresses); const allAccumulatedRewards = - await this.stakingService.getAllAccumulatedRewards(stakeAddresses); - const allRewardCapacity = - await this.stakingService.getAllRewardCapacity(stakeAddresses); + await this.stakingAbi.getAllAccumulatedRewards(stakeAddresses); + const allRewardCapacity = await this.stakingAbi.getAllRewardCapacity( + stakeAddresses, + ); const rewardsDepleted = stakeAddresses.map( (_, index) => @@ -69,8 +70,7 @@ export class StakingFilteringService { ); return stakeAddresses.filter( - (_, index) => - rewardsDepleted[index] === stakingFilter.rewardsDepletedPerFarm, + (_, index) => rewardsDepleted[index] === stakingFilter.rewardsEnded, ); } } diff --git a/src/modules/staking/services/staking.service.ts b/src/modules/staking/services/staking.service.ts index 67d928453..e68e733bf 100644 --- a/src/modules/staking/services/staking.service.ts +++ b/src/modules/staking/services/staking.service.ts @@ -39,8 +39,6 @@ import { } from '../models/staking.args'; import { SortingOrder } from 'src/modules/common/page.data'; import { StakingFilteringService } from './staking.filtering.service'; -import { getAllKeys } from 'src/utils/get.many.utils'; -import { CacheService } from '@multiversx/sdk-nestjs-cache'; @Injectable() export class StakingService { @@ -56,7 +54,6 @@ export class StakingService { private readonly weeklyRewardsSplittingAbi: WeeklyRewardsSplittingAbiService, @Inject(forwardRef(() => StakingFilteringService)) private readonly stakingFilteringService: StakingFilteringService, - private readonly cachingService: CacheService, ) {} async getFarmsStaking(): Promise { @@ -118,37 +115,6 @@ export class StakingService { }); } - async getAllAccumulatedRewards( - stakeAddresses: string[], - ): Promise { - return await getAllKeys( - this.cachingService, - stakeAddresses, - 'stake.accumulatedRewards', - this.stakingAbi.accumulatedRewards.bind(this.stakingAbi), - ); - } - - async getAllRewardCapacity(stakeAddresses: string[]): Promise { - return await getAllKeys( - this.cachingService, - stakeAddresses, - 'stake.rewardCapacity', - this.stakingAbi.rewardCapacity.bind(this.stakingAbi), - ); - } - - async getAllProduceRewardsEnabled( - stakeAddresses: string[], - ): Promise { - return await getAllKeys( - this.cachingService, - stakeAddresses, - 'stake.produceRewardsEnabled', - this.stakingAbi.produceRewardsEnabled.bind(this.stakingAbi), - ); - } - async getFarmToken(stakeAddress: string): Promise { const farmTokenID = await this.stakingAbi.farmTokenID(stakeAddress); return await this.tokenService.getNftCollectionMetadata(farmTokenID); @@ -161,17 +127,8 @@ export class StakingService { return await this.tokenService.tokenMetadata(farmingTokenID); } - async getAllFarmingTokensIds(stakeAddresses: string[]): Promise { - return await getAllKeys( - this.cachingService, - stakeAddresses, - 'stake.farmingTokenID', - this.stakingAbi.farmingTokenID.bind(this.stakingAbi), - ); - } - async getAllFarmingTokens(stakeAddresses: string[]): Promise { - const farmingTokenIDs = await this.getAllFarmingTokensIds( + const farmingTokenIDs = await this.stakingAbi.getAllFarmingTokensIds( stakeAddresses, ); From e1bf094fd8edcf57aaff78b1f33b331700e2a6d9 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 17:16:15 +0300 Subject: [PATCH 305/313] SERVICES-2578: add batch get methods to token compute service --- .../tokens/services/token.compute.service.ts | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index eb76249f8..43fd531f9 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -28,6 +28,7 @@ import { PendingExecutor } from 'src/utils/pending.executor'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { TokenService } from './token.service'; import { computeValueUSD } from 'src/utils/token.converters'; +import { getAllKeys } from 'src/utils/get.many.utils'; @Injectable() export class TokenComputeService implements ITokenComputeService { @@ -189,6 +190,15 @@ export class TokenComputeService implements ITokenComputeService { return priceSoFar; } + async getAllTokensPriceDerivedEGLD(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenPriceDerivedEGLD', + this.tokenPriceDerivedEGLD.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -222,6 +232,15 @@ export class TokenComputeService implements ITokenComputeService { .toFixed(); } + async getAllTokensPriceDerivedUSD(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenPriceDerivedUSD', + this.tokenPriceDerivedUSD.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -243,6 +262,15 @@ export class TokenComputeService implements ITokenComputeService { return values24h[0]?.value ?? undefined; } + async getAllTokensPrevious24hPrice(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenPrevious24hPrice', + this.tokenPrevious24hPrice.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -265,6 +293,15 @@ export class TokenComputeService implements ITokenComputeService { return values7d[0]?.value ?? undefined; } + async getAllTokensPrevious7dPrice(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenPrevious7dPrice', + this.tokenPrevious7dPrice.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -388,6 +425,15 @@ export class TokenComputeService implements ITokenComputeService { return valuesLast2Days.current; } + async getAllTokensVolumeUSD24h(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenVolumeUSD24h', + this.tokenVolumeUSD24h.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -405,6 +451,17 @@ export class TokenComputeService implements ITokenComputeService { return valuesLast2Days.previous; } + async getAllTokensPrevious24hVolumeUSD( + tokenIDs: string[], + ): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenPrevious24hVolumeUSD', + this.tokenPrevious24hVolumeUSD.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -503,6 +560,15 @@ export class TokenComputeService implements ITokenComputeService { ).toFixed(); } + async getAllTokensLiquidityUSD(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenLiquidityUSD', + this.tokenLiquidityUSD.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -533,6 +599,15 @@ export class TokenComputeService implements ITokenComputeService { return undefined; } + async getAllTokensCreatedAt(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenCreatedAt', + this.tokenCreatedAt.bind(this), + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -674,4 +749,13 @@ export class TokenComputeService implements ITokenComputeService { return trendingScore.toFixed(); } + + async getAllTokensTrendingScore(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.tokenTrendingScore', + this.tokenTrendingScore.bind(this), + ); + } } From 1df54bcb049265c0b8355635670e05143473fede Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 17:20:56 +0300 Subject: [PATCH 306/313] SERVICES-2578: use batch getters in token and token filtering services --- .../services/token.filtering.service.ts | 31 ++--- src/modules/tokens/services/token.service.ts | 106 +++++++++--------- 2 files changed, 64 insertions(+), 73 deletions(-) diff --git a/src/modules/tokens/services/token.filtering.service.ts b/src/modules/tokens/services/token.filtering.service.ts index fc127242f..7a21c69ba 100644 --- a/src/modules/tokens/services/token.filtering.service.ts +++ b/src/modules/tokens/services/token.filtering.service.ts @@ -38,15 +38,13 @@ export class TokenFilteringService { return tokenIDs; } - const filteredIDs = []; - for (const tokenID of tokenIDs) { - const tokenType = await this.tokenService.getEsdtTokenType(tokenID); + const tokenTypes = await this.tokenService.getAllEsdtTokensType( + tokenIDs, + ); - if (tokenType === tokensFilter.type) { - filteredIDs.push(tokenID); - } - } - return filteredIDs; + return tokenIDs.filter( + (_, index) => tokenTypes[index] === tokensFilter.type, + ); } async tokensBySearchTerm( @@ -84,17 +82,12 @@ export class TokenFilteringService { return tokenIDs; } - const filteredIDs = []; - for (const tokenID of tokenIDs) { - const liquidity = await this.tokenCompute.tokenLiquidityUSD( - tokenID, - ); + const tokensLiquidityUSD = + await this.tokenCompute.getAllTokensLiquidityUSD(tokenIDs); - const liquidityBN = new BigNumber(liquidity); - if (liquidityBN.gte(tokensFilter.minLiquidity)) { - filteredIDs.push(tokenID); - } - } - return filteredIDs; + return tokenIDs.filter((_, index) => { + const liquidity = new BigNumber(tokensLiquidityUSD[index]); + return liquidity.gte(tokensFilter.minLiquidity); + }); } } diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index fb42a1343..a1bc8a650 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -48,13 +48,13 @@ export class TokenService { ); } - const promises = tokenIDs.map((tokenID) => this.tokenMetadata(tokenID)); - let tokens = await Promise.all(promises); + let tokens = await this.getAllTokensMetadata(tokenIDs); if (filters.type) { - for (const token of tokens) { - token.type = await this.getEsdtTokenType(token.identifier); - } + const tokenTypes = await this.getAllEsdtTokensType(tokenIDs); + tokens.forEach((token, index) => { + token.type = tokenTypes[index]; + }); tokens = tokens.filter((token) => token.type === filters.type); } @@ -87,9 +87,7 @@ export class TokenService { tokenIDs = await this.sortTokens(tokenIDs, sorting); } - let tokens = await Promise.all( - tokenIDs.map((tokenID) => this.tokenMetadata(tokenID)), - ); + let tokens = await this.getAllTokensMetadata(tokenIDs); tokens = await this.tokenFilteringService.tokensBySearchTerm( filters, @@ -124,6 +122,16 @@ export class TokenService { return await this.tokenRepository.getTokenType(tokenID); } + async getAllEsdtTokensType(tokenIDs: string[]): Promise { + return getAllKeys( + this.cachingService, + tokenIDs, + 'token.getEsdtTokenType', + this.getEsdtTokenType.bind(this), + CacheTtlInfo.Token, + ); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -181,19 +189,21 @@ export class TokenService { async getUniqueTokenIDs(activePool: boolean): Promise { const pairsMetadata = await this.routerAbi.pairsMetadata(); const tokenIDs: string[] = []; - await Promise.all( - pairsMetadata.map(async (iterator) => { - if (activePool) { - const state = await this.pairAbi.state(iterator.address); - if (state !== 'Active') { - return; - } + const pairStates = activePool + ? await this.pairService.getAllStates( + pairsMetadata.map((pair) => pair.address), + ) + : []; + + for (const [index, pair] of pairsMetadata.entries()) { + if (activePool) { + if (pairStates[index] !== 'Active') { + continue; } - tokenIDs.push( - ...[iterator.firstTokenID, iterator.secondTokenID], - ); - }), - ); + } + tokenIDs.push(...[pair.firstTokenID, pair.secondTokenID]); + } + return [...new Set(tokenIDs)]; } @@ -205,25 +215,22 @@ export class TokenService { switch (tokenSorting.sortField) { case TokensSortableFields.PRICE: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenPriceDerivedUSD(tokenID), - ), - ); + sortFieldData = + await this.tokenCompute.getAllTokensPriceDerivedUSD( + tokenIDs, + ); break; case TokensSortableFields.PREVIOUS_24H_PRICE: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenPrevious24hPrice(tokenID), - ), - ); + sortFieldData = + await this.tokenCompute.getAllTokensPrevious24hPrice( + tokenIDs, + ); break; case TokensSortableFields.PREVIOUS_7D_PRICE: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenPrevious7dPrice(tokenID), - ), - ); + sortFieldData = + await this.tokenCompute.getAllTokensPrevious7dPrice( + tokenIDs, + ); break; case TokensSortableFields.PRICE_CHANGE_7D: sortFieldData = await Promise.all( @@ -254,32 +261,23 @@ export class TokenService { ); break; case TokensSortableFields.CREATED_AT: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenCreatedAt(tokenID), - ), + sortFieldData = await this.tokenCompute.getAllTokensCreatedAt( + tokenIDs, ); break; case TokensSortableFields.LIQUIDITY: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenLiquidityUSD(tokenID), - ), - ); + sortFieldData = + await this.tokenCompute.getAllTokensLiquidityUSD(tokenIDs); break; case TokensSortableFields.VOLUME: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenVolumeUSD24h(tokenID), - ), - ); + sortFieldData = + await this.tokenCompute.getAllTokensVolumeUSD24h(tokenIDs); break; case TokensSortableFields.PREVIOUS_24H_VOLUME: - sortFieldData = await Promise.all( - tokenIDs.map((tokenID) => - this.tokenCompute.tokenPrevious24hVolumeUSD(tokenID), - ), - ); + sortFieldData = + await this.tokenCompute.getAllTokensPrevious24hVolumeUSD( + tokenIDs, + ); break; case TokensSortableFields.TRADES_COUNT: sortFieldData = await Promise.all( From b4796a213fcb8bab4f3a2f100dea430b242219ee Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 17:21:36 +0300 Subject: [PATCH 307/313] SERVICES-2578: use batch getter methods in token dataloaders --- src/modules/tokens/services/token.loader.ts | 64 ++++----------------- 1 file changed, 10 insertions(+), 54 deletions(-) diff --git a/src/modules/tokens/services/token.loader.ts b/src/modules/tokens/services/token.loader.ts index 9dad620d5..ed9c85fdf 100644 --- a/src/modules/tokens/services/token.loader.ts +++ b/src/modules/tokens/services/token.loader.ts @@ -17,12 +17,7 @@ export class TokenLoader { public readonly tokenTypeLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.getEsdtTokenType', - this.tokenService.getEsdtTokenType.bind(this.tokenService), - ); + return await this.tokenService.getAllEsdtTokensType(tokenIDs); }, ); @@ -30,21 +25,13 @@ export class TokenLoader { string, string >(async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.tokenPriceDerivedEGLD', - this.tokenCompute.tokenPriceDerivedEGLD.bind(this.tokenCompute), - ); + return await this.tokenCompute.getAllTokensPriceDerivedEGLD(tokenIDs); }); public readonly tokenPriceDerivedUSDLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, + return await this.tokenCompute.getAllTokensPriceDerivedUSD( tokenIDs, - 'token.tokenPriceDerivedUSD', - this.tokenCompute.tokenPriceDerivedUSD.bind(this.tokenCompute), ); }, ); @@ -53,33 +40,20 @@ export class TokenLoader { string, string >(async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.tokenPrevious24hPrice', - this.tokenCompute.tokenPrevious24hPrice.bind(this.tokenCompute), - ); + return await this.tokenCompute.getAllTokensPrevious24hPrice(tokenIDs); }); public readonly tokenPrevious7dPriceLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, + return await this.tokenCompute.getAllTokensPrevious7dPrice( tokenIDs, - 'token.tokenPrevious7dPrice', - this.tokenCompute.tokenPrevious7dPrice.bind(this.tokenCompute), ); }, ); public readonly tokenVolumeUSD24hLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.tokenVolumeUSD24h', - this.tokenCompute.tokenVolumeUSD24h.bind(this.tokenCompute), - ); + return await this.tokenCompute.getAllTokensVolumeUSD24h(tokenIDs); }, ); @@ -87,33 +61,20 @@ export class TokenLoader { string, string >(async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, + return await this.tokenCompute.getAllTokensPrevious24hVolumeUSD( tokenIDs, - 'token.tokenPrevious24hVolumeUSD', - this.tokenCompute.tokenPrevious24hVolumeUSD.bind(this.tokenCompute), ); }); public readonly tokenLiquidityUSDLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.tokenLiquidityUSD', - this.tokenCompute.tokenLiquidityUSD.bind(this.tokenCompute), - ); + return await this.tokenCompute.getAllTokensLiquidityUSD(tokenIDs); }, ); public readonly tokenCreatedAtLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.tokenCreatedAt', - this.tokenCompute.tokenCreatedAt.bind(this.tokenCompute), - ); + return await this.tokenCompute.getAllTokensCreatedAt(tokenIDs); }, ); @@ -142,12 +103,7 @@ export class TokenLoader { public readonly tokenTrendingScoreLoader = new DataLoader( async (tokenIDs: string[]) => { - return await getAllKeys( - this.cacheService, - tokenIDs, - 'token.tokenTrendingScore', - this.tokenCompute.tokenTrendingScore.bind(this.tokenCompute), - ); + return await this.tokenCompute.getAllTokensTrendingScore(tokenIDs); }, ); } From de873dc192b6757efcd92098273058661017ca5d Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 17:24:18 +0300 Subject: [PATCH 308/313] SERVICES-2578: remove ttl from getAllKeys call --- src/modules/tokens/services/token.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index a1bc8a650..abc176215 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -128,7 +128,6 @@ export class TokenService { tokenIDs, 'token.getEsdtTokenType', this.getEsdtTokenType.bind(this), - CacheTtlInfo.Token, ); } From 4706d48568fb62268c699f3a816a7c21c40f252b Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Sep 2024 17:38:24 +0300 Subject: [PATCH 309/313] MEX-516: fix missing user outdated contracts Signed-off-by: Claudiu Lataretu --- .../services/userEnergy/user.energy.compute.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/user/services/userEnergy/user.energy.compute.service.ts b/src/modules/user/services/userEnergy/user.energy.compute.service.ts index 3e101cf52..efbfe807f 100644 --- a/src/modules/user/services/userEnergy/user.energy.compute.service.ts +++ b/src/modules/user/services/userEnergy/user.energy.compute.service.ts @@ -313,7 +313,7 @@ export class UserEnergyComputeService { (token) => token.creator, ); const promisesDualYieldTokens = dualYieldTokens.map((token) => { - return this.getFarmAddressForDualYieldToken(token.collection); + return this.getStakeAddressForDualYieldToken(token.collection); }); userActiveStakeAddresses = userActiveStakeAddresses.concat( @@ -716,14 +716,14 @@ export class UserEnergyComputeService { return this.stakeProxyAbi.lpFarmAddress(stakingProxyAddress); } - async getStakeAddressForDualYieldToken(token: UserDualYiledToken) { - if (!token || token === undefined) { + async getStakeAddressForDualYieldToken(collection: string) { + if (!collection || collection === undefined) { return undefined; } const stakingProxyAddress = await this.stakeProxyService.getStakingProxyAddressByDualYieldTokenID( - token.collection, + collection, ); return this.stakeProxyAbi.stakingFarmAddress(stakingProxyAddress); } From 839165ec04690dc126f8620c6736541f599a0c5f Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 18:15:32 +0300 Subject: [PATCH 310/313] SERVICES-2582: add optional TTL param to getAllKeys util function --- src/utils/get.many.utils.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/utils/get.many.utils.ts b/src/utils/get.many.utils.ts index 98449e6ac..c3b098dec 100644 --- a/src/utils/get.many.utils.ts +++ b/src/utils/get.many.utils.ts @@ -1,9 +1,11 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { parseCachedNullOrUndefined } from './cache.utils'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; async function getMany( cacheService: CacheService, keys: string[], + localTtl: number, ): Promise<(T | undefined)[]> { const values = await cacheService.getManyLocal(keys); @@ -25,6 +27,10 @@ async function getMany( const remoteValues = await cacheService.getManyRemote(missingKeys); + if (localTtl > 0) { + await cacheService.setManyLocal(missingKeys, remoteValues, localTtl); + } + for (const [index, missingIndex] of missingIndexes.entries()) { const remoteValue = remoteValues[index]; values[missingIndex] = remoteValue @@ -40,9 +46,14 @@ export async function getAllKeys( rawKeys: string[], baseKey: string, getterMethod: (address: string) => Promise, + ttlOptions?: CacheTtlInfo, ): Promise { const keys = rawKeys.map((tokenID) => `${baseKey}.${tokenID}`); - const values = await getMany(cacheService, keys); + const values = await getMany( + cacheService, + keys, + ttlOptions?.localTtl ?? 0, + ); const missingIndexes: number[] = []; values.forEach((value, index) => { From 9d4c2478caf62a3e14387ae7257e65c94c2ad069 Mon Sep 17 00:00:00 2001 From: hschiau Date: Wed, 11 Sep 2024 18:17:59 +0300 Subject: [PATCH 311/313] SERVICES-2582: pass ttl option in all getAllKeys calls --- .../farm/base-module/services/farm.abi.loader.ts | 11 +++++++++++ .../farm/base-module/services/farm.base.service.ts | 4 ++++ .../base-module/services/farm.compute.loader.ts | 4 ++++ src/modules/pair/services/pair.abi.loader.ts | 6 ++++++ src/modules/pair/services/pair.compute.loader.ts | 13 +++++++++++++ src/modules/pair/services/pair.service.ts | 11 +++++++++++ .../tokens/services/token.compute.service.ts | 9 +++++++++ src/modules/tokens/services/token.loader.ts | 3 +++ src/modules/tokens/services/token.service.ts | 3 +++ 9 files changed, 64 insertions(+) diff --git a/src/modules/farm/base-module/services/farm.abi.loader.ts b/src/modules/farm/base-module/services/farm.abi.loader.ts index 93b81955a..d5e809213 100644 --- a/src/modules/farm/base-module/services/farm.abi.loader.ts +++ b/src/modules/farm/base-module/services/farm.abi.loader.ts @@ -6,6 +6,7 @@ import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { FarmAbiService } from './farm.abi.service'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { getAllKeys } from 'src/utils/get.many.utils'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; @Injectable({ scope: Scope.REQUEST, @@ -44,6 +45,7 @@ export class FarmAbiLoader { addresses, 'farm.produceRewardsEnabled', this.farmAbi.produceRewardsEnabled.bind(this.farmAbi), + CacheTtlInfo.ContractState, ); }); @@ -54,6 +56,7 @@ export class FarmAbiLoader { addresses, 'farm.perBlockRewards', this.farmAbi.rewardsPerBlock.bind(this.farmAbi), + CacheTtlInfo.ContractState, ); }, ); @@ -65,6 +68,7 @@ export class FarmAbiLoader { addresses, 'farm.farmTokenSupply', this.farmAbi.farmTokenSupply.bind(this.farmAbi), + CacheTtlInfo.ContractInfo, ); }, ); @@ -76,6 +80,7 @@ export class FarmAbiLoader { addresses, 'farm.penaltyPercent', this.farmAbi.penaltyPercent.bind(this.farmAbi), + CacheTtlInfo.ContractState, ); }, ); @@ -87,6 +92,7 @@ export class FarmAbiLoader { addresses, 'farm.minimumFarmingEpochs', this.farmAbi.minimumFarmingEpochs.bind(this.farmAbi), + CacheTtlInfo.ContractState, ); }, ); @@ -98,6 +104,7 @@ export class FarmAbiLoader { addresses, 'farm.rewardPerShare', this.farmAbi.rewardPerShare.bind(this.farmAbi), + CacheTtlInfo.ContractInfo, ); }, ); @@ -109,6 +116,7 @@ export class FarmAbiLoader { addresses, 'farm.rewardReserve', this.farmAbi.rewardReserve.bind(this.farmAbi), + CacheTtlInfo.ContractInfo, ); }, ); @@ -120,6 +128,7 @@ export class FarmAbiLoader { addresses, 'farm.lastRewardBlockNonce', this.farmAbi.lastRewardBlockNonce.bind(this.farmAbi), + CacheTtlInfo.ContractInfo, ); }, ); @@ -133,6 +142,7 @@ export class FarmAbiLoader { addresses, 'farm.divisionSafetyConstant', this.farmAbi.divisionSafetyConstant.bind(this.farmAbi), + CacheTtlInfo.ContractInfo, ); }); @@ -143,6 +153,7 @@ export class FarmAbiLoader { addresses, 'farm.state', this.farmAbi.state.bind(this.farmAbi), + CacheTtlInfo.ContractState, ); }, ); diff --git a/src/modules/farm/base-module/services/farm.base.service.ts b/src/modules/farm/base-module/services/farm.base.service.ts index e9faf590a..0d3c3a5e2 100644 --- a/src/modules/farm/base-module/services/farm.base.service.ts +++ b/src/modules/farm/base-module/services/farm.base.service.ts @@ -17,6 +17,7 @@ import { NftCollection } from 'src/modules/tokens/models/nftCollection.model'; import { Inject, forwardRef } from '@nestjs/common'; import { TokenService } from 'src/modules/tokens/services/token.service'; import { getAllKeys } from 'src/utils/get.many.utils'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; export abstract class FarmServiceBase { constructor( @@ -39,6 +40,7 @@ export abstract class FarmServiceBase { farmAddresses, 'farm.farmedTokenID', this.farmAbi.farmedTokenID.bind(this.farmAbi), + CacheTtlInfo.Token, ); return this.tokenService.getAllTokensMetadata(farmedTokenIDs); } @@ -54,6 +56,7 @@ export abstract class FarmServiceBase { farmAddresses, 'farm.farmTokenID', this.farmAbi.farmTokenID.bind(this.farmAbi), + CacheTtlInfo.Token, ); return this.tokenService.getAllNftsCollectionMetadata(farmTokenIDs); } @@ -69,6 +72,7 @@ export abstract class FarmServiceBase { farmAddresses, 'farm.farmingTokenID', this.farmAbi.farmingTokenID.bind(this.farmAbi), + CacheTtlInfo.Token, ); return this.tokenService.getAllTokensMetadata(farmingTokenIDs); } diff --git a/src/modules/farm/base-module/services/farm.compute.loader.ts b/src/modules/farm/base-module/services/farm.compute.loader.ts index 9bc565fb3..dd4824295 100644 --- a/src/modules/farm/base-module/services/farm.compute.loader.ts +++ b/src/modules/farm/base-module/services/farm.compute.loader.ts @@ -3,6 +3,7 @@ import { FarmComputeService } from './farm.compute.service'; import DataLoader from 'dataloader'; import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { getAllKeys } from 'src/utils/get.many.utils'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; @Injectable({ scope: Scope.REQUEST, @@ -20,6 +21,7 @@ export class FarmComputeLoader { addresses, 'farm.farmLockedValueUSD', this.farmCompute.farmLockedValueUSD.bind(this.farmCompute), + CacheTtlInfo.ContractState, ); }, ); @@ -31,6 +33,7 @@ export class FarmComputeLoader { addresses, 'farm.farmedTokenPriceUSD', this.farmCompute.farmedTokenPriceUSD.bind(this.farmCompute), + CacheTtlInfo.Price, ); }, ); @@ -42,6 +45,7 @@ export class FarmComputeLoader { addresses, 'farm.farmingTokenPriceUSD', this.farmCompute.farmingTokenPriceUSD.bind(this.farmCompute), + CacheTtlInfo.Price, ); }, ); diff --git a/src/modules/pair/services/pair.abi.loader.ts b/src/modules/pair/services/pair.abi.loader.ts index c12bd8772..7852938a4 100644 --- a/src/modules/pair/services/pair.abi.loader.ts +++ b/src/modules/pair/services/pair.abi.loader.ts @@ -7,6 +7,7 @@ import { PairService } from './pair.service'; import { PairInfoModel } from '../models/pair-info.model'; import { getAllKeys } from 'src/utils/get.many.utils'; import { constantsConfig } from 'src/config'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; @Injectable({ scope: Scope.REQUEST, @@ -44,6 +45,7 @@ export class PairAbiLoader { addresses, 'pair.pairInfoMetadata', this.pairAbi.pairInfoMetadata.bind(this.pairAbi), + CacheTtlInfo.ContractBalance, ); }, ); @@ -55,6 +57,7 @@ export class PairAbiLoader { addresses, 'pair.totalFeePercent', this.pairAbi.totalFeePercent.bind(this.pairAbi), + CacheTtlInfo.ContractState, ); }, ); @@ -66,6 +69,7 @@ export class PairAbiLoader { addresses, 'pair.specialFeePercent', this.pairAbi.specialFeePercent.bind(this.pairAbi), + CacheTtlInfo.ContractState, ); }, ); @@ -79,6 +83,7 @@ export class PairAbiLoader { addresses, 'pair.feesCollectorCutPercentage', this.pairAbi.feesCollectorCutPercentage.bind(this.pairAbi), + CacheTtlInfo.ContractState, ); return percentages.map( @@ -108,6 +113,7 @@ export class PairAbiLoader { addresses, 'pair.initialLiquidityAdder', this.pairAbi.initialLiquidityAdder.bind(this.pairAbi), + CacheTtlInfo.ContractState, ); }); } diff --git a/src/modules/pair/services/pair.compute.loader.ts b/src/modules/pair/services/pair.compute.loader.ts index e631338e0..874c25de1 100644 --- a/src/modules/pair/services/pair.compute.loader.ts +++ b/src/modules/pair/services/pair.compute.loader.ts @@ -4,6 +4,7 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import { getAllKeys } from 'src/utils/get.many.utils'; import DataLoader from 'dataloader'; import { PairService } from './pair.service'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; @Injectable({ scope: Scope.REQUEST, @@ -22,6 +23,7 @@ export class PairComputeLoader { addresses, 'pair.firstTokenPrice', this.pairCompute.firstTokenPrice.bind(this.pairCompute), + CacheTtlInfo.Price, ); }, ); @@ -33,6 +35,7 @@ export class PairComputeLoader { addresses, 'pair.secondTokenPrice', this.pairCompute.secondTokenPrice.bind(this.pairCompute), + CacheTtlInfo.Price, ); }, ); @@ -44,6 +47,7 @@ export class PairComputeLoader { addresses, 'pair.firstTokenPriceUSD', this.pairCompute.firstTokenPriceUSD.bind(this.pairCompute), + CacheTtlInfo.Price, ); }, ); @@ -55,6 +59,7 @@ export class PairComputeLoader { addresses, 'pair.secondTokenPriceUSD', this.pairCompute.secondTokenPriceUSD.bind(this.pairCompute), + CacheTtlInfo.Price, ); }, ); @@ -66,6 +71,7 @@ export class PairComputeLoader { addresses, 'pair.lpTokenPriceUSD', this.pairCompute.lpTokenPriceUSD.bind(this.pairCompute), + CacheTtlInfo.Price, ); }, ); @@ -79,6 +85,7 @@ export class PairComputeLoader { addresses, 'pair.firstTokenLockedValueUSD', this.pairCompute.firstTokenLockedValueUSD.bind(this.pairCompute), + CacheTtlInfo.ContractInfo, ); }); @@ -91,6 +98,7 @@ export class PairComputeLoader { addresses, 'pair.secondTokenLockedValueUSD', this.pairCompute.secondTokenLockedValueUSD.bind(this.pairCompute), + CacheTtlInfo.ContractInfo, ); }); @@ -109,6 +117,7 @@ export class PairComputeLoader { addresses, 'pair.previous24hLockedValueUSD', this.pairCompute.previous24hLockedValueUSD.bind(this.pairCompute), + CacheTtlInfo.ContractInfo, ); }); @@ -119,6 +128,7 @@ export class PairComputeLoader { addresses, 'pair.previous24hVolumeUSD', this.pairCompute.previous24hVolumeUSD.bind(this.pairCompute), + CacheTtlInfo.Analytics, ); }, ); @@ -130,6 +140,7 @@ export class PairComputeLoader { addresses, 'pair.previous24hFeesUSD', this.pairCompute.previous24hFeesUSD.bind(this.pairCompute), + CacheTtlInfo.Analytics, ); }, ); @@ -141,6 +152,7 @@ export class PairComputeLoader { addresses, 'pair.feesAPR', this.pairCompute.feesAPR.bind(this.pairCompute), + CacheTtlInfo.ContractState, ); }, ); @@ -152,6 +164,7 @@ export class PairComputeLoader { addresses, 'pair.type', this.pairCompute.type.bind(this.pairCompute), + CacheTtlInfo.ContractState, ); }, ); diff --git a/src/modules/pair/services/pair.service.ts b/src/modules/pair/services/pair.service.ts index a0e3a746d..d82fb26ee 100644 --- a/src/modules/pair/services/pair.service.ts +++ b/src/modules/pair/services/pair.service.ts @@ -20,6 +20,7 @@ import { PairComputeService } from './pair.compute.service'; import { RouterAbiService } from 'src/modules/router/services/router.abi.service'; import { TokenService } from 'src/modules/tokens/services/token.service'; import { getAllKeys } from 'src/utils/get.many.utils'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; @Injectable() export class PairService { @@ -46,6 +47,7 @@ export class PairService { pairAddresses, 'pair.firstTokenID', this.pairAbi.firstTokenID.bind(this.pairAbi), + CacheTtlInfo.Token, ); return this.tokenService.getAllTokensMetadata(tokenIDs); @@ -62,6 +64,7 @@ export class PairService { pairAddresses, 'pair.secondTokenID', this.pairAbi.secondTokenID.bind(this.pairAbi), + CacheTtlInfo.Token, ); return this.tokenService.getAllTokensMetadata(tokenIDs); @@ -80,6 +83,7 @@ export class PairService { pairAddresses, 'pair.lpTokenID', this.pairAbi.lpTokenID.bind(this.pairAbi), + CacheTtlInfo.Token, ); } @@ -95,6 +99,7 @@ export class PairService { pairAddresses, 'pair.state', this.pairAbi.state.bind(this.pairAbi), + CacheTtlInfo.ContractState, ); } @@ -104,6 +109,7 @@ export class PairService { pairAddresses, 'pair.feeState', this.pairAbi.feeState.bind(this.pairAbi), + CacheTtlInfo.ContractState, ); } @@ -113,6 +119,7 @@ export class PairService { pairAddresses, 'pair.lockedValueUSD', this.pairCompute.lockedValueUSD.bind(this.pairCompute), + CacheTtlInfo.ContractInfo, ); } @@ -122,6 +129,7 @@ export class PairService { pairAddresses, 'pair.deployedAt', this.pairCompute.deployedAt.bind(this.pairCompute), + CacheTtlInfo.ContractState, ); } @@ -131,6 +139,7 @@ export class PairService { pairAddresses, 'pair.tradesCount', this.pairCompute.tradesCount.bind(this.pairCompute), + CacheTtlInfo.ContractState, ); } @@ -140,6 +149,7 @@ export class PairService { pairAddresses, 'pair.hasFarms', this.pairCompute.hasFarms.bind(this.pairCompute), + CacheTtlInfo.ContractState, ); } @@ -149,6 +159,7 @@ export class PairService { pairAddresses, 'pair.hasDualFarms', this.pairCompute.hasDualFarms.bind(this.pairCompute), + CacheTtlInfo.ContractState, ); } diff --git a/src/modules/tokens/services/token.compute.service.ts b/src/modules/tokens/services/token.compute.service.ts index 43fd531f9..8a227db18 100644 --- a/src/modules/tokens/services/token.compute.service.ts +++ b/src/modules/tokens/services/token.compute.service.ts @@ -196,6 +196,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenPriceDerivedEGLD', this.tokenPriceDerivedEGLD.bind(this), + CacheTtlInfo.Price, ); } @@ -238,6 +239,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenPriceDerivedUSD', this.tokenPriceDerivedUSD.bind(this), + CacheTtlInfo.Price, ); } @@ -268,6 +270,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenPrevious24hPrice', this.tokenPrevious24hPrice.bind(this), + CacheTtlInfo.TokenAnalytics, ); } @@ -299,6 +302,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenPrevious7dPrice', this.tokenPrevious7dPrice.bind(this), + CacheTtlInfo.TokenAnalytics, ); } @@ -431,6 +435,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenVolumeUSD24h', this.tokenVolumeUSD24h.bind(this), + CacheTtlInfo.Token, ); } @@ -459,6 +464,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenPrevious24hVolumeUSD', this.tokenPrevious24hVolumeUSD.bind(this), + CacheTtlInfo.Token, ); } @@ -566,6 +572,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenLiquidityUSD', this.tokenLiquidityUSD.bind(this), + CacheTtlInfo.TokenAnalytics, ); } @@ -605,6 +612,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenCreatedAt', this.tokenCreatedAt.bind(this), + CacheTtlInfo.Token, ); } @@ -756,6 +764,7 @@ export class TokenComputeService implements ITokenComputeService { tokenIDs, 'token.tokenTrendingScore', this.tokenTrendingScore.bind(this), + CacheTtlInfo.Token, ); } } diff --git a/src/modules/tokens/services/token.loader.ts b/src/modules/tokens/services/token.loader.ts index ed9c85fdf..a5435952c 100644 --- a/src/modules/tokens/services/token.loader.ts +++ b/src/modules/tokens/services/token.loader.ts @@ -4,6 +4,7 @@ import { CacheService } from '@multiversx/sdk-nestjs-cache'; import DataLoader from 'dataloader'; import { getAllKeys } from 'src/utils/get.many.utils'; import { TokenService } from './token.service'; +import { CacheTtlInfo } from 'src/services/caching/cache.ttl.info'; @Injectable({ scope: Scope.REQUEST, @@ -85,6 +86,7 @@ export class TokenLoader { tokenIDs, 'token.tokenSwapCount', this.tokenCompute.tokenSwapCount.bind(this.tokenCompute), + CacheTtlInfo.Token, ); }, ); @@ -98,6 +100,7 @@ export class TokenLoader { tokenIDs, 'token.tokenPrevious24hSwapCount', this.tokenCompute.tokenPrevious24hSwapCount.bind(this.tokenCompute), + CacheTtlInfo.Token, ); }); diff --git a/src/modules/tokens/services/token.service.ts b/src/modules/tokens/services/token.service.ts index abc176215..44bd970ec 100644 --- a/src/modules/tokens/services/token.service.ts +++ b/src/modules/tokens/services/token.service.ts @@ -128,6 +128,7 @@ export class TokenService { tokenIDs, 'token.getEsdtTokenType', this.getEsdtTokenType.bind(this), + CacheTtlInfo.Token, ); } @@ -149,6 +150,7 @@ export class TokenService { tokenIDs, 'token.tokenMetadata', this.tokenMetadata.bind(this), + CacheTtlInfo.Token, ); } @@ -176,6 +178,7 @@ export class TokenService { collections, 'token.getNftCollectionMetadata', this.getNftCollectionMetadata.bind(this), + CacheTtlInfo.Token, ); } From ea8af0ac780b2a54cfd669abebd3932cf2cdf1b3 Mon Sep 17 00:00:00 2001 From: Claudiu Lataretu Date: Wed, 11 Sep 2024 21:35:42 +0300 Subject: [PATCH 312/313] SERVICES-2582: fix set local cache in get all keys Signed-off-by: Claudiu Lataretu --- src/utils/get.many.utils.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/utils/get.many.utils.ts b/src/utils/get.many.utils.ts index c3b098dec..dad22b19b 100644 --- a/src/utils/get.many.utils.ts +++ b/src/utils/get.many.utils.ts @@ -28,7 +28,13 @@ async function getMany( const remoteValues = await cacheService.getManyRemote(missingKeys); if (localTtl > 0) { - await cacheService.setManyLocal(missingKeys, remoteValues, localTtl); + await cacheService.setManyLocal( + missingKeys, + remoteValues.map((value) => + value ? parseCachedNullOrUndefined(value) : undefined, + ), + localTtl, + ); } for (const [index, missingIndex] of missingIndexes.entries()) { From e6d844938d95ccb46e9c93b0115348a52a59fa23 Mon Sep 17 00:00:00 2001 From: hschiau Date: Thu, 12 Sep 2024 09:56:29 +0300 Subject: [PATCH 313/313] SERVICES-2584: use config delay value --- src/services/crons/analytics.cache.warmer.service.ts | 4 ++-- src/services/crons/pair.cache.warmer.service.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/crons/analytics.cache.warmer.service.ts b/src/services/crons/analytics.cache.warmer.service.ts index 999d1d94a..c514fbb07 100644 --- a/src/services/crons/analytics.cache.warmer.service.ts +++ b/src/services/crons/analytics.cache.warmer.service.ts @@ -51,13 +51,13 @@ export class AnalyticsCacheWarmerService { awsOneYear(), 'feeBurned', ); - delay(1000); + delay(constantsConfig.AWS_QUERY_CACHE_WARMER_DELAY); const penaltyBurned = await this.analyticsCompute.computeTokenBurned( constantsConfig.MEX_TOKEN_ID, awsOneYear(), 'penaltyBurned', ); - delay(1000); + delay(constantsConfig.AWS_QUERY_CACHE_WARMER_DELAY); const cachedKeys = await Promise.all([ this.analyticsSetter.feeTokenBurned( constantsConfig.MEX_TOKEN_ID, diff --git a/src/services/crons/pair.cache.warmer.service.ts b/src/services/crons/pair.cache.warmer.service.ts index b20b59734..dea86c60c 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -90,26 +90,26 @@ export class PairCacheWarmerService { metric: 'firstTokenVolume', time, }); - await delay(1000); + await delay(constantsConfig.AWS_QUERY_CACHE_WARMER_DELAY); const secondTokenVolume24h = await this.analyticsQuery.getAggregatedValue({ series: pairAddress, metric: 'secondTokenVolume', time, }); - await delay(1000); + await delay(constantsConfig.AWS_QUERY_CACHE_WARMER_DELAY); const volumeUSD24h = await this.analyticsQuery.getAggregatedValue({ series: pairAddress, metric: 'volumeUSD', time, }); - await delay(1000); + await delay(constantsConfig.AWS_QUERY_CACHE_WARMER_DELAY); const feesUSD24h = await this.analyticsQuery.getAggregatedValue({ series: pairAddress, metric: 'feesUSD', time, }); - await delay(1000); + await delay(constantsConfig.AWS_QUERY_CACHE_WARMER_DELAY); const cachedKeys = await Promise.all([ this.pairSetterService.setFirstTokenVolume(