From ae56ec78fe735b05c90c106f952b45f7d0c571e5 Mon Sep 17 00:00:00 2001 From: luke <123917244@qq.com> Date: Mon, 7 Apr 2025 17:31:36 +0800 Subject: [PATCH 01/11] feat: usdc price --- .../src/config/config.service.ts | 22 +++ .../src/core/contract.service.ts | 33 +++- .../src/core/core.module.ts | 2 + .../src/project/price.service.ts | 187 ++++++++++++++++-- .../src/project/project.model.ts | 25 ++- .../src/project/project.resolver.ts | 4 +- .../src/project/project.service.ts | 10 +- 7 files changed, 260 insertions(+), 23 deletions(-) diff --git a/apps/indexer-coordinator/src/config/config.service.ts b/apps/indexer-coordinator/src/config/config.service.ts index 39a8496c4..7c86ba525 100644 --- a/apps/indexer-coordinator/src/config/config.service.ts +++ b/apps/indexer-coordinator/src/config/config.service.ts @@ -4,13 +4,27 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, In } from 'typeorm'; +import { argv } from '../yargs'; import { ConfigEntity } from './config.model'; +export const COIN_ADDRESS = { + testnet: { + baseUSDC: ['0x2d9dcE396FcD6543Da1Ba7c9029c4B77E7716C74', 18], + baseSQT: ['0x37B797EBE14B4490FE64c67390AeCfE20D650953', 18], + }, + mainnet: { + baseUSDC: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 6], + baseSQT: ['0x858c50C3AF1913b0E849aFDB74617388a1a5340d', 18], + }, +}; + export enum ConfigType { FLEX_PRICE = 'flex_price', FLEX_PRICE_RATIO = 'flex_price_ratio', FLEX_VALID_PERIOD = 'flex_valid_period', FLEX_ENABLED = 'flex_enabled', + FLEX_TOKEN_ADDRESS = 'flex_token_address', + FLEX_SLIPPAGE = 'flex_slippage', ALLOCATION_REWARD_THRESHOLD = 'allocation_reward_threshold', ALLOCATION_REWARD_LAST_FORCE_TIME = 'allocation_reward_last_force_time', STATE_CHANNEL_REWARD_THRESHOLD = 'state_channel_reward_threshold', @@ -23,6 +37,8 @@ const defaultConfig: Record = { [ConfigType.FLEX_PRICE_RATIO]: '80', [ConfigType.FLEX_VALID_PERIOD]: `${60 * 60 * 24 * 3}`, // 3 days [ConfigType.FLEX_ENABLED]: 'true', + [ConfigType.FLEX_TOKEN_ADDRESS]: `${COIN_ADDRESS[argv.network].baseSQT[0]}`, + [ConfigType.FLEX_SLIPPAGE]: '5', [ConfigType.ALLOCATION_REWARD_THRESHOLD]: '2000000000000000000000', // 2000 sqt [ConfigType.ALLOCATION_REWARD_LAST_FORCE_TIME]: '0', [ConfigType.STATE_CHANNEL_REWARD_THRESHOLD]: '2000000000000000000000', // 2000 sqt @@ -73,6 +89,8 @@ export class ConfigService { ConfigType.FLEX_PRICE_RATIO, ConfigType.FLEX_VALID_PERIOD, ConfigType.FLEX_ENABLED, + ConfigType.FLEX_TOKEN_ADDRESS, + ConfigType.FLEX_SLIPPAGE, ]; const config = await this.configRepo.find({ where: { key: In(keys) }, @@ -100,6 +118,10 @@ export class ConfigService { return values; } + getUSDC() { + return COIN_ADDRESS[argv.network].baseUSDC; + } + getDefault(key: string) { return defaultConfig[key]; } diff --git a/apps/indexer-coordinator/src/core/contract.service.ts b/apps/indexer-coordinator/src/core/contract.service.ts index c2524720c..b4b933c67 100644 --- a/apps/indexer-coordinator/src/core/contract.service.ts +++ b/apps/indexer-coordinator/src/core/contract.service.ts @@ -2,22 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 import { Injectable } from '@nestjs/common'; -import { ContractSDK } from '@subql/contract-sdk'; +import { ContractSDK, PriceOracle__factory, SQContracts } from '@subql/contract-sdk'; import { cidToBytes32 } from '@subql/network-clients'; import { isValidPrivate, toBuffer } from 'ethereumjs-util'; import { BigNumber, Overrides, Wallet, providers } from 'ethers'; -import { parseEther } from 'ethers/lib/utils'; +import { parseEther, formatEther } from 'ethers/lib/utils'; import { getEthProvider } from 'src/utils/ethProvider'; import { mutexPromise } from 'src/utils/promise'; import { redisDel, redisGetObj, redisSetObj } from 'src/utils/redis'; import { argv } from 'src/yargs'; +import { ConfigService } from '../config/config.service'; import { Config } from '../configure/configure.module'; import { ChainID, initContractSDK, initProvider, networkToChainID } from '../utils/contractSDK'; import { decrypt } from '../utils/encrypt'; import { TextColor, colorText, debugLogger, getLogger } from '../utils/logger'; import { Controller } from './account.model'; import { AccountService } from './account.service'; -import { IndexerDeploymentStatus, TxFun, TxOptions, TxType } from './types'; +import { IndexerDeploymentStatus, TxOptions, TxType } from './types'; const MAX_RETRY = 3; const logger = getLogger('contract'); @@ -33,7 +34,11 @@ export class ContractService { private gasFeeLimit: BigNumber; private gasFeeLimitEnabled: boolean; - constructor(private accountService: AccountService, private config: Config) { + constructor( + private accountService: AccountService, + private config: Config, + private configService: ConfigService + ) { this.chainID = networkToChainID[config.network]; this.existentialBalance = parseEther('0.001'); this.provider = initProvider(config.networkEndpoint, this.chainID, logger); @@ -212,6 +217,26 @@ export class ContractService { } } + async convertFromUSDC(amount: string): Promise<{ error?: string; data?: string }> { + try { + const [USDC_ADDRESS] = this.configService.getUSDC(); + const priceOracleAddress = await this.sdk.settings.getContractAddress( + SQContracts.PriceOracle + ); + const priceOracleFactory = PriceOracle__factory.connect(priceOracleAddress, this.provider); + const assetPrice = await priceOracleFactory.convertPrice( + USDC_ADDRESS, + this.sdk.sqToken.address, + amount + ); + logger.debug(`convertFromUSDC amount:${amount}, asset:${formatEther(assetPrice)}`); + return { data: assetPrice.toString() }; + } catch (e) { + logger.error(`fail to convert from USDC. ${e.message}`); + return { error: e.message }; + } + } + async sendTransaction(txOptions: TxOptions) { if (this.gasFeeLimitEnabled) { switch (txOptions.type) { diff --git a/apps/indexer-coordinator/src/core/core.module.ts b/apps/indexer-coordinator/src/core/core.module.ts index b7c00ece0..cf5a57056 100644 --- a/apps/indexer-coordinator/src/core/core.module.ts +++ b/apps/indexer-coordinator/src/core/core.module.ts @@ -6,6 +6,7 @@ import { ScheduleModule } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; import { NetworkService } from 'src/network/network.service'; +import { ConfigModule } from '../config/config.module'; import { SubscriptionModule } from '../subscription/subscription.module'; import { Controller, Indexer } from './account.model'; import { AccountResolver } from './account.resolver'; @@ -22,6 +23,7 @@ import { ServiceResolver } from './service.resolver'; SubscriptionModule, TypeOrmModule.forFeature([Controller, Indexer]), ScheduleModule.forRoot(), + ConfigModule, ], providers: [ ContractService, diff --git a/apps/indexer-coordinator/src/project/price.service.ts b/apps/indexer-coordinator/src/project/price.service.ts index 39ffa45d8..9faf5b9b8 100644 --- a/apps/indexer-coordinator/src/project/price.service.ts +++ b/apps/indexer-coordinator/src/project/price.service.ts @@ -8,6 +8,7 @@ import axios from 'axios'; import { BigNumber } from 'ethers'; import _ from 'lodash'; import { ConfigService, ConfigType } from 'src/config/config.service'; +import { ContractService } from 'src/core/contract.service'; import { argv } from 'src/yargs'; import { In, Repository } from 'typeorm'; import { getLogger } from '../utils/logger'; @@ -21,7 +22,8 @@ export class PriceService { constructor( @InjectRepository(PaygEntity) private paygRepo: Repository, - private configService: ConfigService + private configService: ConfigService, + private contract: ContractService ) {} @Cron(CronExpression.EVERY_10_MINUTES) @@ -30,33 +32,99 @@ export class PriceService { await this.request(dids, true); } - async inlinePayg(paygs: Payg[]) { + async inlinePayg(paygs: Payg[], remoteExchangeRate: string) { const deploymentIds = paygs.map((p) => p.id); if (!deploymentIds.length) return; const dPrices = await this.getDominatePrice(deploymentIds); const defaultFlex = await this.configService.getFlexConfig(); + const [USDC_TOKEN, USDC_DECIMAL] = this.configService.getUSDC(); + const ONE_USDC = BigNumber.from(10).pow(USDC_DECIMAL).toString(); + + let effectiveExchangeRate = ''; for (const p of paygs) { - const exist = _.find(dPrices, (dp: DominantPrice) => dp.id === p.id); p.minPrice = p.price; + const exist = _.find(dPrices, (dp: DominantPrice) => dp.id === p.id); + if (p.useDefault) { p.price = defaultFlex[ConfigType.FLEX_PRICE]; p.minPrice = defaultFlex[ConfigType.FLEX_PRICE]; p.priceRatio = Number(defaultFlex[ConfigType.FLEX_PRICE_RATIO]); + p.token = defaultFlex[ConfigType.FLEX_TOKEN_ADDRESS]; + } + + if (p.token === USDC_TOKEN) { + if (!effectiveExchangeRate) { + const res = await this.checkEffectiveExchangeRate( + remoteExchangeRate, + ONE_USDC, + defaultFlex + ); + if (res.error) { + p.error = `payg: ${res.error}`; + } else { + effectiveExchangeRate = res.data; + } + } } if (exist && exist.price !== null) { + p.dominantPrice = exist.price; p.priceRatio = p.priceRatio !== null ? p.priceRatio : Number(defaultFlex[ConfigType.FLEX_PRICE_RATIO]); - const minPrice = BigNumber.from(p.price || 0); - const dominant = BigNumber.from(exist.price).mul(p.priceRatio).div(100); - p.price = minPrice.gt(dominant) ? minPrice.toString() : dominant.toString(); - p.dominantPrice = exist.price; + + if (exist.token === USDC_TOKEN) { + p.rawdominantToken = USDC_TOKEN; + + if (!effectiveExchangeRate) { + const res = await this.checkEffectiveExchangeRate( + remoteExchangeRate, + ONE_USDC, + defaultFlex + ); + if (res.error) { + p.error = `dominant: ${res.error}`; + } else { + effectiveExchangeRate = res.data; + } + } + } + } + } + + effectiveExchangeRate = effectiveExchangeRate ?? remoteExchangeRate; + + for (const p of paygs) { + p.exchangeRate = effectiveExchangeRate; + if (p.token === USDC_TOKEN) { + p.rawpaygMinPrice = p.price; + p.rawpaygToken = p.token; + + p.price = BigNumber.from(effectiveExchangeRate).mul(p.price).div(ONE_USDC).toString(); + p.minPrice = p.price; + p.token = this.contract.getSdk().sqToken.address; } + + if (!p.dominantPrice) continue; + + if (p.rawdominantToken === USDC_TOKEN) { + p.rawdominantPrice = p.dominantPrice; + + p.dominantPrice = BigNumber.from(effectiveExchangeRate) + .mul(p.dominantPrice) + .div(ONE_USDC) + .toString(); + } + + const minPrice = BigNumber.from(p.price || 0); + const dominant = BigNumber.from(p.dominantPrice).mul(p.priceRatio).div(100); + + p.price = minPrice.gt(dominant) ? minPrice.toString() : dominant.toString(); } } + // eslint-disable-next-line complexity async fillPaygAndDominatePrice(projects: Project[]) { const deploymentIds = projects.map((p) => p.id); if (deploymentIds.length) { @@ -83,6 +151,12 @@ export class PriceService { const defaultFlex = await this.configService.getFlexConfig(); + const [USDC_TOKEN, USDC_DECIMAL] = this.configService.getUSDC(); + const ONE_USDC = BigNumber.from(10).pow(USDC_DECIMAL).toString(); + const SQT_TOKEN = this.contract.getSdk().sqToken.address; + + let exchangeRate = ''; + for (const p of projects) { p.payg = _.find(paygs, (payg: PaygEntity) => payg.id === p.id); p.dominantPrice = _.find(prices, (pri: DominantPrice) => pri.id === p.id); @@ -94,6 +168,7 @@ export class PriceService { p.payg.price = defaultFlex[ConfigType.FLEX_PRICE]; p.payg.minPrice = defaultFlex[ConfigType.FLEX_PRICE]; p.payg.priceRatio = Number(defaultFlex[ConfigType.FLEX_PRICE_RATIO]); + p.payg.token = defaultFlex[ConfigType.FLEX_TOKEN_ADDRESS]; } else { p.payg.minPrice = p.payg.price; p.payg.priceRatio = @@ -102,8 +177,63 @@ export class PriceService { : Number(defaultFlex[ConfigType.FLEX_PRICE_RATIO]); } + if (p.payg.token === USDC_TOKEN) { + if (!exchangeRate) { + const transferRes = await this.contract.convertFromUSDC(ONE_USDC); + if (transferRes.error) { + p.payg.error = transferRes.error; + } else { + exchangeRate = transferRes.data; + } + } + } + // no dominant price - if (!p.dominantPrice?.price) continue; + if (!p.dominantPrice?.price) { + continue; + } + + if (p.dominantPrice.token === USDC_TOKEN) { + if (!exchangeRate) { + const transferRes = await this.contract.convertFromUSDC(ONE_USDC); + if (transferRes.error) { + p.payg.error = transferRes.error; + } else { + exchangeRate = transferRes.data; + } + } + } + } + + if (!exchangeRate) return; + + for (const p of projects) { + if (p.payg.token === USDC_TOKEN) { + p.payg.error = null; + p.payg.rawpaygMinPrice = p.payg.price; + p.payg.rawpaygToken = p.payg.token; + + p.payg.price = BigNumber.from(exchangeRate).mul(p.payg.price).div(ONE_USDC).toString(); + p.payg.minPrice = p.payg.price; + p.payg.token = SQT_TOKEN; + p.payg.exchangeRate = exchangeRate; + } + + if (!p.dominantPrice?.price) { + continue; + } + + if (p.dominantPrice?.token === USDC_TOKEN) { + p.payg.error = null; + p.dominantPrice.rawToken = p.dominantPrice.token; + p.dominantPrice.rawPrice = p.dominantPrice.price; + + p.dominantPrice.token = SQT_TOKEN; + p.dominantPrice.price = BigNumber.from(exchangeRate) + .mul(p.dominantPrice.price) + .div(ONE_USDC) + .toString(); + } const minPrice = BigNumber.from(p.payg.price || 0); const dominant = BigNumber.from(p.dominantPrice.price).mul(p.payg.priceRatio).div(100); @@ -112,13 +242,47 @@ export class PriceService { } } + private async checkEffectiveExchangeRate( + remoteExchangeRate: string, + ONE_USDC: string, + defaultFlex: Record + ): Promise<{ error?: string; data?: string }> { + const transferRes = await this.contract.convertFromUSDC(ONE_USDC); + if (transferRes.error) { + // p.error = transferRes.error; + return { error: transferRes.error }; + } else { + const curChainExchangeRateBig = BigNumber.from(transferRes.data); + const slippage = Number(defaultFlex[ConfigType.FLEX_SLIPPAGE]); + const rerBig = BigNumber.from(remoteExchangeRate); + + let smaller = rerBig; + let bigger = curChainExchangeRateBig; + + if (smaller.gte(bigger)) { + smaller = curChainExchangeRateBig; + bigger = rerBig; + } + + const upperbound = BigNumber.from(smaller) + .mul(100 + slippage) + .div(100); + + const effectiveExchangeRate = bigger.lte(upperbound) + ? remoteExchangeRate + : curChainExchangeRateBig.toString(); + + return { data: effectiveExchangeRate }; + } + } + private async getDominatePrice(deploymentIds: string[]): Promise { const res = []; const nocacheIds = []; for (const did of deploymentIds) { const c = this.cache.get(did); if (c) { - res.push(_.pick(c, ['id', 'price', 'lastError'])); + res.push(_.pick(c, ['id', 'price', 'token', 'lastError'])); continue; } nocacheIds.push(did); @@ -144,17 +308,18 @@ export class PriceService { if (r.data.error) { throw new Error(r.data.error); } - for (const { deployment: id, price } of r.data) { + for (const { deployment: id, price, token_address: token } of r.data) { const info = { id, price, + token, retrieveCount: 1, failCount: 0, }; if (setCache && price !== null) { this.cache.set(id, info); } - res.push(_.pick(info, ['id', 'price', 'lastError'])); + res.push(_.pick(info, ['id', 'price', 'token', 'lastError'])); } } catch (e) { getLogger('price').error(`fail to request price, error: ${e.message}`); diff --git a/apps/indexer-coordinator/src/project/project.model.ts b/apps/indexer-coordinator/src/project/project.model.ts index 4662e058b..c472a103c 100644 --- a/apps/indexer-coordinator/src/project/project.model.ts +++ b/apps/indexer-coordinator/src/project/project.model.ts @@ -49,6 +49,12 @@ export class DominantPrice { @Field({ nullable: true }) price?: string; @Field({ nullable: true }) + token?: string; + @Field({ nullable: true }) + rawPrice?: string; + @Field({ nullable: true }) + rawToken?: string; + @Field({ nullable: true }) retrieveCount?: number; @Field({ nullable: true }) failCount?: number; @@ -461,6 +467,24 @@ export class Payg extends PaygEntity { @Field({ nullable: true }) dominantPrice?: string; + + @Field({ nullable: true }) + exchangeRate?: string; + + @Field({ nullable: true }) + rawdominantPrice?: string; + + @Field({ nullable: true }) + rawdominantToken?: string; + + @Field({ nullable: true }) + rawpaygMinPrice?: string; + + @Field({ nullable: true }) + rawpaygToken?: string; + + @Field({ nullable: true }) + error?: string; } @ObjectType('Project') @@ -488,5 +512,4 @@ export class ProjectDetails extends ProjectEntity { @Field(() => DominantPrice, { nullable: true }) dominantPrice?: DominantPrice; - } diff --git a/apps/indexer-coordinator/src/project/project.resolver.ts b/apps/indexer-coordinator/src/project/project.resolver.ts index 6b3997f53..a98087db4 100644 --- a/apps/indexer-coordinator/src/project/project.resolver.ts +++ b/apps/indexer-coordinator/src/project/project.resolver.ts @@ -191,8 +191,8 @@ export class ProjectResolver { } @Query(() => [Payg]) - getAlivePaygs() { - return this.projectService.getAlivePaygs(); + getAlivePaygs(@Args('exchangeRate') exchangeRate: string) { + return this.projectService.getAlivePaygs(exchangeRate); } @Query(() => AggregatedManifest) diff --git a/apps/indexer-coordinator/src/project/project.service.ts b/apps/indexer-coordinator/src/project/project.service.ts index 39054ba78..1ca43a54e 100644 --- a/apps/indexer-coordinator/src/project/project.service.ts +++ b/apps/indexer-coordinator/src/project/project.service.ts @@ -177,11 +177,11 @@ export class ProjectService { }); } - async getAlivePaygs(): Promise { + async getAlivePaygs(remoteExchangeRate: string): Promise { // return this.paygRepo.find({ where: { price: Not('') } }); // FIXME remove this const paygs = await this.paygRepo.find({ where: { price: Not('') } }); - await this.priceService.inlinePayg(paygs); + await this.priceService.inlinePayg(paygs, remoteExchangeRate); for (const payg of paygs) { payg.overflow = 10000; } @@ -275,14 +275,14 @@ export class ProjectService { const flexConfig = await this.configService.getFlexConfig(); let paygConfig = {}; - if (flexConfig.flex_enabled === 'true') { + if (flexConfig[ConfigType.FLEX_ENABLED] === 'true') { paygConfig = { id: id.trim(), - price: flexConfig.flex_price, + price: flexConfig[ConfigType.FLEX_PRICE], expiration: Number(this.configService.getDefault(ConfigType.FLEX_VALID_PERIOD)) || 259200, // default: 3 days threshold: 10, overflow: 10, - token: this.contract.getSdk().sqToken.address, + token: flexConfig[ConfigType.FLEX_TOKEN_ADDRESS], } as PaygEntity; } From 29df28175edc188bd575223189bae128f683acc2 Mon Sep 17 00:00:00 2001 From: luke <123917244@qq.com> Date: Tue, 8 Apr 2025 10:52:02 +0800 Subject: [PATCH 02/11] feat: compatibility --- .../src/project/price.service.ts | 23 ++++++++++++++++++- .../src/project/project.resolver.ts | 2 +- .../src/project/project.service.ts | 2 +- ttt.js | 17 ++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 ttt.js diff --git a/apps/indexer-coordinator/src/project/price.service.ts b/apps/indexer-coordinator/src/project/price.service.ts index 9faf5b9b8..7db526858 100644 --- a/apps/indexer-coordinator/src/project/price.service.ts +++ b/apps/indexer-coordinator/src/project/price.service.ts @@ -32,7 +32,7 @@ export class PriceService { await this.request(dids, true); } - async inlinePayg(paygs: Payg[], remoteExchangeRate: string) { + async inlinePayg(paygs: Payg[], remoteExchangeRate?: string, trim: boolean = true) { const deploymentIds = paygs.map((p) => p.id); if (!deploymentIds.length) return; const dPrices = await this.getDominatePrice(deploymentIds); @@ -98,6 +98,10 @@ export class PriceService { for (const p of paygs) { p.exchangeRate = effectiveExchangeRate; if (p.token === USDC_TOKEN) { + if (!p.exchangeRate) { + getLogger('price').error(`${p.id} fail to get payg exchange rate from usdc. ${p.error}`); + continue; + } p.rawpaygMinPrice = p.price; p.rawpaygToken = p.token; @@ -109,6 +113,12 @@ export class PriceService { if (!p.dominantPrice) continue; if (p.rawdominantToken === USDC_TOKEN) { + if (!p.exchangeRate) { + getLogger('price').error( + `${p.id} fail to get dominant price exchange rate for usdc. ${p.error}` + ); + continue; + } p.rawdominantPrice = p.dominantPrice; p.dominantPrice = BigNumber.from(effectiveExchangeRate) @@ -122,6 +132,12 @@ export class PriceService { p.price = minPrice.gt(dominant) ? minPrice.toString() : dominant.toString(); } + + if (trim) { + _.remove(paygs, function (p) { + return !p.exchangeRate && (p.token === USDC_TOKEN || p.rawdominantToken === USDC_TOKEN); + }); + } } // eslint-disable-next-line complexity @@ -253,6 +269,11 @@ export class PriceService { return { error: transferRes.error }; } else { const curChainExchangeRateBig = BigNumber.from(transferRes.data); + + if (!remoteExchangeRate) { + return { data: curChainExchangeRateBig.toString() }; + } + const slippage = Number(defaultFlex[ConfigType.FLEX_SLIPPAGE]); const rerBig = BigNumber.from(remoteExchangeRate); diff --git a/apps/indexer-coordinator/src/project/project.resolver.ts b/apps/indexer-coordinator/src/project/project.resolver.ts index a98087db4..03ae1e683 100644 --- a/apps/indexer-coordinator/src/project/project.resolver.ts +++ b/apps/indexer-coordinator/src/project/project.resolver.ts @@ -191,7 +191,7 @@ export class ProjectResolver { } @Query(() => [Payg]) - getAlivePaygs(@Args('exchangeRate') exchangeRate: string) { + getAlivePaygs(@Args('exchangeRate', { nullable: true }) exchangeRate: string) { return this.projectService.getAlivePaygs(exchangeRate); } diff --git a/apps/indexer-coordinator/src/project/project.service.ts b/apps/indexer-coordinator/src/project/project.service.ts index 1ca43a54e..98a16d63b 100644 --- a/apps/indexer-coordinator/src/project/project.service.ts +++ b/apps/indexer-coordinator/src/project/project.service.ts @@ -177,7 +177,7 @@ export class ProjectService { }); } - async getAlivePaygs(remoteExchangeRate: string): Promise { + async getAlivePaygs(remoteExchangeRate?: string): Promise { // return this.paygRepo.find({ where: { price: Not('') } }); // FIXME remove this const paygs = await this.paygRepo.find({ where: { price: Not('') } }); diff --git a/ttt.js b/ttt.js new file mode 100644 index 000000000..151deb5ec --- /dev/null +++ b/ttt.js @@ -0,0 +1,17 @@ +const target = Object.defineProperties({}, { + foo: { + value: 123, + writable: false, + configurable: false + }, +}); + +const handler = { + get(target, propKey) { + return 123; + } +}; + +const proxy = new Proxy(target, handler); + +proxy.xx From 237998c852aae39f6c142ee55df84009a610a7f9 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Tue, 8 Apr 2025 16:43:12 +0800 Subject: [PATCH 03/11] feat: add usdc price preview --- apps/indexer-admin/package.json | 6 +- apps/indexer-admin/src/hooks/paygHook.ts | 9 +- .../src/pages/global-config/GlobalConfig.tsx | 76 ++++--- .../pages/project-details/payg/paygCard.tsx | 191 ++++++++++++------ .../src/pages/project-details/types.ts | 2 + .../pages/projects/components/projectItem.tsx | 30 ++- apps/indexer-admin/src/utils/queries.ts | 3 + apps/indexer-admin/yarn.lock | 33 +-- 8 files changed, 244 insertions(+), 106 deletions(-) diff --git a/apps/indexer-admin/package.json b/apps/indexer-admin/package.json index 2f86d22b8..788a101df 100644 --- a/apps/indexer-admin/package.json +++ b/apps/indexer-admin/package.json @@ -20,11 +20,11 @@ "@patternfly/react-log-viewer": "^4.43.19", "@rainbow-me/rainbowkit": "^1.2.0", "@subql/components": "3.0.1-3", - "@subql/contract-sdk": "1.3.0", + "@subql/contract-sdk": "1.7.0", "@subql/network-clients": "1.1.0", - "@subql/network-config": "1.1.1", + "@subql/network-config": "1.1.2-3", "@subql/network-query": "1.1.0", - "@subql/react-hooks": "1.1.0", + "@subql/react-hooks": "1.1.2-19", "ahooks": "^3.7.7", "animate.css": "^4.1.1", "antd": "^5.3.2", diff --git a/apps/indexer-admin/src/hooks/paygHook.ts b/apps/indexer-admin/src/hooks/paygHook.ts index fa7a64f38..65785da73 100644 --- a/apps/indexer-admin/src/hooks/paygHook.ts +++ b/apps/indexer-admin/src/hooks/paygHook.ts @@ -13,7 +13,7 @@ import { BigNumber } from 'ethers'; import { useContractSDK } from 'containers/contractSdk'; import { useCoordinatorIndexer } from 'containers/coordinatorIndexer'; import { PAYG_PRICE } from 'utils/queries'; -import { network } from 'utils/web3'; +import { network, SUPPORTED_NETWORK } from 'utils/web3'; import { useProjectDetails } from './projectHook'; @@ -42,17 +42,18 @@ export function usePAYGConfig(deploymentId: string) { useDefault: true, }; } + const usdcDecimal = STABLE_COIN_DECIMAL[SUPPORTED_NETWORK]; return { paygPrice: payg.token === sdk?.sqToken.address ? formatEther(BigNumber.from(payg.price).mul(1000)) - : formatUnits(BigNumber.from(payg.price).mul(1000), +STABLE_COIN_DECIMAL), + : formatUnits(BigNumber.from(payg.price).mul(1000), usdcDecimal), paygRatio: payg.priceRatio || 80, paygMinPrice: payg.token === sdk?.sqToken.address ? formatEther(BigNumber.from(payg.minPrice).mul(1000)) - : formatUnits(BigNumber.from(payg.minPrice).mul(1000), +STABLE_COIN_DECIMAL), + : formatUnits(BigNumber.from(payg.minPrice).mul(1000), usdcDecimal), paygExpiration: (payg.expiration ?? 0) / daySeconds, token: payg.token, useDefault: payg.useDefault, @@ -73,7 +74,7 @@ export function usePAYGConfig(deploymentId: string) { const paygPrice = token === sdk?.sqToken.address ? parseEther(minPrice) - : parseUnits(minPrice, +import.meta.env.VITE_STABLE_TOKEN_DECIMAL); + : parseUnits(minPrice, STABLE_COIN_DECIMAL[SUPPORTED_NETWORK]); await paygPriceRequest({ variables: { diff --git a/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx b/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx index 7e5868d9c..8190632bc 100644 --- a/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx +++ b/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx @@ -6,11 +6,14 @@ import { BsBoxArrowInUpRight } from 'react-icons/bs'; import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined'; import { useMutation, useQuery } from '@apollo/client'; import { openNotification, Spinner, Tag, Typography } from '@subql/components'; -import { formatSQT, useAsyncMemo } from '@subql/react-hooks'; +import { SQT_DECIMAL, STABLE_COIN_DECIMAL } from '@subql/network-config'; +import { formatSQT, useAsyncMemo, useStableCoin } from '@subql/react-hooks'; +import { useUpdate } from 'ahooks'; import { Button, Form, Input, Select, Switch, Tooltip } from 'antd'; import { useForm } from 'antd/es/form/Form'; import BigNumberJs from 'bignumber.js'; -import { parseEther } from 'ethers/lib/utils'; +import { STABLE_COIN_ADDRESS } from 'conf/stableCoin'; +import { formatUnits, parseEther, parseUnits } from 'ethers/lib/utils'; import styled from 'styled-components'; import { SubqlInput } from 'styles/input'; @@ -23,7 +26,7 @@ import { getIndexerSocialCredibility, SET_CONFIG, } from 'utils/queries'; -import { TOKEN_SYMBOL } from 'utils/web3'; +import { SUPPORTED_NETWORK, TOKEN_SYMBOL } from 'utils/web3'; import NodeOperatorInformation from './components/NodeOperatorInforamtion'; @@ -41,11 +44,13 @@ const Wrapper = styled.div` const GlobalConfig: FC = () => { const { indexer: account, loading } = useCoordinatorIndexer(); + const sdk = useContractSDK(); + const { transPrice } = useStableCoin(sdk, SUPPORTED_NETWORK); + const update = useUpdate(); const [config, setConfig] = useState({ autoReduceOverAllocation: false, flexEnabled: false, }); - const sdk = useContractSDK(); const configQueryData = useQuery(GET_ALL_CONFIG); const [setConfigMutation] = useMutation(SET_CONFIG); @@ -79,13 +84,26 @@ const GlobalConfig: FC = () => { const validPeriod = configQueryData.data.allConfig.find( (i) => i.key === ConfigKey.FlexValidPeriod ); + const tokenAddress = configQueryData.data.allConfig.find( + (i) => i.key === ConfigKey.FlexTokenAddress + ); + + const isSQT = tokenAddress?.value === sdk?.sqToken.address || !tokenAddress?.value; form.setFieldValue('priceRatio', BigNumberJs(priceRatio?.value || '0')); - form.setFieldValue('price', BigNumberJs(formatSQT(price?.value || '0')).multipliedBy(1000)); + form.setFieldValue('flexTokenAddress', tokenAddress?.value || sdk?.sqToken.address); + form.setFieldValue( + 'price', + BigNumberJs( + isSQT + ? formatSQT(price?.value || '0') + : formatUnits(price?.value || '0', STABLE_COIN_DECIMAL[SUPPORTED_NETWORK]) + ).multipliedBy(1000) + ); form.setFieldValue('validPeriod', BigNumberJs(validPeriod?.value || '0').dividedBy(86400)); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [configQueryData.data, form]); + }, [configQueryData.data, form, sdk]); return (
{ /> + { type="number" addonAfter={ { + console.warn(e); setPaygConf({ ...paygConf, token: e, }); }} options={[ - // { - // value: STABLE_COIN_ADDRESS, - // label: ( - //
- // - // USDC - //
- // ), - // }, + { + value: STABLE_COIN_ADDRESS, + label: ( +
+ + USDC +
+ ), + }, { value: sdk.sqToken.address, label: ( diff --git a/apps/indexer-admin/src/pages/project-details/types.ts b/apps/indexer-admin/src/pages/project-details/types.ts index 718ac1613..90f2b8891 100644 --- a/apps/indexer-admin/src/pages/project-details/types.ts +++ b/apps/indexer-admin/src/pages/project-details/types.ts @@ -114,9 +114,11 @@ export type ProjectConfig = { priceRatio: number; token: string; useDefault: boolean; + error?: string; }; dominantPrice: { price?: string; + token?: string; lastError?: string; }; diff --git a/apps/indexer-admin/src/pages/projects/components/projectItem.tsx b/apps/indexer-admin/src/pages/projects/components/projectItem.tsx index 5c229b550..ccb06f220 100644 --- a/apps/indexer-admin/src/pages/projects/components/projectItem.tsx +++ b/apps/indexer-admin/src/pages/projects/components/projectItem.tsx @@ -4,8 +4,10 @@ import { FC, useMemo } from 'react'; import { IoWarning } from 'react-icons/io5'; import { useHistory } from 'react-router-dom'; +import { formatUnits } from '@ethersproject/units'; import { Spinner, SubqlProgress, Tag, Typography } from '@subql/components'; import { indexingProgress } from '@subql/network-clients'; +import { SQT_DECIMAL, STABLE_COIN_DECIMAL, STABLE_COIN_SYMBOLS } from '@subql/network-config'; import { formatSQT } from '@subql/react-hooks'; import { Tooltip } from 'antd'; import BigNumberJs from 'bignumber.js'; @@ -13,6 +15,7 @@ import { isNull, isUndefined } from 'lodash'; import Avatar from 'components/avatar'; import UnsafeWarn from 'components/UnsafeWarn'; +import { useContractSDK } from 'containers/contractSdk'; import { useCoordinatorIndexer } from 'containers/coordinatorIndexer'; import { useDeploymentStatus, useIsOnline } from 'hooks/projectHook'; import { useGetIfUnsafeDeployment } from 'hooks/useGetIfUnsafeDeployment'; @@ -24,7 +27,7 @@ import { } from 'pages/project-details/types'; import { cidToBytes32 } from 'utils/ipfs'; import { formatValueToFixed } from 'utils/units'; -import { TOKEN_SYMBOL } from 'utils/web3'; +import { SUPPORTED_NETWORK, TOKEN_SYMBOL } from 'utils/web3'; import { OnlineStatus, statusText } from '../constant'; import { ItemContainer, ProfileContainer, ProjectItemContainer } from '../styles'; @@ -36,7 +39,7 @@ type Props = Omit & { const ProjectItem: FC = (props) => { const { id, details, payg, metadata, projectType, metadataLoading, dominantPrice } = props; - + const sdk = useContractSDK(); const { indexer: account } = useCoordinatorIndexer(); const history = useHistory(); const { status } = useDeploymentStatus(id); @@ -122,8 +125,27 @@ const ProjectItem: FC = (props) => { {payg?.expiration ? ( - {BigNumberJs(formatSQT(payg?.price)).multipliedBy(1000).toFixed()} {TOKEN_SYMBOL} / 1000 - requests + {BigNumberJs( + formatUnits( + payg?.price, + payg.token === sdk?.sqToken.address + ? SQT_DECIMAL + : STABLE_COIN_DECIMAL[SUPPORTED_NETWORK] + ) + ) + .multipliedBy(1000) + .toFixed()}{' '} + {payg.token === sdk?.sqToken.address + ? TOKEN_SYMBOL + : STABLE_COIN_SYMBOLS[SUPPORTED_NETWORK]}{' '} + / 1000 requests + {payg.error ? ( + + + + ) : ( + '' + )} {dominantPrice.lastError ? ( Date: Fri, 11 Apr 2025 17:50:49 +0800 Subject: [PATCH 04/11] feat: exchange rate --- .../pages/projects/components/projectItem.tsx | 2 +- .../src/project/price.service.ts | 20 ++++++++++--------- .../src/project/project.service.ts | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/indexer-admin/src/pages/projects/components/projectItem.tsx b/apps/indexer-admin/src/pages/projects/components/projectItem.tsx index ccb06f220..244bea283 100644 --- a/apps/indexer-admin/src/pages/projects/components/projectItem.tsx +++ b/apps/indexer-admin/src/pages/projects/components/projectItem.tsx @@ -140,7 +140,7 @@ const ProjectItem: FC = (props) => { : STABLE_COIN_SYMBOLS[SUPPORTED_NETWORK]}{' '} / 1000 requests {payg.error ? ( - + ) : ( diff --git a/apps/indexer-coordinator/src/project/price.service.ts b/apps/indexer-coordinator/src/project/price.service.ts index 7db526858..ca0733580 100644 --- a/apps/indexer-coordinator/src/project/price.service.ts +++ b/apps/indexer-coordinator/src/project/price.service.ts @@ -93,7 +93,7 @@ export class PriceService { } } - effectiveExchangeRate = effectiveExchangeRate ?? remoteExchangeRate; + effectiveExchangeRate = effectiveExchangeRate || remoteExchangeRate; for (const p of paygs) { p.exchangeRate = effectiveExchangeRate; @@ -141,7 +141,7 @@ export class PriceService { } // eslint-disable-next-line complexity - async fillPaygAndDominatePrice(projects: Project[]) { + async fillPaygAndDominatePrice(projects: Project[], convert: boolean = false) { const deploymentIds = projects.map((p) => p.id); if (deploymentIds.length) { const [paygRes, priceRes] = await Promise.allSettled([ @@ -193,7 +193,7 @@ export class PriceService { : Number(defaultFlex[ConfigType.FLEX_PRICE_RATIO]); } - if (p.payg.token === USDC_TOKEN) { + if (convert && p.payg.token === USDC_TOKEN) { if (!exchangeRate) { const transferRes = await this.contract.convertFromUSDC(ONE_USDC); if (transferRes.error) { @@ -209,7 +209,7 @@ export class PriceService { continue; } - if (p.dominantPrice.token === USDC_TOKEN) { + if (convert && p.dominantPrice.token === USDC_TOKEN) { if (!exchangeRate) { const transferRes = await this.contract.convertFromUSDC(ONE_USDC); if (transferRes.error) { @@ -224,7 +224,7 @@ export class PriceService { if (!exchangeRate) return; for (const p of projects) { - if (p.payg.token === USDC_TOKEN) { + if (convert && p.payg.token === USDC_TOKEN) { p.payg.error = null; p.payg.rawpaygMinPrice = p.payg.price; p.payg.rawpaygToken = p.payg.token; @@ -239,7 +239,7 @@ export class PriceService { continue; } - if (p.dominantPrice?.token === USDC_TOKEN) { + if (convert && p.dominantPrice?.token === USDC_TOKEN) { p.payg.error = null; p.dominantPrice.rawToken = p.dominantPrice.token; p.dominantPrice.rawPrice = p.dominantPrice.price; @@ -251,9 +251,11 @@ export class PriceService { .toString(); } - const minPrice = BigNumber.from(p.payg.price || 0); - const dominant = BigNumber.from(p.dominantPrice.price).mul(p.payg.priceRatio).div(100); - p.payg.price = minPrice.gt(dominant) ? minPrice.toString() : dominant.toString(); + if (convert) { + const minPrice = BigNumber.from(p.payg.price || 0); + const dominant = BigNumber.from(p.dominantPrice.price).mul(p.payg.priceRatio).div(100); + p.payg.price = minPrice.gt(dominant) ? minPrice.toString() : dominant.toString(); + } } } } diff --git a/apps/indexer-coordinator/src/project/project.service.ts b/apps/indexer-coordinator/src/project/project.service.ts index 98a16d63b..44db262e6 100644 --- a/apps/indexer-coordinator/src/project/project.service.ts +++ b/apps/indexer-coordinator/src/project/project.service.ts @@ -163,7 +163,7 @@ export class ProjectService { const projects = (await this.projectRepo.find()) as Project[]; if (!['monitor', 'metadata'].includes(source)) { - await this.priceService.fillPaygAndDominatePrice(projects); + await this.priceService.fillPaygAndDominatePrice(projects, true); } return projects.sort((a, b) => { @@ -180,6 +180,7 @@ export class ProjectService { async getAlivePaygs(remoteExchangeRate?: string): Promise { // return this.paygRepo.find({ where: { price: Not('') } }); // FIXME remove this + console.log('-------getAlivePaygs----',remoteExchangeRate); const paygs = await this.paygRepo.find({ where: { price: Not('') } }); await this.priceService.inlinePayg(paygs, remoteExchangeRate); for (const payg of paygs) { From 4a2074fefbc77a58c3a6bdb5e66eb759fd4ae511 Mon Sep 17 00:00:00 2001 From: luke <123917244@qq.com> Date: Fri, 11 Apr 2025 17:51:23 +0800 Subject: [PATCH 05/11] build: v1.11.0-1 --- apps/indexer-coordinator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/indexer-coordinator/package.json b/apps/indexer-coordinator/package.json index a559fd634..9af2786b4 100644 --- a/apps/indexer-coordinator/package.json +++ b/apps/indexer-coordinator/package.json @@ -1,6 +1,6 @@ { "name": "@subql/indexer-coordinator", - "version": "2.10.0", + "version": "2.11.0-1", "description": "", "author": "SubQuery", "license": "Apache-2.0", From 7a5aefa9bc78aba3c7d2437453388116d53c3c77 Mon Sep 17 00:00:00 2001 From: luke <123917244@qq.com> Date: Tue, 22 Apr 2025 10:32:10 +0800 Subject: [PATCH 06/11] style: remove console --- apps/indexer-coordinator/src/project/project.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/indexer-coordinator/src/project/project.service.ts b/apps/indexer-coordinator/src/project/project.service.ts index 44db262e6..039c3d6da 100644 --- a/apps/indexer-coordinator/src/project/project.service.ts +++ b/apps/indexer-coordinator/src/project/project.service.ts @@ -180,7 +180,6 @@ export class ProjectService { async getAlivePaygs(remoteExchangeRate?: string): Promise { // return this.paygRepo.find({ where: { price: Not('') } }); // FIXME remove this - console.log('-------getAlivePaygs----',remoteExchangeRate); const paygs = await this.paygRepo.find({ where: { price: Not('') } }); await this.priceService.inlinePayg(paygs, remoteExchangeRate); for (const payg of paygs) { From 01805c2c3b080894576127587012456ce6fee906 Mon Sep 17 00:00:00 2001 From: luke <123917244@qq.com> Date: Tue, 22 Apr 2025 10:59:00 +0800 Subject: [PATCH 07/11] feat: add debug --- apps/indexer-coordinator/src/project/project.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/indexer-coordinator/src/project/project.service.ts b/apps/indexer-coordinator/src/project/project.service.ts index 039c3d6da..0597f2000 100644 --- a/apps/indexer-coordinator/src/project/project.service.ts +++ b/apps/indexer-coordinator/src/project/project.service.ts @@ -180,6 +180,7 @@ export class ProjectService { async getAlivePaygs(remoteExchangeRate?: string): Promise { // return this.paygRepo.find({ where: { price: Not('') } }); // FIXME remove this + getLogger('project').debug(`remoteExchangeRate: ${remoteExchangeRate}`); const paygs = await this.paygRepo.find({ where: { price: Not('') } }); await this.priceService.inlinePayg(paygs, remoteExchangeRate); for (const payg of paygs) { From fe0917d3fd8714d30685f9d6986f54e4b8602a52 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Tue, 22 Apr 2025 11:01:35 +0800 Subject: [PATCH 08/11] chore: update iamge --- apps/indexer-admin/public/images/sqt.png | Bin 0 -> 11511 bytes .../src/pages/global-config/GlobalConfig.tsx | 2 +- .../src/pages/project-details/payg/paygCard.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 apps/indexer-admin/public/images/sqt.png diff --git a/apps/indexer-admin/public/images/sqt.png b/apps/indexer-admin/public/images/sqt.png new file mode 100644 index 0000000000000000000000000000000000000000..a360dd8ead0bd783d7c782ec4e07b5c9397d2709 GIT binary patch literal 11511 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 literal 0 HcmV?d00001 diff --git a/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx b/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx index 8190632bc..cfc23645c 100644 --- a/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx +++ b/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx @@ -312,7 +312,7 @@ const GlobalConfig: FC = () => {
{TOKEN_SYMBOL} diff --git a/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx b/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx index 43d95ad1f..0fa72bf0f 100644 --- a/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx +++ b/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx @@ -452,7 +452,7 @@ export function PaygCard({ id }: TProjectPAYG) {
{TOKEN_SYMBOL} From 6209833a00ed413e842f4fb4c575a94bad1c9d5d Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Tue, 22 Apr 2025 11:04:17 +0800 Subject: [PATCH 09/11] chore: border radius --- .../src/pages/global-config/GlobalConfig.tsx | 8 +++++++- .../src/pages/project-details/payg/paygCard.tsx | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx b/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx index cfc23645c..752b6bd1f 100644 --- a/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx +++ b/apps/indexer-admin/src/pages/global-config/GlobalConfig.tsx @@ -311,7 +311,13 @@ const GlobalConfig: FC = () => { label: (
diff --git a/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx b/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx index 0fa72bf0f..971706c6a 100644 --- a/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx +++ b/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx @@ -451,7 +451,13 @@ export function PaygCard({ id }: TProjectPAYG) { label: (
From 1cdd89b9ce90df2a1ac704d0576cdedf31382134 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Tue, 22 Apr 2025 17:37:48 +0800 Subject: [PATCH 10/11] feat: default --- .../src/pages/project-details/payg/paygCard.tsx | 10 +++++----- .../src/pages/projects/components/projectItem.tsx | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx b/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx index 971706c6a..874f02cfc 100644 --- a/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx +++ b/apps/indexer-admin/src/pages/project-details/payg/paygCard.tsx @@ -72,7 +72,7 @@ export function PaygCard({ id }: TProjectPAYG) { }, [paygConfig, sdk, showModal]); const dominantPriceSQT = useMemo(() => { - if (dominantPrice?.token === sdk?.sqToken.address) { + if (dominantPrice?.token === sdk?.sqToken.address || !dominantPrice?.token) { return BigNumberJs( formatEther(BigNumber.from(dominantPrice?.price || 0).mul(1000)) ).toString(); @@ -89,7 +89,7 @@ export function PaygCard({ id }: TProjectPAYG) { }, [dominantPrice, transPrice, sdk]); const minPriceSQT = useMemo(() => { - if (paygConfig.token === sdk?.sqToken.address) { + if (paygConfig.token === sdk?.sqToken.address || !paygConfig.token) { return paygConfig.paygMinPrice; } return transPrice(STABLE_COIN_ADDRESS, BigNumberJs(paygConfig.paygMinPrice).toString()) @@ -131,7 +131,7 @@ export function PaygCard({ id }: TProjectPAYG) {
Current dominant price: - {dominantPrice?.token === sdk?.sqToken.address ? ( + {dominantPrice?.token === sdk?.sqToken.address || !dominantPrice?.token ? ( <> {formatEther(BigNumber.from(dominantPrice?.price || 0).mul(1000))}{' '} {TOKEN_SYMBOLS[SUPPORTED_NETWORK]} / 1000 reqeusts @@ -172,7 +172,7 @@ export function PaygCard({ id }: TProjectPAYG) { Price ratio: {paygConf.priceRatio}% ( - {dominantPrice?.token === sdk?.sqToken.address ? ( + {dominantPrice?.token === sdk?.sqToken.address || !dominantPrice?.token ? ( {BigNumberJs( formatEther( @@ -226,7 +226,7 @@ export function PaygCard({ id }: TProjectPAYG) { style={{ display: 'flex', alignItems: 'center', gap: 4 }} > Minimum pricing:{' '} - {paygConfig.token === sdk?.sqToken.address ? ( + {paygConfig.token === sdk?.sqToken.address || !paygConfig.token ? ( {paygConfig.paygMinPrice} {TOKEN_SYMBOLS[SUPPORTED_NETWORK]} / 1000 reqeusts diff --git a/apps/indexer-admin/src/pages/projects/components/projectItem.tsx b/apps/indexer-admin/src/pages/projects/components/projectItem.tsx index 244bea283..2c939e22a 100644 --- a/apps/indexer-admin/src/pages/projects/components/projectItem.tsx +++ b/apps/indexer-admin/src/pages/projects/components/projectItem.tsx @@ -128,14 +128,14 @@ const ProjectItem: FC = (props) => { {BigNumberJs( formatUnits( payg?.price, - payg.token === sdk?.sqToken.address + payg.token === sdk?.sqToken.address || !payg.token ? SQT_DECIMAL : STABLE_COIN_DECIMAL[SUPPORTED_NETWORK] ) ) .multipliedBy(1000) .toFixed()}{' '} - {payg.token === sdk?.sqToken.address + {payg.token === sdk?.sqToken.address || !payg.token ? TOKEN_SYMBOL : STABLE_COIN_SYMBOLS[SUPPORTED_NETWORK]}{' '} / 1000 requests From 81351f7a3c3a244bb32925d1d9f94e171333786c Mon Sep 17 00:00:00 2001 From: luke <123917244@qq.com> Date: Tue, 22 Apr 2025 18:23:58 +0800 Subject: [PATCH 11/11] fix: project list price --- apps/indexer-coordinator/src/project/price.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/indexer-coordinator/src/project/price.service.ts b/apps/indexer-coordinator/src/project/price.service.ts index ca0733580..6782e4606 100644 --- a/apps/indexer-coordinator/src/project/price.service.ts +++ b/apps/indexer-coordinator/src/project/price.service.ts @@ -221,10 +221,10 @@ export class PriceService { } } - if (!exchangeRate) return; - for (const p of projects) { if (convert && p.payg.token === USDC_TOKEN) { + if (!exchangeRate) continue; + p.payg.error = null; p.payg.rawpaygMinPrice = p.payg.price; p.payg.rawpaygToken = p.payg.token; @@ -240,6 +240,8 @@ export class PriceService { } if (convert && p.dominantPrice?.token === USDC_TOKEN) { + if (!exchangeRate) continue; + p.payg.error = null; p.dominantPrice.rawToken = p.dominantPrice.token; p.dominantPrice.rawPrice = p.dominantPrice.price;