diff --git a/src/modules/pair/models/pair.model.ts b/src/modules/pair/models/pair.model.ts index 469b5b8fc..8a6872d77 100644 --- a/src/modules/pair/models/pair.model.ts +++ b/src/modules/pair/models/pair.model.ts @@ -3,6 +3,7 @@ import { PaginationArgs } from '../../dex.model'; 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'; @ArgsType() export class GetPairsArgs extends PaginationArgs {} @@ -102,6 +103,11 @@ export class PairModel { @Field() specialFeePercent: number; + @Field({ + description: 'Percentage of special fees that go to the fees collector', + }) + feesCollectorCutPercentage: number; + @Field(() => [String]) trustedSwapPairs: string[]; @@ -126,6 +132,12 @@ export class PairModel { @Field(() => [FeeDestination]) feeDestinations: FeeDestination[]; + @Field(() => FeesCollectorModel, { + nullable: true, + description: 'Fees collector set for this pair', + }) + feesCollector: FeesCollectorModel; + constructor(init?: Partial) { Object.assign(this, init); } diff --git a/src/modules/pair/pair.resolver.ts b/src/modules/pair/pair.resolver.ts index a06f4d8a9..f14b2b2ad 100644 --- a/src/modules/pair/pair.resolver.ts +++ b/src/modules/pair/pair.resolver.ts @@ -25,6 +25,8 @@ import { EsdtToken } from '../tokens/models/esdtToken.model'; 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'; @Resolver(() => PairModel) export class PairResolver { @@ -136,6 +138,16 @@ export class PairResolver { return this.pairAbi.specialFeePercent(parent.address); } + @ResolveField() + async feesCollectorCutPercentage( + @Parent() parent: PairModel, + ): Promise { + const fees = await this.pairAbi.feesCollectorCutPercentage( + parent.address, + ); + return fees / constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS; + } + @ResolveField() async type(@Parent() parent: PairModel): Promise { return this.pairCompute.type(parent.address); @@ -182,6 +194,21 @@ export class PairResolver { return this.pairAbi.feeDestinations(parent.address); } + @ResolveField() + async feesCollector( + @Parent() parent: PairModel, + ): Promise { + const feesCollectorAddress = await this.pairAbi.feesCollectorAddress( + parent.address, + ); + + return feesCollectorAddress + ? new FeesCollectorModel({ + address: feesCollectorAddress, + }) + : undefined; + } + @Query(() => String) async getAmountOut( @Args('pairAddress') pairAddress: string, diff --git a/src/modules/pair/services/pair.abi.service.ts b/src/modules/pair/services/pair.abi.service.ts index 8a906d3b8..161ca934b 100644 --- a/src/modules/pair/services/pair.abi.service.ts +++ b/src/modules/pair/services/pair.abi.service.ts @@ -15,6 +15,7 @@ import { EnumValue, Field, ResultsParser, + ReturnCode, Struct, TokenIdentifierValue, U64Value, @@ -702,4 +703,56 @@ export class PairAbiService ).toFixed(), }); } + + @ErrorLoggerAsync({ + className: PairAbiService.name, + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async feesCollectorAddress(pairAddress: string): Promise { + return await this.getFeesCollectorAddressRaw(pairAddress); + } + + async getFeesCollectorAddressRaw(address: string): Promise { + const contract = await this.mxProxy.getPairSmartContract(address); + const interaction: Interaction = + contract.methods.getFeesCollectorAddress([]); + const response = await this.getGenericData(interaction); + + if (response.returnCode.equals(ReturnCode.UserError)) { + return undefined; + } + + return new Address(response.firstValue.valueOf().toString()).bech32(); + } + + @ErrorLoggerAsync({ + className: PairAbiService.name, + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'pair', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async feesCollectorCutPercentage(pairAddress: string): Promise { + return await this.getFeesCollectorCutPercentageRaw(pairAddress); + } + + async getFeesCollectorCutPercentageRaw(address: string): Promise { + const contract = await this.mxProxy.getPairSmartContract(address); + const interaction: Interaction = + contract.methods.getFeesCollectorCutPercentage([]); + const response = await this.getGenericData(interaction); + + if (response.returnCode.equals(ReturnCode.UserError)) { + return undefined; + } + + return response.firstValue.valueOf().toNumber(); + } } diff --git a/src/modules/pair/services/pair.setter.service.ts b/src/modules/pair/services/pair.setter.service.ts index bfba12074..486cb89de 100644 --- a/src/modules/pair/services/pair.setter.service.ts +++ b/src/modules/pair/services/pair.setter.service.ts @@ -321,17 +321,6 @@ export class PairSetterService extends GenericSetterService { ); } - async setExternSwapGasLimit( - pairAddress: string, - value: string, - ): Promise { - return await this.setData( - this.getCacheKey('externSwapGasLimit', pairAddress), - value, - oneHour(), - ); - } - async setWhitelistedAddresses( pairAddress: string, value: string[], @@ -351,11 +340,27 @@ export class PairSetterService extends GenericSetterService { ); } - async setTransferExecGasLimit(pairAddress: string, value: string) { + async setFeesCollectorAddress( + pairAddress: string, + value: string, + ): Promise { return await this.setData( - this.getCacheKey('transferExecGasLimit', pairAddress), + this.getCacheKey('feesCollectorAddress', pairAddress), value, - oneHour(), + CacheTtlInfo.ContractState.remoteTtl, + CacheTtlInfo.ContractState.localTtl, + ); + } + + async setFeesCollectorCutPercentage( + pairAddress: string, + value: number, + ): Promise { + return await this.setData( + this.getCacheKey('feesCollectorCutPercentage', 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 bdc89eaa4..2acf0665b 100644 --- a/src/services/crons/pair.cache.warmer.service.ts +++ b/src/services/crons/pair.cache.warmer.service.ts @@ -159,45 +159,63 @@ export class PairCacheWarmerService { @Cron(CronExpression.EVERY_MINUTE) async cachePairsInfo(): Promise { - const pairsAddresses = await this.routerAbi.pairsAddress(); + Locker.lock('pairsInfo', async () => { + const pairsAddresses = await this.routerAbi.pairsAddress(); - for (const pairAddress of pairsAddresses) { - const [ - feesAPR, - state, - type, - feeState, - totalFeePercent, - specialFeePercent, - ] = await Promise.all([ - this.pairComputeService.computeFeesAPR(pairAddress), - this.pairAbi.getStateRaw(pairAddress), - this.pairComputeService.computeTypeFromTokens(pairAddress), - this.pairAbi.getFeeStateRaw(pairAddress), - this.pairAbi.getTotalFeePercentRaw(pairAddress), - this.pairAbi.getSpecialFeePercentRaw(pairAddress), - ]); + for (const pairAddress of pairsAddresses) { + const [ + feesAPR, + state, + type, + feeState, + totalFeePercent, + specialFeePercent, + feesCollectorAddress, + feesCollectorCutPercentage, + ] = await Promise.all([ + this.pairComputeService.computeFeesAPR(pairAddress), + this.pairAbi.getStateRaw(pairAddress), + this.pairComputeService.computeTypeFromTokens(pairAddress), + this.pairAbi.getFeeStateRaw(pairAddress), + this.pairAbi.getTotalFeePercentRaw(pairAddress), + this.pairAbi.getSpecialFeePercentRaw(pairAddress), + this.pairAbi.getFeesCollectorAddressRaw(pairAddress), + this.pairAbi.getFeesCollectorCutPercentageRaw(pairAddress), + ]); - const cachedKeys = await Promise.all([ - this.pairSetterService.setFeesAPR(pairAddress, feesAPR), - this.pairSetterService.setState(pairAddress, state), - this.pairSetterService.setType(pairAddress, type), - this.pairSetterService.setFeeState(pairAddress, feeState), - this.pairSetterService.setTotalFeePercent( - pairAddress, - new BigNumber(totalFeePercent) - .dividedBy(constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS) - .toNumber(), - ), - this.pairSetterService.setSpecialFeePercent( - pairAddress, - new BigNumber(specialFeePercent) - .dividedBy(constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS) - .toNumber(), - ), - ]); - await this.deleteCacheKeys(cachedKeys); - } + const cachedKeys = await Promise.all([ + this.pairSetterService.setFeesAPR(pairAddress, feesAPR), + this.pairSetterService.setState(pairAddress, state), + this.pairSetterService.setType(pairAddress, type), + this.pairSetterService.setFeeState(pairAddress, feeState), + this.pairSetterService.setTotalFeePercent( + pairAddress, + new BigNumber(totalFeePercent) + .dividedBy( + constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS, + ) + .toNumber(), + ), + this.pairSetterService.setSpecialFeePercent( + pairAddress, + new BigNumber(specialFeePercent) + .dividedBy( + constantsConfig.SWAP_FEE_PERCENT_BASE_POINTS, + ) + .toNumber(), + ), + this.pairSetterService.setFeesCollectorAddress( + pairAddress, + feesCollectorAddress, + ), + this.pairSetterService.setFeesCollectorCutPercentage( + pairAddress, + feesCollectorCutPercentage, + ), + ]); + await this.deleteCacheKeys(cachedKeys); + } + }); } @Cron('*/12 * * * * *') // Update prices and reserves every 12 seconds