From a60070d9ad4a6f034c4e72476e0dae34f46d68f7 Mon Sep 17 00:00:00 2001 From: Patrick <42653152+lapatric@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:30:22 +0200 Subject: [PATCH 1/7] [NO-TASK] fix apollo errors (#44) * [NO-TASK] improve error handling * add yarn.lock * [NO-TASK] improvements * [NO-TASK] improvements 2 --------- Co-authored-by: bernd2022 --- api.apollo.config.ts | 56 ++++++++++++++++++++ api.config.ts | 7 --- api.service.ts | 64 +++++++++++++---------- bridge/bridge.service.ts | 2 +- challenges/challenges.service.ts | 3 +- ecosystem/ecosystem.deps.service.ts | 3 +- ecosystem/ecosystem.minter.service.ts | 2 +- ecosystem/ecosystem.stablecoin.service.ts | 3 +- frontendcode/frontendcode.service.ts | 2 +- package.json | 1 + positions/positions.service.ts | 3 +- savings/savings.core.service.ts | 3 +- savings/savings.leadrate.service.ts | 2 +- trades/trade.service.ts | 2 +- yarn.lock | 9 +++- 15 files changed, 117 insertions(+), 45 deletions(-) create mode 100644 api.apollo.config.ts diff --git a/api.apollo.config.ts b/api.apollo.config.ts new file mode 100644 index 0000000..a73e0e9 --- /dev/null +++ b/api.apollo.config.ts @@ -0,0 +1,56 @@ +import { ApolloClient, ApolloLink, createHttpLink, InMemoryCache } from '@apollo/client/core'; +import { onError } from '@apollo/client/link/error'; +import { RetryLink } from '@apollo/client/link/retry'; +import fetch from 'cross-fetch'; +import { CONFIG } from './api.config'; + +// Simply error logging +const errorLink = onError(({ graphQLErrors, networkError, operation }) => { + if (graphQLErrors) { + graphQLErrors.forEach(() => { + console.error(`[GraphQL error in operation: ${operation?.operationName || 'unknown'}]`); + }); + } + if (networkError) { + console.error(`[Network error in operation: ${operation?.operationName || 'unknown'}]`); + } +}); + +const retryLink = new RetryLink({ + delay: { + initial: 1000, + max: 5000, + jitter: true, + }, + attempts: { + max: 3, + retryIf: (error) => { + // Retry on 5xx errors and network failures + return !!error && (error.statusCode >= 500 || error.message?.includes('fetch failed')); + }, + }, +}); + +const httpLink = createHttpLink({ + uri: CONFIG.indexer, + fetch: (uri: RequestInfo | URL, options?: RequestInit) => { + const controller = new AbortController(); + const timeout = setTimeout(() => { + controller.abort(); + }, 60000); // 60 second timeout + + return fetch(uri, { + ...options, + signal: controller.signal, + }).finally(() => { + clearTimeout(timeout); + }); + }, +}); + +const link = ApolloLink.from([errorLink, retryLink, httpLink]); + +export const PONDER_CLIENT = new ApolloClient({ + link, + cache: new InMemoryCache(), +}); diff --git a/api.config.ts b/api.config.ts index 5ffc3df..4a03f6f 100644 --- a/api.config.ts +++ b/api.config.ts @@ -1,4 +1,3 @@ -import { ApolloClient, InMemoryCache } from '@apollo/client/core'; import { Chain, createPublicClient, http } from 'viem'; import { mainnet, polygon } from 'viem/chains'; @@ -67,12 +66,6 @@ console.log(CONFIG); // Refer to https://github.com/yagop/node-telegram-bot-api/blob/master/doc/usage.md#sending-files process.env.NTBA_FIX_350 = 'true'; -// PONDER CLIENT REQUEST -export const PONDER_CLIENT = new ApolloClient({ - uri: CONFIG.indexer, - cache: new InMemoryCache(), -}); - // VIEM CONFIG export const VIEM_CHAIN = CONFIG.chain; export const VIEM_CONFIG = createPublicClient({ diff --git a/api.service.ts b/api.service.ts index ebc68a3..039cd19 100644 --- a/api.service.ts +++ b/api.service.ts @@ -43,43 +43,53 @@ export class ApiService { async updateWorkflow() { this.logger.log(`Fetched blockheight: ${this.fetchedBlockheight}`); const promises = [ - this.minter.updateMinters(), - this.positions.updatePositonV2s(), - this.positions.updateMintingUpdateV2s(), - this.prices.updatePrices(), - this.stablecoin.updateEcosystemKeyValues(), - this.stablecoin.updateEcosystemMintBurnMapping(), - this.deps.updateDepsInfo(), - this.leadrate.updateLeadrateRates(), - this.leadrate.updateLeadrateProposals(), - this.challenges.updateChallengeV2s(), - this.challenges.updateBidV2s(), - this.challenges.updateChallengesPrices(), - this.savings.updateSavingsUserLeaderboard(), + this.minter.updateMinters().catch((err) => this.logger.error('Failed to update minters:', err)), + this.positions.updatePositonV2s().catch((err) => this.logger.error('Failed to update positions:', err)), + this.positions.updateMintingUpdateV2s().catch((err) => this.logger.error('Failed to update minting updates:', err)), + this.prices.updatePrices().catch((err) => this.logger.error('Failed to update prices:', err)), + this.stablecoin.updateEcosystemKeyValues().catch((err) => this.logger.error('Failed to update ecosystem key values:', err)), + this.stablecoin.updateEcosystemMintBurnMapping().catch((err) => this.logger.error('Failed to update mint burn mapping:', err)), + this.deps.updateDepsInfo().catch((err) => this.logger.error('Failed to update deps info:', err)), + this.leadrate.updateLeadrateRates().catch((err) => this.logger.error('Failed to update leadrate rates:', err)), + this.leadrate.updateLeadrateProposals().catch((err) => this.logger.error('Failed to update leadrate proposals:', err)), + this.challenges.updateChallengeV2s().catch((err) => this.logger.error('Failed to update challenges:', err)), + this.challenges.updateBidV2s().catch((err) => this.logger.error('Failed to update bids:', err)), + this.challenges.updateChallengesPrices().catch((err) => this.logger.error('Failed to update challenge prices:', err)), + this.savings.updateSavingsUserLeaderboard().catch((err) => this.logger.error('Failed to update savings leaderboard:', err)), ]; return Promise.all(promises); } async updateSocialMedia() { - this.socialMediaService.update(); + this.socialMediaService.update().catch((err) => this.logger.error('Failed to update social media:', err)); } @Interval(POLLING_DELAY[CONFIG.chain.id]) async updateBlockheight() { - const tmp: number = parseInt((await VIEM_CONFIG.getBlockNumber()).toString()); - this.indexingTimeoutCount += 1; - if (tmp > this.fetchedBlockheight && !this.indexing) { - this.indexing = true; - await this.updateWorkflow(); - await this.updateSocialMedia(); - this.indexingTimeoutCount = 0; - this.fetchedBlockheight = tmp; - this.indexing = false; - } - if (this.indexingTimeoutCount >= INDEXING_TIMEOUT_COUNT && this.indexing) { - this.indexingTimeoutCount = 0; - this.indexing = false; + try { + const tmp: number = parseInt((await VIEM_CONFIG.getBlockNumber()).toString()); + this.indexingTimeoutCount += 1; + if (tmp > this.fetchedBlockheight && !this.indexing) { + this.indexing = true; + try { + await this.updateWorkflow(); + await this.updateSocialMedia(); + this.indexingTimeoutCount = 0; + this.fetchedBlockheight = tmp; + } catch (error) { + this.logger.error('Error in updateWorkflow:', error); + } finally { + this.indexing = false; + } + } + if (this.indexingTimeoutCount >= INDEXING_TIMEOUT_COUNT && this.indexing) { + this.logger.warn(`Indexing timeout reached after ${INDEXING_TIMEOUT_COUNT} attempts`); + this.indexingTimeoutCount = 0; + this.indexing = false; + } + } catch (error) { + this.logger.error('Error getting block number:', error); } } } diff --git a/bridge/bridge.service.ts b/bridge/bridge.service.ts index 633a4b7..a18453e 100644 --- a/bridge/bridge.service.ts +++ b/bridge/bridge.service.ts @@ -1,6 +1,6 @@ import { gql } from '@apollo/client/core'; import { Injectable } from '@nestjs/common'; -import { PONDER_CLIENT } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { StablecoinBridgeQuery } from './bridge.types'; @Injectable() diff --git a/challenges/challenges.service.ts b/challenges/challenges.service.ts index 5c4e6ca..dfbd72a 100644 --- a/challenges/challenges.service.ts +++ b/challenges/challenges.service.ts @@ -1,6 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { gql } from '@apollo/client/core'; -import { PONDER_CLIENT, VIEM_CONFIG } from 'api.config'; +import { VIEM_CONFIG } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { ApiBidsBidders, ApiBidsChallenges, diff --git a/ecosystem/ecosystem.deps.service.ts b/ecosystem/ecosystem.deps.service.ts index 26cc869..65dd3e5 100644 --- a/ecosystem/ecosystem.deps.service.ts +++ b/ecosystem/ecosystem.deps.service.ts @@ -1,7 +1,8 @@ import { gql } from '@apollo/client/core'; import { ADDRESS, DecentralizedEUROABI, EquityABI } from '@deuro/eurocoin'; import { Injectable, Logger } from '@nestjs/common'; -import { PONDER_CLIENT, VIEM_CONFIG } from 'api.config'; +import { VIEM_CONFIG } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { PositionsService } from 'positions/positions.service'; import { formatUnits } from 'viem'; import { ApiEcosystemDepsInfo } from './ecosystem.deps.types'; diff --git a/ecosystem/ecosystem.minter.service.ts b/ecosystem/ecosystem.minter.service.ts index 3fd42c8..000c6f9 100644 --- a/ecosystem/ecosystem.minter.service.ts +++ b/ecosystem/ecosystem.minter.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { PONDER_CLIENT } from '../api.config'; +import { PONDER_CLIENT } from '../api.apollo.config'; import { gql } from '@apollo/client/core'; import { ApiMinterListing, ApiMinterMapping, MinterQuery, MinterQueryObjectArray } from './ecosystem.minter.types'; import { Address } from 'viem'; diff --git a/ecosystem/ecosystem.stablecoin.service.ts b/ecosystem/ecosystem.stablecoin.service.ts index 8771644..b920604 100644 --- a/ecosystem/ecosystem.stablecoin.service.ts +++ b/ecosystem/ecosystem.stablecoin.service.ts @@ -1,7 +1,8 @@ import { gql } from '@apollo/client/core'; import { ADDRESS } from '@deuro/eurocoin'; import { Injectable, Logger } from '@nestjs/common'; -import { CONFIG, PONDER_CLIENT } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; +import { CONFIG } from 'api.config'; import { PricesService } from 'prices/prices.service'; import { Address } from 'viem'; import { EcosystemCollateralService } from './ecosystem.collateral.service'; diff --git a/frontendcode/frontendcode.service.ts b/frontendcode/frontendcode.service.ts index ec13f95..d05b5c3 100644 --- a/frontendcode/frontendcode.service.ts +++ b/frontendcode/frontendcode.service.ts @@ -1,6 +1,6 @@ import { gql } from '@apollo/client/core'; import { Injectable } from '@nestjs/common'; -import { PONDER_CLIENT } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { FrontendCodeRegisteredQuery, FrontendCodeSavingsQuery } from './frontendcode.types'; @Injectable() diff --git a/package.json b/package.json index be996cc..0314312 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@types/node-telegram-bot-api": "^0.64.7", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", + "cross-fetch": "^4.1.0", "dotenv": "^16.3.1", "graphql": "^16.8.2", "node-telegram-bot-api": "^0.66.0", diff --git a/positions/positions.service.ts b/positions/positions.service.ts index e71fd2e..aa05349 100644 --- a/positions/positions.service.ts +++ b/positions/positions.service.ts @@ -3,7 +3,8 @@ import { ADDRESS, PositionV2ABI, SavingsABI } from '@deuro/eurocoin'; import { Injectable, Logger } from '@nestjs/common'; import { FIVEDAYS_MS } from 'utils/const-helper'; import { Address, erc20Abi, getAddress } from 'viem'; -import { CONFIG, PONDER_CLIENT, VIEM_CONFIG } from '../api.config'; +import { CONFIG, VIEM_CONFIG } from '../api.config'; +import { PONDER_CLIENT } from '../api.apollo.config'; import { ApiMintingUpdateListing, ApiMintingUpdateMapping, diff --git a/savings/savings.core.service.ts b/savings/savings.core.service.ts index 895aeb6..9f39271 100644 --- a/savings/savings.core.service.ts +++ b/savings/savings.core.service.ts @@ -1,7 +1,8 @@ import { gql } from '@apollo/client/core'; import { ADDRESS, SavingsGatewayABI } from '@deuro/eurocoin'; import { Injectable, Logger } from '@nestjs/common'; -import { PONDER_CLIENT, VIEM_CONFIG } from 'api.config'; +import { VIEM_CONFIG } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { EcosystemStablecoinService } from 'ecosystem/ecosystem.stablecoin.service'; import { Address, formatUnits, zeroAddress } from 'viem'; import { ApiSavingsInfo, ApiSavingsUserLeaderboard, ApiSavingsUserTable } from './savings.core.types'; diff --git a/savings/savings.leadrate.service.ts b/savings/savings.leadrate.service.ts index 250ecec..5d01e6e 100644 --- a/savings/savings.leadrate.service.ts +++ b/savings/savings.leadrate.service.ts @@ -1,6 +1,6 @@ import { gql } from '@apollo/client/core'; import { Injectable, Logger } from '@nestjs/common'; -import { PONDER_CLIENT } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { ApiLeadrateInfo, ApiLeadrateProposed, diff --git a/trades/trade.service.ts b/trades/trade.service.ts index 08e445f..6dd18a8 100644 --- a/trades/trade.service.ts +++ b/trades/trade.service.ts @@ -1,6 +1,6 @@ import { gql } from '@apollo/client/core'; import { Injectable } from '@nestjs/common'; -import { PONDER_CLIENT } from 'api.config'; +import { PONDER_CLIENT } from 'api.apollo.config'; import { TradeQuery } from './trade.types'; @Injectable() diff --git a/yarn.lock b/yarn.lock index 8a26855..818a5ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2613,6 +2613,13 @@ cron@3.1.7: "@types/luxon" "~3.4.0" luxon "~3.4.0" +cross-fetch@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.1.0.tgz#8f69355007ee182e47fa692ecbaa37a52e43c3d2" + integrity sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw== + dependencies: + node-fetch "^2.7.0" + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -5058,7 +5065,7 @@ node-emoji@1.11.0: dependencies: lodash "^4.17.21" -node-fetch@^2.6.1: +node-fetch@^2.6.1, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== From 1344bbe4fa9381ea88d3b6373ecdbb39b81e989e Mon Sep 17 00:00:00 2001 From: Patrick <42653152+lapatric@users.noreply.github.com> Date: Mon, 7 Jul 2025 11:00:55 +0200 Subject: [PATCH 2/7] [NO-TASK] apollo config improvements (#46) --- api.apollo.config.ts | 22 +++++++++++++++------- bridge/bridge.service.ts | 7 ++++--- challenges/challenges.service.ts | 4 ++-- ecosystem/ecosystem.deps.service.ts | 2 +- ecosystem/ecosystem.minter.service.ts | 2 +- ecosystem/ecosystem.stablecoin.service.ts | 4 ++-- frontendcode/frontendcode.service.ts | 4 ++-- positions/positions.service.ts | 4 ++-- savings/savings.core.service.ts | 6 +++--- savings/savings.leadrate.service.ts | 4 ++-- socialmedia/socialmedia.service.ts | 2 +- trades/trade.service.ts | 4 ++-- 12 files changed, 37 insertions(+), 28 deletions(-) diff --git a/api.apollo.config.ts b/api.apollo.config.ts index a73e0e9..9356387 100644 --- a/api.apollo.config.ts +++ b/api.apollo.config.ts @@ -1,18 +1,25 @@ -import { ApolloClient, ApolloLink, createHttpLink, InMemoryCache } from '@apollo/client/core'; +import { ApolloClient, ApolloError, ApolloLink, createHttpLink, InMemoryCache, ServerError } from '@apollo/client/core'; import { onError } from '@apollo/client/link/error'; import { RetryLink } from '@apollo/client/link/retry'; import fetch from 'cross-fetch'; import { CONFIG } from './api.config'; -// Simply error logging const errorLink = onError(({ graphQLErrors, networkError, operation }) => { if (graphQLErrors) { - graphQLErrors.forEach(() => { - console.error(`[GraphQL error in operation: ${operation?.operationName || 'unknown'}]`); + graphQLErrors.forEach((error) => { + console.error(`[GraphQL error in operation: ${operation?.operationName || 'unknown'}]`, { + message: error.message, + locations: error.locations, + path: error.path, + }); }); } if (networkError) { - console.error(`[Network error in operation: ${operation?.operationName || 'unknown'}]`); + console.error(`[Network error in operation: ${operation?.operationName || 'unknown'}]`, { + message: networkError.message, + name: networkError.name, + stack: networkError.stack, + }); } }); @@ -24,9 +31,10 @@ const retryLink = new RetryLink({ }, attempts: { max: 3, - retryIf: (error) => { + retryIf: (error: ApolloError): boolean => { // Retry on 5xx errors and network failures - return !!error && (error.statusCode >= 500 || error.message?.includes('fetch failed')); + const statusCode = (error.networkError as ServerError)?.statusCode; + return !!error && (statusCode >= 500 || error.message?.includes('fetch failed')); }, }, }); diff --git a/bridge/bridge.service.ts b/bridge/bridge.service.ts index a18453e..062317c 100644 --- a/bridge/bridge.service.ts +++ b/bridge/bridge.service.ts @@ -3,16 +3,17 @@ import { Injectable } from '@nestjs/common'; import { PONDER_CLIENT } from 'api.apollo.config'; import { StablecoinBridgeQuery } from './bridge.types'; +import { StablecoinEnum } from './bridge.enum'; + @Injectable() export class BridgeService { - async getBridgedStables(stablecoinParam: string, timestamp: Date): Promise { - const stablecoin = stablecoinParam.toUpperCase(); + async getBridgedStables(stablecoin: StablecoinEnum, timestamp: Date): Promise { const checkTimestamp = Math.trunc(timestamp.getTime() / 1000); const bridgeFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetBridge${stablecoin} { bridge${stablecoin}s( orderBy: "timestamp", orderDirection: "desc" where: { diff --git a/challenges/challenges.service.ts b/challenges/challenges.service.ts index dfbd72a..5aac60b 100644 --- a/challenges/challenges.service.ts +++ b/challenges/challenges.service.ts @@ -200,7 +200,7 @@ export class ChallengesService { const challenges = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetChallengesV2 { challengeV2s(orderBy: "status", orderDirection: "asc", limit: 1000) { items { id @@ -245,7 +245,7 @@ export class ChallengesService { const bids = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetChallengeBidsV2 { challengeBidV2s(orderBy: "created", orderDirection: "desc", limit: 1000) { items { id diff --git a/ecosystem/ecosystem.deps.service.ts b/ecosystem/ecosystem.deps.service.ts index 65dd3e5..1b668ed 100644 --- a/ecosystem/ecosystem.deps.service.ts +++ b/ecosystem/ecosystem.deps.service.ts @@ -53,7 +53,7 @@ export class EcosystemDepsService { const profitLossPonder = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetDEPS { dEPSs(orderBy: "id", limit: 1000) { items { id diff --git a/ecosystem/ecosystem.minter.service.ts b/ecosystem/ecosystem.minter.service.ts index 000c6f9..2291b4a 100644 --- a/ecosystem/ecosystem.minter.service.ts +++ b/ecosystem/ecosystem.minter.service.ts @@ -31,7 +31,7 @@ export class EcosystemMinterService { const { data } = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetMinters { minters(orderBy: "id", limit: 1000) { items { id diff --git a/ecosystem/ecosystem.stablecoin.service.ts b/ecosystem/ecosystem.stablecoin.service.ts index b920604..89acb74 100644 --- a/ecosystem/ecosystem.stablecoin.service.ts +++ b/ecosystem/ecosystem.stablecoin.service.ts @@ -73,7 +73,7 @@ export class EcosystemStablecoinService { const ecosystem = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetEcosystems { ecosystems(orderBy: "id", limit: 1000) { items { id @@ -130,7 +130,7 @@ export class EcosystemStablecoinService { const response = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetMintBurnAddressMappers { mintBurnAddressMappers(orderBy: "id", limit: 1000) { items { id diff --git a/frontendcode/frontendcode.service.ts b/frontendcode/frontendcode.service.ts index d05b5c3..2ab9fcf 100644 --- a/frontendcode/frontendcode.service.ts +++ b/frontendcode/frontendcode.service.ts @@ -11,7 +11,7 @@ export class FrontendCodeService { const frontendCodeFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetFrontendCodeRegistered { frontendCodeRegistereds( orderBy: "created", orderDirection: "desc" where: { created_gt: "${checkTimestamp}" } @@ -35,7 +35,7 @@ export class FrontendCodeService { const savedFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetFrontendCodeSavingsSaved { savingsSaveds( orderBy: "created", orderDirection: "desc" where: { created_gt: "${checkTimestamp}" } diff --git a/positions/positions.service.ts b/positions/positions.service.ts index aa05349..d3e7c76 100644 --- a/positions/positions.service.ts +++ b/positions/positions.service.ts @@ -80,7 +80,7 @@ export class PositionsService { const { data } = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetPositionsV2 { positionV2s(orderBy: "availableForClones", orderDirection: "desc", limit: 1000) { items { position @@ -271,7 +271,7 @@ export class PositionsService { const { data } = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetMintingUpdatesV2 { mintingUpdateV2s(orderBy: "created", orderDirection: "desc", limit: 1000) { items { id diff --git a/savings/savings.core.service.ts b/savings/savings.core.service.ts index 9f39271..0477588 100644 --- a/savings/savings.core.service.ts +++ b/savings/savings.core.service.ts @@ -89,7 +89,7 @@ export class SavingsCoreService { const savedFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetSavingsSaved { savingsSaveds( orderBy: "blockheight" orderDirection: "desc" @@ -115,7 +115,7 @@ export class SavingsCoreService { const withdrawnFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetSavingsWithdrawn { savingsWithdrawns( orderBy: "blockheight" orderDirection: "desc" @@ -141,7 +141,7 @@ export class SavingsCoreService { const interestFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetSavingsInterest { savingsInterests( orderBy: "blockheight" orderDirection: "desc" diff --git a/savings/savings.leadrate.service.ts b/savings/savings.leadrate.service.ts index 5d01e6e..949060c 100644 --- a/savings/savings.leadrate.service.ts +++ b/savings/savings.leadrate.service.ts @@ -64,7 +64,7 @@ export class SavingsLeadrateService { const { data } = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetSavingsRateChanged { savingsRateChangeds(orderBy: "blockheight", orderDirection: "desc") { items { id @@ -107,7 +107,7 @@ export class SavingsLeadrateService { const { data } = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetSavingsRateProposed { savingsRateProposeds(orderBy: "blockheight", orderDirection: "desc") { items { id diff --git a/socialmedia/socialmedia.service.ts b/socialmedia/socialmedia.service.ts index 660b26a..34a0fb6 100644 --- a/socialmedia/socialmedia.service.ts +++ b/socialmedia/socialmedia.service.ts @@ -134,7 +134,7 @@ export class SocialMediaService { try { const checkDate = new Date(this.socialMediaState.bridgeUpdates); - for (const stablecoin in StablecoinEnum) { + for (const stablecoin of Object.values(StablecoinEnum)) { const requestedBridged = await this.bridges.getBridgedStables(stablecoin, checkDate); if (requestedBridged.length > 0) { diff --git a/trades/trade.service.ts b/trades/trade.service.ts index 6dd18a8..d1cb11b 100644 --- a/trades/trade.service.ts +++ b/trades/trade.service.ts @@ -11,7 +11,7 @@ export class TradesService { const tradeFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetTrades { trades( orderBy: "time", orderDirection: "desc" where: { @@ -38,7 +38,7 @@ export class TradesService { const tradeFetched = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` - query { + query GetTraderShares { trades( where: { trader: "${trader}" From 0710047dc77e9a56562ed1077a78bf5914cb6662 Mon Sep 17 00:00:00 2001 From: lapatric <42653152+lapatric@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:08:20 +0200 Subject: [PATCH 3/7] [NO-TASK] batch updateWorkflow --- api.service.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/api.service.ts b/api.service.ts index 039cd19..d19ca83 100644 --- a/api.service.ts +++ b/api.service.ts @@ -42,23 +42,34 @@ export class ApiService { async updateWorkflow() { this.logger.log(`Fetched blockheight: ${this.fetchedBlockheight}`); - const promises = [ + + const batch1 = [ this.minter.updateMinters().catch((err) => this.logger.error('Failed to update minters:', err)), this.positions.updatePositonV2s().catch((err) => this.logger.error('Failed to update positions:', err)), this.positions.updateMintingUpdateV2s().catch((err) => this.logger.error('Failed to update minting updates:', err)), this.prices.updatePrices().catch((err) => this.logger.error('Failed to update prices:', err)), + ]; + + const batch2 = [ this.stablecoin.updateEcosystemKeyValues().catch((err) => this.logger.error('Failed to update ecosystem key values:', err)), this.stablecoin.updateEcosystemMintBurnMapping().catch((err) => this.logger.error('Failed to update mint burn mapping:', err)), this.deps.updateDepsInfo().catch((err) => this.logger.error('Failed to update deps info:', err)), this.leadrate.updateLeadrateRates().catch((err) => this.logger.error('Failed to update leadrate rates:', err)), this.leadrate.updateLeadrateProposals().catch((err) => this.logger.error('Failed to update leadrate proposals:', err)), + ]; + + const batch3 = [ this.challenges.updateChallengeV2s().catch((err) => this.logger.error('Failed to update challenges:', err)), this.challenges.updateBidV2s().catch((err) => this.logger.error('Failed to update bids:', err)), this.challenges.updateChallengesPrices().catch((err) => this.logger.error('Failed to update challenge prices:', err)), this.savings.updateSavingsUserLeaderboard().catch((err) => this.logger.error('Failed to update savings leaderboard:', err)), ]; - return Promise.all(promises); + await Promise.all(batch1); + await new Promise((resolve) => setTimeout(resolve, 50)); + await Promise.all(batch2); + await new Promise((resolve) => setTimeout(resolve, 50)); + await Promise.all(batch3); } async updateSocialMedia() { From 85cfcc4a88a1c108315a34b5f953bb0bd237b393 Mon Sep 17 00:00:00 2001 From: lapatric <42653152+lapatric@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:44:13 +0200 Subject: [PATCH 4/7] [NO-TASK] reduce batch size --- api.service.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/api.service.ts b/api.service.ts index d19ca83..684996b 100644 --- a/api.service.ts +++ b/api.service.ts @@ -47,20 +47,26 @@ export class ApiService { this.minter.updateMinters().catch((err) => this.logger.error('Failed to update minters:', err)), this.positions.updatePositonV2s().catch((err) => this.logger.error('Failed to update positions:', err)), this.positions.updateMintingUpdateV2s().catch((err) => this.logger.error('Failed to update minting updates:', err)), - this.prices.updatePrices().catch((err) => this.logger.error('Failed to update prices:', err)), ]; const batch2 = [ + this.prices.updatePrices().catch((err) => this.logger.error('Failed to update prices:', err)), this.stablecoin.updateEcosystemKeyValues().catch((err) => this.logger.error('Failed to update ecosystem key values:', err)), this.stablecoin.updateEcosystemMintBurnMapping().catch((err) => this.logger.error('Failed to update mint burn mapping:', err)), + ]; + + const batch3 = [ this.deps.updateDepsInfo().catch((err) => this.logger.error('Failed to update deps info:', err)), this.leadrate.updateLeadrateRates().catch((err) => this.logger.error('Failed to update leadrate rates:', err)), this.leadrate.updateLeadrateProposals().catch((err) => this.logger.error('Failed to update leadrate proposals:', err)), ]; - const batch3 = [ + const batch4 = [ this.challenges.updateChallengeV2s().catch((err) => this.logger.error('Failed to update challenges:', err)), this.challenges.updateBidV2s().catch((err) => this.logger.error('Failed to update bids:', err)), + ]; + + const batch5 = [ this.challenges.updateChallengesPrices().catch((err) => this.logger.error('Failed to update challenge prices:', err)), this.savings.updateSavingsUserLeaderboard().catch((err) => this.logger.error('Failed to update savings leaderboard:', err)), ]; @@ -70,6 +76,10 @@ export class ApiService { await Promise.all(batch2); await new Promise((resolve) => setTimeout(resolve, 50)); await Promise.all(batch3); + await new Promise((resolve) => setTimeout(resolve, 50)); + await Promise.all(batch4); + await new Promise((resolve) => setTimeout(resolve, 50)); + await Promise.all(batch5); } async updateSocialMedia() { From 1ab322ac3b20676036abcde1febf1c6471ca453b Mon Sep 17 00:00:00 2001 From: lapatric <42653152+lapatric@users.noreply.github.com> Date: Mon, 7 Jul 2025 17:20:17 +0200 Subject: [PATCH 5/7] [NO-TASK] remove retry & time operations --- api.apollo.config.ts | 21 ++---------- api.service.ts | 78 +++++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/api.apollo.config.ts b/api.apollo.config.ts index 9356387..f5411ae 100644 --- a/api.apollo.config.ts +++ b/api.apollo.config.ts @@ -1,6 +1,5 @@ -import { ApolloClient, ApolloError, ApolloLink, createHttpLink, InMemoryCache, ServerError } from '@apollo/client/core'; +import { ApolloClient, ApolloLink, createHttpLink, InMemoryCache } from '@apollo/client/core'; import { onError } from '@apollo/client/link/error'; -import { RetryLink } from '@apollo/client/link/retry'; import fetch from 'cross-fetch'; import { CONFIG } from './api.config'; @@ -23,22 +22,6 @@ const errorLink = onError(({ graphQLErrors, networkError, operation }) => { } }); -const retryLink = new RetryLink({ - delay: { - initial: 1000, - max: 5000, - jitter: true, - }, - attempts: { - max: 3, - retryIf: (error: ApolloError): boolean => { - // Retry on 5xx errors and network failures - const statusCode = (error.networkError as ServerError)?.statusCode; - return !!error && (statusCode >= 500 || error.message?.includes('fetch failed')); - }, - }, -}); - const httpLink = createHttpLink({ uri: CONFIG.indexer, fetch: (uri: RequestInfo | URL, options?: RequestInit) => { @@ -56,7 +39,7 @@ const httpLink = createHttpLink({ }, }); -const link = ApolloLink.from([errorLink, retryLink, httpLink]); +const link = ApolloLink.from([errorLink, httpLink]); export const PONDER_CLIENT = new ApolloClient({ link, diff --git a/api.service.ts b/api.service.ts index 684996b..c73bd54 100644 --- a/api.service.ts +++ b/api.service.ts @@ -25,6 +25,7 @@ export class ApiService { private indexing: boolean = false; private indexingTimeoutCount: number = 0; private fetchedBlockheight: number = 0; + private isUpdatingWorkflow: boolean = false; constructor( private readonly minter: EcosystemMinterService, @@ -41,45 +42,56 @@ export class ApiService { } async updateWorkflow() { - this.logger.log(`Fetched blockheight: ${this.fetchedBlockheight}`); + if (this.isUpdatingWorkflow) { + this.logger.warn(`Skipping updateWorkflow - previous update still in progress at block ${this.fetchedBlockheight}`); + return; + } - const batch1 = [ - this.minter.updateMinters().catch((err) => this.logger.error('Failed to update minters:', err)), - this.positions.updatePositonV2s().catch((err) => this.logger.error('Failed to update positions:', err)), - this.positions.updateMintingUpdateV2s().catch((err) => this.logger.error('Failed to update minting updates:', err)), - ]; + this.isUpdatingWorkflow = true; + this.logger.log(`Fetched blockheight: ${this.fetchedBlockheight}`); - const batch2 = [ - this.prices.updatePrices().catch((err) => this.logger.error('Failed to update prices:', err)), - this.stablecoin.updateEcosystemKeyValues().catch((err) => this.logger.error('Failed to update ecosystem key values:', err)), - this.stablecoin.updateEcosystemMintBurnMapping().catch((err) => this.logger.error('Failed to update mint burn mapping:', err)), - ]; + try { + const timeTask = async (name: string, fn: () => Promise) => { + const start = Date.now(); + try { + await fn(); + this.logger.debug(`${name} completed in ${Date.now() - start}ms`); + } catch (err) { + this.logger.error(`Failed to update ${name} after ${Date.now() - start}ms:`, err); + throw err; + } + }; - const batch3 = [ - this.deps.updateDepsInfo().catch((err) => this.logger.error('Failed to update deps info:', err)), - this.leadrate.updateLeadrateRates().catch((err) => this.logger.error('Failed to update leadrate rates:', err)), - this.leadrate.updateLeadrateProposals().catch((err) => this.logger.error('Failed to update leadrate proposals:', err)), - ]; + const batch1 = [ + timeTask('updateMinters', () => this.minter.updateMinters()).catch(() => {}), + timeTask('updatePositonV2s', () => this.positions.updatePositonV2s()).catch(() => {}), + timeTask('updateMintingUpdateV2s', () => this.positions.updateMintingUpdateV2s()).catch(() => {}), + timeTask('updatePrices', () => this.prices.updatePrices()).catch(() => {}), + ]; - const batch4 = [ - this.challenges.updateChallengeV2s().catch((err) => this.logger.error('Failed to update challenges:', err)), - this.challenges.updateBidV2s().catch((err) => this.logger.error('Failed to update bids:', err)), - ]; + const batch2 = [ + timeTask('updateEcosystemKeyValues', () => this.stablecoin.updateEcosystemKeyValues()).catch(() => {}), + timeTask('updateEcosystemMintBurnMapping', () => this.stablecoin.updateEcosystemMintBurnMapping()).catch(() => {}), + timeTask('updateDepsInfo', () => this.deps.updateDepsInfo()).catch(() => {}), + timeTask('updateLeadrateRates', () => this.leadrate.updateLeadrateRates()).catch(() => {}), + timeTask('updateLeadrateProposals', () => this.leadrate.updateLeadrateProposals()).catch(() => {}), + ]; - const batch5 = [ - this.challenges.updateChallengesPrices().catch((err) => this.logger.error('Failed to update challenge prices:', err)), - this.savings.updateSavingsUserLeaderboard().catch((err) => this.logger.error('Failed to update savings leaderboard:', err)), - ]; + const batch3 = [ + timeTask('updateChallenges', () => this.challenges.updateChallengeV2s()).catch(() => {}), + timeTask('updateBids', () => this.challenges.updateBidV2s()).catch(() => {}), + timeTask('updateChallengesPrices', () => this.challenges.updateChallengesPrices()).catch(() => {}), + timeTask('updateSavingsUserLeaderboard', () => this.savings.updateSavingsUserLeaderboard()).catch(() => {}), + ]; - await Promise.all(batch1); - await new Promise((resolve) => setTimeout(resolve, 50)); - await Promise.all(batch2); - await new Promise((resolve) => setTimeout(resolve, 50)); - await Promise.all(batch3); - await new Promise((resolve) => setTimeout(resolve, 50)); - await Promise.all(batch4); - await new Promise((resolve) => setTimeout(resolve, 50)); - await Promise.all(batch5); + await Promise.all(batch1); + await new Promise((resolve) => setTimeout(resolve, 50)); + await Promise.all(batch2); + await new Promise((resolve) => setTimeout(resolve, 50)); + await Promise.all(batch3); + } finally { + this.isUpdatingWorkflow = false; + } } async updateSocialMedia() { From 969d45053aae428e1808fd9a2a4c27ef4c7ec5d1 Mon Sep 17 00:00:00 2001 From: bernd2022 Date: Wed, 9 Jul 2025 08:57:57 +0200 Subject: [PATCH 6/7] [NO-TASK] serialize all update tasks --- api.service.ts | 40 +++++++++++++-------------------- savings/savings.core.service.ts | 4 +++- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/api.service.ts b/api.service.ts index c73bd54..3b718df 100644 --- a/api.service.ts +++ b/api.service.ts @@ -62,33 +62,23 @@ export class ApiService { } }; - const batch1 = [ - timeTask('updateMinters', () => this.minter.updateMinters()).catch(() => {}), - timeTask('updatePositonV2s', () => this.positions.updatePositonV2s()).catch(() => {}), - timeTask('updateMintingUpdateV2s', () => this.positions.updateMintingUpdateV2s()).catch(() => {}), - timeTask('updatePrices', () => this.prices.updatePrices()).catch(() => {}), + const promises = [ + await timeTask('updateMinters', () => this.minter.updateMinters()).catch(() => {}), + await timeTask('updatePositonV2s', () => this.positions.updatePositonV2s()).catch(() => {}), + await timeTask('updateMintingUpdateV2s', () => this.positions.updateMintingUpdateV2s()).catch(() => {}), + await timeTask('updatePrices', () => this.prices.updatePrices()).catch(() => {}), + await timeTask('updateEcosystemKeyValues', () => this.stablecoin.updateEcosystemKeyValues()).catch(() => {}), + await timeTask('updateEcosystemMintBurnMapping', () => this.stablecoin.updateEcosystemMintBurnMapping()).catch(() => {}), + await timeTask('updateDepsInfo', () => this.deps.updateDepsInfo()).catch(() => {}), + await timeTask('updateLeadrateRates', () => this.leadrate.updateLeadrateRates()).catch(() => {}), + await timeTask('updateLeadrateProposals', () => this.leadrate.updateLeadrateProposals()).catch(() => {}), + await timeTask('updateChallengeV2s', () => this.challenges.updateChallengeV2s()).catch(() => {}), + await timeTask('updateBidV2s', () => this.challenges.updateBidV2s()).catch(() => {}), + await timeTask('updateChallengesPrices', () => this.challenges.updateChallengesPrices()).catch(() => {}), + await timeTask('updateSavingsUserLeaderboard', () => this.savings.updateSavingsUserLeaderboard()).catch(() => {}), ]; - const batch2 = [ - timeTask('updateEcosystemKeyValues', () => this.stablecoin.updateEcosystemKeyValues()).catch(() => {}), - timeTask('updateEcosystemMintBurnMapping', () => this.stablecoin.updateEcosystemMintBurnMapping()).catch(() => {}), - timeTask('updateDepsInfo', () => this.deps.updateDepsInfo()).catch(() => {}), - timeTask('updateLeadrateRates', () => this.leadrate.updateLeadrateRates()).catch(() => {}), - timeTask('updateLeadrateProposals', () => this.leadrate.updateLeadrateProposals()).catch(() => {}), - ]; - - const batch3 = [ - timeTask('updateChallenges', () => this.challenges.updateChallengeV2s()).catch(() => {}), - timeTask('updateBids', () => this.challenges.updateBidV2s()).catch(() => {}), - timeTask('updateChallengesPrices', () => this.challenges.updateChallengesPrices()).catch(() => {}), - timeTask('updateSavingsUserLeaderboard', () => this.savings.updateSavingsUserLeaderboard()).catch(() => {}), - ]; - - await Promise.all(batch1); - await new Promise((resolve) => setTimeout(resolve, 50)); - await Promise.all(batch2); - await new Promise((resolve) => setTimeout(resolve, 50)); - await Promise.all(batch3); + await Promise.all(promises); } finally { this.isUpdatingWorkflow = false; } diff --git a/savings/savings.core.service.ts b/savings/savings.core.service.ts index 0477588..05e9b8c 100644 --- a/savings/savings.core.service.ts +++ b/savings/savings.core.service.ts @@ -1,8 +1,8 @@ import { gql } from '@apollo/client/core'; import { ADDRESS, SavingsGatewayABI } from '@deuro/eurocoin'; import { Injectable, Logger } from '@nestjs/common'; -import { VIEM_CONFIG } from 'api.config'; import { PONDER_CLIENT } from 'api.apollo.config'; +import { VIEM_CONFIG } from 'api.config'; import { EcosystemStablecoinService } from 'ecosystem/ecosystem.stablecoin.service'; import { Address, formatUnits, zeroAddress } from 'viem'; import { ApiSavingsInfo, ApiSavingsUserLeaderboard, ApiSavingsUserTable } from './savings.core.types'; @@ -42,6 +42,8 @@ export class SavingsCoreService { } async updateSavingsUserLeaderboard(): Promise { + this.logger.debug('Updating SavingsUserLeaderboard'); + const data = await PONDER_CLIENT.query({ fetchPolicy: 'no-cache', query: gql` From 5240ac64b374fad9204a2e915f45179163c5f5c1 Mon Sep 17 00:00:00 2001 From: bernd2022 Date: Wed, 9 Jul 2025 11:01:09 +0200 Subject: [PATCH 7/7] [NO-TASK] reduce http timeout to 10 seconds --- api.apollo.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.apollo.config.ts b/api.apollo.config.ts index f5411ae..6ed2907 100644 --- a/api.apollo.config.ts +++ b/api.apollo.config.ts @@ -28,7 +28,7 @@ const httpLink = createHttpLink({ const controller = new AbortController(); const timeout = setTimeout(() => { controller.abort(); - }, 60000); // 60 second timeout + }, 10000); // 10 second timeout return fetch(uri, { ...options,