diff --git a/backfill/process/process_events.go b/backfill/process/process_events.go index 4bf8c84b..5363ab60 100644 --- a/backfill/process/process_events.go +++ b/backfill/process/process_events.go @@ -40,6 +40,11 @@ func PrepareEvents(network string, payload fetch.ProcessedPayload, transactionsI } } + // TODO: This will be removed after TransactionDetails migration + // if network == "mainnet01" { + // return events, nil + // } + coinbaseDecoded, err := decodeCoinbase(string(payload.Coinbase)) if err != nil { return nil, fmt.Errorf("decoding Coinbase JSON of block: %w", err) diff --git a/backfill/process/process_transactions.go b/backfill/process/process_transactions.go index 352f8b5d..cf677b6c 100644 --- a/backfill/process/process_transactions.go +++ b/backfill/process/process_transactions.go @@ -138,6 +138,11 @@ func PrepareTransactions(network string, blockId int64, payload fetch.ProcessedP transactionRecords = append(transactionRecords, transactionRecord) } + // TODO: This will be removed after TransactionDetails migration + // if network == "mainnet01" { + // return transactionRecords, nil + // } + coinbaseTx, err := processCoinbaseTransaction(string(payload.Coinbase), blockId, block.CreationTime, int64(block.ChainId)) if err != nil { diff --git a/backfill/process/process_transfers.go b/backfill/process/process_transfers.go index 6cfc55b7..579a4d65 100644 --- a/backfill/process/process_transfers.go +++ b/backfill/process/process_transfers.go @@ -20,6 +20,11 @@ func PrepareTransfers(network string, payload fetch.ProcessedPayload, transactio transfers = append(transfers, nftTransfers...) } + // TODO: This will be removed after TransactionDetails migration + // if network == "mainnet01" { + // return transfers, nil + // } + coinbaseDecoded, err := decodeCoinbase(string(payload.Coinbase)) if err != nil { return nil, fmt.Errorf("decoding Coinbase JSON of block: %w", err) diff --git a/indexer/src/kadena-server/config/graphql-types.ts b/indexer/src/kadena-server/config/graphql-types.ts index 50b862c2..07b4150e 100644 --- a/indexer/src/kadena-server/config/graphql-types.ts +++ b/indexer/src/kadena-server/config/graphql-types.ts @@ -152,13 +152,23 @@ export type ExecutionPayload = { export type FungibleAccount = Node & { __typename?: 'FungibleAccount'; accountName: Scalars['String']['output']; + /** + * Uses "length" multiplier which scales with the number of items in the returned array. + * Each chain account requires a separate database lookup, so complexity = 5 × array_length. + * Example: 3 chain accounts = 5 × 3 = 15 complexity points. + * This pattern is ideal for array fields where complexity scales linearly with result size. + */ chainAccounts: Array; fungibleName: Scalars['String']['output']; id: Scalars['ID']['output']; totalBalance: Scalars['Decimal']['output']; - /** Default page size is 20. */ + /** + * Default page size is 20. + * Uses "first" multiplier to control pagination complexity. + * Transactions are expensive operations requiring joins, so base value is higher. + * Example: requesting 20 transactions = 10 × 20 = 200 complexity points. + */ transactions: FungibleAccountTransactionsConnection; - /** Default page size is 20. */ transfers: FungibleAccountTransfersConnection; }; @@ -468,10 +478,7 @@ export type Query = { fungibleAccount?: Maybe; /** Retrieve an account by public key. */ fungibleAccountsByPublicKey: Array; - /** - * Retrieve an account by its name and fungible, such as coin, on a specific chain. - * @deprecated deprecated, use Query.fungibleChainAccounts - */ + /** Retrieve an account by its name and fungible, such as coin, on a specific chain. */ fungibleChainAccount?: Maybe; /** Retrieve an account by its name and fungible, such as coin, on a specific chain. */ fungibleChainAccounts?: Maybe>; @@ -506,6 +513,7 @@ export type Query = { nonFungibleChainAccount?: Maybe; /** Execute arbitrary Pact code via a local call without gas-estimation or signature-verification (e.g. (+ 1 2) or (coin.get-details )). */ pactQuery: Array; + tokens?: Maybe; /** Retrieve one transaction by its unique key. Throws an error if multiple transactions are found. */ transaction?: Maybe; /** @@ -621,6 +629,13 @@ export type QueryPactQueryArgs = { pactQuery: Array; }; +export type QueryTokensArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +}; + export type QueryTransactionArgs = { blockHash?: InputMaybe; minimumDepth?: InputMaybe; @@ -664,7 +679,7 @@ export type QueryTransfersArgs = { export type QueryBlocksFromDepthConnection = { __typename?: 'QueryBlocksFromDepthConnection'; - edges: Array>; + edges: Array; pageInfo: PageInfo; }; @@ -676,7 +691,7 @@ export type QueryBlocksFromDepthConnectionEdge = { export type QueryBlocksFromHeightConnection = { __typename?: 'QueryBlocksFromHeightConnection'; - edges: Array>; + edges: Array; pageInfo: PageInfo; }; @@ -711,9 +726,21 @@ export type QueryEventsConnectionEdge = { node: Event; }; +export type QueryTokensConnection = { + __typename?: 'QueryTokensConnection'; + edges: Array; + pageInfo: PageInfo; +}; + +export type QueryTokensEdge = { + __typename?: 'QueryTokensEdge'; + cursor: Scalars['String']['output']; + node: Token; +}; + export type QueryTransactionsByPublicKeyConnection = { __typename?: 'QueryTransactionsByPublicKeyConnection'; - edges: Array>; + edges: Array; pageInfo: PageInfo; totalCount: Scalars['Int']['output']; }; @@ -812,6 +839,13 @@ export type SubscriptionTransactionArgs = { requestKey: Scalars['String']['input']; }; +export type Token = { + __typename?: 'Token'; + chainId: Scalars['String']['output']; + id: Scalars['ID']['output']; + name: Scalars['String']['output']; +}; + /** A transaction. */ export type Transaction = Node & { __typename?: 'Transaction'; @@ -1260,7 +1294,7 @@ export type ResolversTypes = { Query: ResolverTypeWrapper<{}>; QueryBlocksFromDepthConnection: ResolverTypeWrapper< Omit & { - edges: Array>; + edges: Array; } >; QueryBlocksFromDepthConnectionEdge: ResolverTypeWrapper< @@ -1268,7 +1302,7 @@ export type ResolversTypes = { >; QueryBlocksFromHeightConnection: ResolverTypeWrapper< Omit & { - edges: Array>; + edges: Array; } >; QueryBlocksFromHeightConnectionEdge: ResolverTypeWrapper< @@ -1290,9 +1324,11 @@ export type ResolversTypes = { QueryEventsConnectionEdge: ResolverTypeWrapper< Omit & { node: ResolversTypes['Event'] } >; + QueryTokensConnection: ResolverTypeWrapper; + QueryTokensEdge: ResolverTypeWrapper; QueryTransactionsByPublicKeyConnection: ResolverTypeWrapper< Omit & { - edges: Array>; + edges: Array; } >; QueryTransactionsByPublicKeyConnectionEdge: ResolverTypeWrapper< @@ -1320,6 +1356,7 @@ export type ResolversTypes = { Signer: ResolverTypeWrapper; String: ResolverTypeWrapper; Subscription: ResolverTypeWrapper<{}>; + Token: ResolverTypeWrapper; Transaction: ResolverTypeWrapper< Omit & { cmd: ResolversTypes['TransactionCommand']; @@ -1495,13 +1532,13 @@ export type ResolversParentTypes = { PageInfo: PageInfo; Query: {}; QueryBlocksFromDepthConnection: Omit & { - edges: Array>; + edges: Array; }; QueryBlocksFromDepthConnectionEdge: Omit & { node: ResolversParentTypes['Block']; }; QueryBlocksFromHeightConnection: Omit & { - edges: Array>; + edges: Array; }; QueryBlocksFromHeightConnectionEdge: Omit & { node: ResolversParentTypes['Block']; @@ -1519,8 +1556,10 @@ export type ResolversParentTypes = { QueryEventsConnectionEdge: Omit & { node: ResolversParentTypes['Event']; }; + QueryTokensConnection: QueryTokensConnection; + QueryTokensEdge: QueryTokensEdge; QueryTransactionsByPublicKeyConnection: Omit & { - edges: Array>; + edges: Array; }; QueryTransactionsByPublicKeyConnectionEdge: Omit< QueryTransactionsByPublicKeyConnectionEdge, @@ -1542,6 +1581,7 @@ export type ResolversParentTypes = { Signer: Signer; String: Scalars['String']['output']; Subscription: {}; + Token: Token; Transaction: Omit & { cmd: ResolversParentTypes['TransactionCommand']; orphanedTransactions?: Maybe>>; @@ -1583,6 +1623,18 @@ export type ResolversParentTypes = { UserGuard: UserGuard; }; +export type ComplexityDirectiveArgs = { + multipliers?: Maybe>; + value: Scalars['Int']['input']; +}; + +export type ComplexityDirectiveResolver< + Result, + Parent, + ContextType = any, + Args = ComplexityDirectiveArgs, +> = DirectiveResolverFn; + export interface BigIntScalarConfig extends GraphQLScalarTypeConfig { name: 'BigInt'; } @@ -2229,6 +2281,12 @@ export type QueryResolvers< ContextType, RequireFields >; + tokens?: Resolver< + Maybe, + ParentType, + ContextType, + Partial + >; transaction?: Resolver< Maybe, ParentType, @@ -2261,7 +2319,7 @@ export type QueryBlocksFromDepthConnectionResolvers< ResolversParentTypes['QueryBlocksFromDepthConnection'] = ResolversParentTypes['QueryBlocksFromDepthConnection'], > = { edges?: Resolver< - Array>, + Array, ParentType, ContextType >; @@ -2285,7 +2343,7 @@ export type QueryBlocksFromHeightConnectionResolvers< ResolversParentTypes['QueryBlocksFromHeightConnection'] = ResolversParentTypes['QueryBlocksFromHeightConnection'], > = { edges?: Resolver< - Array>, + Array, ParentType, ContextType >; @@ -2348,13 +2406,33 @@ export type QueryEventsConnectionEdgeResolvers< __isTypeOf?: IsTypeOfResolverFn; }; +export type QueryTokensConnectionResolvers< + ContextType = any, + ParentType extends + ResolversParentTypes['QueryTokensConnection'] = ResolversParentTypes['QueryTokensConnection'], +> = { + edges?: Resolver, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type QueryTokensEdgeResolvers< + ContextType = any, + ParentType extends + ResolversParentTypes['QueryTokensEdge'] = ResolversParentTypes['QueryTokensEdge'], +> = { + cursor?: Resolver; + node?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type QueryTransactionsByPublicKeyConnectionResolvers< ContextType = any, ParentType extends ResolversParentTypes['QueryTransactionsByPublicKeyConnection'] = ResolversParentTypes['QueryTransactionsByPublicKeyConnection'], > = { edges?: Resolver< - Array>, + Array, ParentType, ContextType >; @@ -2476,6 +2554,16 @@ export type SubscriptionResolvers< >; }; +export type TokenResolvers< + ContextType = any, + ParentType extends ResolversParentTypes['Token'] = ResolversParentTypes['Token'], +> = { + chainId?: Resolver; + id?: Resolver; + name?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type TransactionResolvers< ContextType = any, ParentType extends ResolversParentTypes['Transaction'] = ResolversParentTypes['Transaction'], @@ -2731,6 +2819,8 @@ export type Resolvers = { QueryCompletedBlockHeightsConnectionEdge?: QueryCompletedBlockHeightsConnectionEdgeResolvers; QueryEventsConnection?: QueryEventsConnectionResolvers; QueryEventsConnectionEdge?: QueryEventsConnectionEdgeResolvers; + QueryTokensConnection?: QueryTokensConnectionResolvers; + QueryTokensEdge?: QueryTokensEdgeResolvers; QueryTransactionsByPublicKeyConnection?: QueryTransactionsByPublicKeyConnectionResolvers; QueryTransactionsByPublicKeyConnectionEdge?: QueryTransactionsByPublicKeyConnectionEdgeResolvers; QueryTransactionsConnection?: QueryTransactionsConnectionResolvers; @@ -2740,6 +2830,7 @@ export type Resolvers = { RawGuard?: RawGuardResolvers; Signer?: SignerResolvers; Subscription?: SubscriptionResolvers; + Token?: TokenResolvers; Transaction?: TransactionResolvers; TransactionCapability?: TransactionCapabilityResolvers; TransactionCommand?: TransactionCommandResolvers; @@ -2756,3 +2847,7 @@ export type Resolvers = { Transfer?: TransferResolvers; UserGuard?: UserGuardResolvers; }; + +export type DirectiveResolvers = { + complexity?: ComplexityDirectiveResolver; +}; diff --git a/indexer/src/kadena-server/config/schema.graphql b/indexer/src/kadena-server/config/schema.graphql index 646cc053..92f96c44 100644 --- a/indexer/src/kadena-server/config/schema.graphql +++ b/indexer/src/kadena-server/config/schema.graphql @@ -272,6 +272,9 @@ type Query { last: Int requestKey: String ): QueryTransfersConnection! @complexity(value: 10, multipliers: ["first", "last"]) + + tokens(after: String, before: String, first: Int, last: Int): QueryTokensConnection! + @complexity(value: 10, multipliers: ["first", "last"]) } """ @@ -1033,3 +1036,19 @@ type RawGuard implements IGuard { predicate: String! @deprecated(reason: "deprecated, use KeysetGuard.predicate") raw: String! } + +type QueryTokensConnection { + edges: [QueryTokensEdge!]! + pageInfo: PageInfo! +} + +type QueryTokensEdge { + cursor: String! + node: Token! +} + +type Token { + id: ID! + name: String! + chainId: String! +} diff --git a/indexer/src/kadena-server/repository/application/balance-repository.ts b/indexer/src/kadena-server/repository/application/balance-repository.ts index 09be0f68..59f55a9c 100644 --- a/indexer/src/kadena-server/repository/application/balance-repository.ts +++ b/indexer/src/kadena-server/repository/application/balance-repository.ts @@ -1,4 +1,6 @@ -import { FungibleAccount, FungibleChainAccount } from '../../config/graphql-types'; +import { FungibleAccount, FungibleChainAccount, PageInfo, Token } from '../../config/graphql-types'; +import { PaginationsParams } from '../pagination'; +import { ConnectionEdge } from '../types'; export interface INonFungibleTokenBalance { id: string; @@ -29,6 +31,10 @@ export type FungibleAccountOutput = Omit< export type FungibleChainAccountOutput = Omit; +export type TokenOutput = Token; + +export interface GetTokensParams extends PaginationsParams {} + export default interface BalanceRepository { getAccountInfo(accountName: string, fungibleName?: string | null): Promise; @@ -59,6 +65,11 @@ export default interface BalanceRepository { tokenId: string, ): Promise; + getTokens(params: GetTokensParams): Promise<{ + pageInfo: PageInfo; + edges: ConnectionEdge[]; + }>; + /** methods below use balance from node */ getAccountInfo_NODE( accountName: string, diff --git a/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts index b996a6af..4c003471 100644 --- a/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts @@ -5,6 +5,7 @@ import { handleSingleQuery } from '../../../utils/raw-query'; import BalanceRepository, { FungibleAccountOutput, FungibleChainAccountOutput, + GetTokensParams, INonFungibleAccount, INonFungibleChainAccount, INonFungibleTokenBalance, @@ -16,6 +17,7 @@ import { import { fungibleAccountValidator } from '../schema-validator/fungible-account-validator'; import { fungibleChainAccountValidator } from '../schema-validator/fungible-chain-account-validator'; import { nonFungibleTokenBalanceValidator } from '../schema-validator/non-fungible-token-balance-validator'; +import { getPageInfo, getPaginationParams } from '../../pagination'; export default class BalanceDbRepository implements BalanceRepository { // TODO: waiting for orphan blocks mechanism to be ready @@ -490,4 +492,51 @@ export default class BalanceDbRepository implements BalanceRepository { const output = balances.map(r => fungibleChainAccountValidator.validate(r)); return output; } + + async getTokens(params: GetTokensParams) { + const { limit, order, after, before } = getPaginationParams(params); + + const queryParams: any[] = [limit]; + let conditions = ''; + + if (after) { + const [module, chainId] = after.split(','); + queryParams.push(module); + queryParams.push(chainId); + conditions += `\nAND (module, "chainId") > ($${queryParams.length - 1}, $${queryParams.length})`; + } + + if (before) { + const [module, chainId] = before.split(','); + queryParams.push(module); + queryParams.push(chainId); + conditions += `\nAND (module, "chainId") < ($${queryParams.length - 1}, $${queryParams.length})`; + } + + const query = ` + SELECT DISTINCT module, "chainId" + FROM "Balances" + WHERE module != 'coin' + ${conditions} + ORDER BY module, "chainId" + LIMIT $1 + `; + + const { rows } = await rootPgPool.query(query, queryParams); + + const edges = rows.map(row => { + const cursor = `${row.module},${row.chainId}`; + return { + cursor, + node: { + id: Buffer.from(`Token:[${cursor}]`).toString('base64'), + name: row.module, + chainId: String(row.chainId), + }, + }; + }); + + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; + } } diff --git a/indexer/src/kadena-server/resolvers/index.ts b/indexer/src/kadena-server/resolvers/index.ts index ca24aad7..7677e90c 100644 --- a/indexer/src/kadena-server/resolvers/index.ts +++ b/indexer/src/kadena-server/resolvers/index.ts @@ -64,6 +64,7 @@ import { totalCountNonFungibleAccountTransactionsConnectionResolver } from './fi import { totalCountNonFungibleChainAccountTransactionsConnectionResolver } from './fields/non-fungible-chain-account/transactions-connection/total-count-non-fungible-chain-account-transactions-connection-resolver'; import { fungibleChainAccountQueryResolver } from './query/fungible-chain-account-query-resolver'; import { powHashBlockResolver } from './fields/block/pow-hash-block-resolver'; +import { tokensQueryResolver } from './query/tokens-query'; export const resolvers: Resolvers = { DateTime: DateTimeResolver, @@ -97,6 +98,7 @@ export const resolvers: Resolvers = { transactions: transactionsQueryResolver, transactionsByPublicKey: transactionsByPublicKeyQueryResolver, transfers: transfersQueryResolver, + tokens: tokensQueryResolver, }, Block: { parent: parentBlockResolver, // data loader set. diff --git a/indexer/src/kadena-server/resolvers/query/tokens-query.ts b/indexer/src/kadena-server/resolvers/query/tokens-query.ts new file mode 100644 index 00000000..71523fbc --- /dev/null +++ b/indexer/src/kadena-server/resolvers/query/tokens-query.ts @@ -0,0 +1,13 @@ +import { ResolverContext } from '../../config/apollo-server-config'; +import { QueryResolvers } from '../../config/graphql-types'; + +export const tokensQueryResolver: QueryResolvers['tokens'] = async ( + _parent, + args, + context, +) => { + console.log('tokensQueryResolver'); + const { after, before, first, last } = args; + const output = await context.balanceRepository.getTokens({ after, before, first, last }); + return output; +}; diff --git a/indexer/src/services/sync/coinbase.ts b/indexer/src/services/sync/coinbase.ts index 5ffe6096..c055f8ed 100644 --- a/indexer/src/services/sync/coinbase.ts +++ b/indexer/src/services/sync/coinbase.ts @@ -46,8 +46,11 @@ export async function startBackfillCoinbaseTransactions() { process.exit(0); } -async function addCoinbaseTransactions(rows: Array, tx: Transaction) { - const fetchPromises = rows.map(async (row, index) => { +export async function addCoinbaseTransactions( + rows: Array, + tx: Transaction, +): Promise { + const fetchPromises = rows.map(async row => { const output = await processCoinbaseTransaction(row.coinbase, { id: row.id, chainId: row.chainId, @@ -93,6 +96,8 @@ async function addCoinbaseTransactions(rows: Array, tx: Transaction) { await Event.bulkCreate(eventsToAdd, { transaction: tx, }); + + return eventsToAdd; } export async function processCoinbaseTransaction( diff --git a/indexer/src/services/sync/guards.ts b/indexer/src/services/sync/guards.ts index 3f696b86..cee3118b 100644 --- a/indexer/src/services/sync/guards.ts +++ b/indexer/src/services/sync/guards.ts @@ -1,9 +1,9 @@ import pLimit from 'p-limit'; -import { closeDatabase, rootPgPool, sequelize } from '../../config/database'; +import { rootPgPool, sequelize } from '../../config/database'; import { getGuardsFromBalances } from './payload'; import Guard from '../../models/guard'; -const CONCURRENCY_LIMIT = 4; // Number of concurrent fetches allowed +const CONCURRENCY_LIMIT = 50; // Number of concurrent fetches allowed const limitFetch = pLimit(CONCURRENCY_LIMIT); export async function backfillGuards() { @@ -17,13 +17,19 @@ export async function backfillGuards() { await rootPgPool.query(deleteGuardsQuery); const limit = 1000; // Number of rows to process in one batch - let offset = 0; + let currentId = 0; while (true) { - console.log(`Fetching rows from offset: ${offset}, limit: ${limit}`); + console.log(`Fetching rows starting from: ${currentId}`); const res = await rootPgPool.query( - `SELECT b.id, b.account, b."chainId", b.module FROM "Balances" b ORDER BY b.id LIMIT $1 OFFSET $2`, - [limit, offset], + ` + SELECT b.id, b.account, b."chainId", b.module + FROM "Balances" b + WHERE b.id > $2 + ORDER BY b.id + LIMIT $1 + `, + [limit, currentId], ); const rows = res.rows; @@ -53,13 +59,13 @@ export async function backfillGuards() { }); await tx.commit(); - console.log(`Batch at offset ${offset} processed successfully.`); - offset += limit; + console.log(`Row at ${currentId} id processed successfully.`); + currentId = rows[rows.length - 1].id; } catch (batchError) { - console.error(`Error processing batch at offset ${offset}:`, batchError); + console.error(`Error processing at id ${currentId}:`, batchError); try { await tx.rollback(); - console.log(`Transaction for batch at offset ${offset} rolled back.`); + console.log(`Transaction for id at ${currentId} rolled back.`); } catch (rollbackError) { console.error('Error during rollback:', rollbackError); } diff --git a/indexer/src/services/sync/payload.ts b/indexer/src/services/sync/payload.ts index 31b52c85..13d76cf6 100644 --- a/indexer/src/services/sync/payload.ts +++ b/indexer/src/services/sync/payload.ts @@ -8,7 +8,8 @@ import Signer from '../../models/signer'; import Guard from '../../models/guard'; import { handleSingleQuery } from '../../kadena-server/utils/raw-query'; import { sequelize } from '../../config/database'; -import { processCoinbaseTransaction } from './coinbase'; +import { addCoinbaseTransactions } from './coinbase'; +import { getRequiredEnvString } from '../../utils/helpers'; const TRANSACTION_INDEX = 0; const RECEIPT_INDEX = 1; @@ -20,6 +21,8 @@ interface BalanceInsertResult { module: string; } +const NETWORK_ID = getRequiredEnvString('SYNC_NETWORK'); + export async function processPayloadKey( block: BlockAttributes, payloadData: any, @@ -27,17 +30,18 @@ export async function processPayloadKey( ): Promise { const transactions = payloadData.transactions || []; - const transactionPromises = transactions.map((transactionArray: any) => - processTransaction(transactionArray, block, tx), - ); - - await processCoinbaseTransaction(payloadData.coinbase, { - id: block.id, - chainId: block.chainId, - creationTime: block.creationTime, + const transactionPromises = transactions.map((transactionInfo: any) => { + return processTransaction(transactionInfo, block, tx); }); + const normalTransactions = (await Promise.all(transactionPromises)).flat(); + + // TODO: This will be removed after TransactionDetails migration + if (NETWORK_ID === 'mainnet01') return normalTransactions; + + const coinbase = await addCoinbaseTransactions([block], tx!); + const coinbaseTransactions = (await Promise.all(coinbase)).flat(); - return (await Promise.all(transactionPromises)).flat(); + return [...normalTransactions, ...coinbaseTransactions]; } export async function processTransaction( diff --git a/indexer/src/services/sync/streaming.ts b/indexer/src/services/sync/streaming.ts index 56e753e1..752c78c9 100644 --- a/indexer/src/services/sync/streaming.ts +++ b/indexer/src/services/sync/streaming.ts @@ -57,7 +57,7 @@ export async function startStreaming() { ); backfillGuards(); // run when initialize - setInterval(backfillGuards, 1000 * 60 * 60); // every one hour + setInterval(backfillGuards, 1000 * 60 * 60 * 12); // every one 12 hours } export function processPayload(payload: any) { diff --git a/indexer/tests/integration/tokens.query.test.ts b/indexer/tests/integration/tokens.query.test.ts new file mode 100644 index 00000000..0fece7c9 --- /dev/null +++ b/indexer/tests/integration/tokens.query.test.ts @@ -0,0 +1,115 @@ +import { GraphQLClient, gql } from 'graphql-request'; +const API_URL = 'http://localhost:3001/graphql'; + +const client = new GraphQLClient(API_URL); + +const resOne = { + data: { + tokens: { + edges: [ + { + cursor: 'YXJrYWRlLnRva2VuLDEy', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxMl0=', + name: 'arkade.token', + chainId: '12', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDEz', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxM10=', + name: 'arkade.token', + chainId: '13', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDE0', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxNF0=', + name: 'arkade.token', + chainId: '14', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDE1', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxNV0=', + name: 'arkade.token', + chainId: '15', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDE2', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxNl0=', + name: 'arkade.token', + chainId: '16', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDE3', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxN10=', + name: 'arkade.token', + chainId: '17', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDE4', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxOF0=', + name: 'arkade.token', + chainId: '18', + }, + }, + { + cursor: 'YXJrYWRlLnRva2VuLDE5', + node: { + id: 'VG9rZW46W2Fya2FkZS50b2tlbiwxOV0=', + name: 'arkade.token', + chainId: '19', + }, + }, + { + cursor: 'ZnJlZS5haXIsMQ==', + node: { + id: 'VG9rZW46W2ZyZWUuYWlyLDFd', + name: 'free.air', + chainId: '1', + }, + }, + { + cursor: 'ZnJlZS5hbmVkYWssMA==', + node: { + id: 'VG9rZW46W2ZyZWUuYW5lZGFrLDBd', + name: 'free.anedak', + chainId: '0', + }, + }, + ], + }, + }, +}; + +describe('Tokens query', () => { + it('first: 10, after: "YXJrYWRlLnRva2VuLDEx"', async () => { + const query = gql` + query { + tokens(first: 10, after: "YXJrYWRlLnRva2VuLDEx") { + edges { + cursor + node { + id + name + chainId + } + } + } + } + `; + + const data = await client.request(query); + expect(resOne.data).toMatchObject(data); + }); +}); diff --git a/package.json b/package.json index 326da9c0..699b1390 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "prettier": "^3.5.1" }, "lint-staged": { - "*.{yml,yaml,go,json,md,js,ts}": [ + "*.{yml,yaml,json,md,js,ts}": [ "prettier --write" ] }