diff --git a/apps/api-gateway/meshrc.ts b/apps/api-gateway/meshrc.ts index 8dd2aff78..bd52a6e9b 100644 --- a/apps/api-gateway/meshrc.ts +++ b/apps/api-gateway/meshrc.ts @@ -27,6 +27,14 @@ export default processConfig( }, }, transforms: [ + { + filterSchema: { + filters: [ + 'Query.!{accountBalance}', + 'Query.!{accountBalances}' + ] + } + }, { rename: { renames: [ @@ -57,7 +65,7 @@ export default processConfig( ]; }), ], - additionalTypeDefs:` + additionalTypeDefs: ` type AverageDelegationPercentageItem { date: String! high: String! diff --git a/apps/api-gateway/package.json b/apps/api-gateway/package.json index 99225c7c2..0b18244ac 100644 --- a/apps/api-gateway/package.json +++ b/apps/api-gateway/package.json @@ -30,6 +30,7 @@ "@graphql-mesh/openapi": "^0.109.8", "@graphql-mesh/runtime": "^0.106.3", "@graphql-mesh/transform-rename": "^0.105.7", + "@graphql-mesh/transform-filter-schema": "^0.104.18", "@graphql-mesh/types": "^0.104.3", "dotenv": "^16.5.0", "graphql": "^16.11.0" diff --git a/apps/api-gateway/schema.graphql b/apps/api-gateway/schema.graphql index b464f1579..cc7405334 100644 --- a/apps/api-gateway/schema.graphql +++ b/apps/api-gateway/schema.graphql @@ -11,13 +11,11 @@ directive @httpOperation(subgraph: String, path: String, operationSpecificHeader directive @transport(subgraph: String, kind: String, location: String, headers: [[String]], queryStringOptions: ObjMap, queryParams: [[String]]) repeatable on SCHEMA type Query { - """Get property data for a specific token""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/token`\nGet property data for a specific token\n" token(currency: queryInput_token_currency = usd): token_200_response tokens(where: tokenFilter, orderBy: String, orderDirection: String, before: String, after: String, limit: Int): tokenPage! account(id: String!): account accounts(where: accountFilter, orderBy: String, orderDirection: String, before: String, after: String, limit: Int): accountPage! - accountBalance(accountId: String!, tokenId: String!): accountBalance - accountBalances(where: accountBalanceFilter, orderBy: String, orderDirection: String, before: String, after: String, limit: Int): accountBalancePage! accountPower(accountId: String!): accountPower accountPowers(where: accountPowerFilter, orderBy: String, orderDirection: String, before: String, after: String, limit: Int): accountPowerPage! votingPowerHistory(transactionHash: String!, accountId: String!, logIndex: Float!): votingPowerHistory @@ -38,72 +36,61 @@ type Query { tokenPrices(where: tokenPriceFilter, orderBy: String, orderDirection: String, before: String, after: String, limit: Int): tokenPricePage! _meta: Meta - """Get total assets""" - totalAssets(days: queryInput_totalAssets_days = _7d): [query_totalAssets_items] - - """Get historical market data for a specific token""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/token/historical-data`\nGet historical market data for a specific token\n" historicalTokenData(skip: NonNegativeInt, limit: Float = 365): [query_historicalTokenData_items] - """Compare total supply between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/total-supply/compare`\nCompare total supply between periods\n" compareTotalSupply(days: queryInput_compareTotalSupply_days = _90d): compareTotalSupply_200_response - """Compare delegated supply between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/delegated-supply/compare`\nCompare delegated supply between periods\n" compareDelegatedSupply(days: queryInput_compareDelegatedSupply_days = _90d): compareDelegatedSupply_200_response - """Compare circulating supply between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/circulating-supply/compare`\nCompare circulating supply between periods\n" compareCirculatingSupply(days: queryInput_compareCirculatingSupply_days = _90d): compareCirculatingSupply_200_response - """Compare treasury between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/treasury/compare`\nCompare treasury between periods\n" compareTreasury(days: queryInput_compareTreasury_days = _90d): compareTreasury_200_response - """Compare cex supply between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/cex-supply/compare`\nCompare cex supply between periods\n" compareCexSupply(days: queryInput_compareCexSupply_days = _90d): compareCexSupply_200_response - """Compare dex supply between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/dex-supply/compare`\nCompare dex supply between periods\n" compareDexSupply(days: queryInput_compareDexSupply_days = _90d): compareDexSupply_200_response - """Compare lending supply between periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/lending-supply/compare`\nCompare lending supply between periods\n" compareLendingSupply(days: queryInput_compareLendingSupply_days = _90d): compareLendingSupply_200_response - """Get active token supply for DAO""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/active-supply/compare`\nGet active token supply for DAO\n" compareActiveSupply(days: queryInput_compareActiveSupply_days = _90d): compareActiveSupply_200_response - """Compare number of proposals between time periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/proposals/compare`\nCompare number of proposals between time periods\n" compareProposals(days: queryInput_compareProposals_days = _90d): compareProposals_200_response - """Compare number of votes between time periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/votes/compare`\nCompare number of votes between time periods\n" compareVotes(days: queryInput_compareVotes_days = _90d): compareVotes_200_response - """Compare average turnout between time periods""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/average-turnout/compare`\nCompare average turnout between time periods\n" compareAverageTurnout(days: queryInput_compareAverageTurnout_days = _90d): compareAverageTurnout_200_response - """ - Returns proposal activity data including voting history, win rates, and detailed proposal information for the specified delegate within the given time window - """ + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/proposals-activity`\nReturns proposal activity data including voting history, win rates, and detailed proposal information for the specified delegate within the given time window\n" proposalsActivity(address: String!, fromDate: NonNegativeInt, skip: NonNegativeInt, limit: PositiveInt = 10, orderBy: queryInput_proposalsActivity_orderBy = timestamp, orderDirection: queryInput_proposalsActivity_orderDirection = desc, userVoteFilter: queryInput_proposalsActivity_userVoteFilter): proposalsActivity_200_response - """Returns a list of proposal""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/proposals`\nReturns a list of proposal\n" proposals(skip: NonNegativeInt, limit: PositiveInt = 10, orderDirection: queryInput_proposals_orderDirection = desc, status: JSON, fromDate: Float, fromEndDate: Float, includeOptimisticProposals: queryInput_proposals_includeOptimisticProposals = TRUE): proposals_200_response - """Returns a single proposal by its ID""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/proposals/{args.id}`\nReturns a single proposal by its ID\n" proposal(id: String!): proposal_200_response - """Returns the active delegates that did not vote on a given proposal""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/proposals/{args.id}/non-voters`\nReturns the active delegates that did not vote on a given proposal\n" proposalNonVoters(id: String!, skip: NonNegativeInt, limit: PositiveInt = 10, orderDirection: queryInput_proposalNonVoters_orderDirection = desc, addresses: JSON): proposalNonVoters_200_response - """ - Fetch historical token balances for multiple addresses at a specific time period using multicall - """ + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/account-balances/historical`\nFetch historical token balances for multiple addresses at a specific time period using multicall\n" historicalBalances(addresses: JSON!, days: queryInput_historicalBalances_days = _7d): [query_historicalBalances_items] - """ - Fetch historical voting power for multiple addresses at a specific time period using multicall - """ + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/historical-voting-power`\nFetch historical voting power for multiple addresses at a specific time period using multicall\n" historicalVotingPower(addresses: JSON!, days: queryInput_historicalVotingPower_days = _7d, fromDate: Float): [query_historicalVotingPower_items] - """ - Get transactions with their associated transfers and delegations, with optional filtering and sorting - """ + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/transactions`\nGet transactions with their associated transfers and delegations, with optional filtering and sorting\n" transactions( limit: PositiveInt = 50 offset: NonNegativeInt @@ -125,32 +112,37 @@ type Query { includes: JSON ): transactions_200_response - """Get the last update time""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/last-update`\nGet the last update time\n" lastUpdate(chart: queryInput_lastUpdate_chart!): lastUpdate_200_response - """Get delegation percentage day buckets with forward-fill""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/delegation-percentage`\nGet delegation percentage day buckets with forward-fill\n" delegationPercentageByDay(startDate: String, endDate: String, orderDirection: queryInput_delegationPercentageByDay_orderDirection = asc, limit: NonNegativeInt = 365, after: String, before: String): delegationPercentageByDay_200_response - """Returns a list of voting power changes""" - votingPowers(account: String!, fromAddresses: JSON, toAddresses: JSON, skip: NonNegativeInt, limit: PositiveInt = 10, orderBy: queryInput_votingPowers_orderBy = timestamp, orderDirection: queryInput_votingPowers_orderDirection = desc, minDelta: String, maxDelta: String): votingPowers_200_response + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/voting-powers`\nReturns a list of voting power changes\n" + votingPowers(account: String!, skip: NonNegativeInt, limit: PositiveInt = 10, orderBy: queryInput_votingPowers_orderBy = timestamp, orderDirection: queryInput_votingPowers_orderDirection = desc, minDelta: String, maxDelta: String): votingPowers_200_response - """ - Returns a mapping of the biggest changes to voting power associated by delegate address - """ + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/voting-power/variations`\nReturns a mapping of the biggest changes to voting power associated by delegate address\n" votingPowerVariations(days: queryInput_votingPowerVariations_days = _90d, limit: PositiveInt = 20, skip: NonNegativeInt, orderDirection: queryInput_votingPowerVariations_orderDirection = desc): votingPowerVariations_200_response - """ - Returns a mapping of the biggest variations to account balances associated by account address - """ + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/account-balance/variations`\nReturns a mapping of the biggest variations to account balances associated by account address\n" accountBalanceVariations(days: queryInput_accountBalanceVariations_days = _90d, limit: PositiveInt = 20, skip: NonNegativeInt, orderDirection: queryInput_accountBalanceVariations_orderDirection = desc): accountBalanceVariations_200_response + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/account-balances`\nReturns account balances\n" + accountBalances(limit: PositiveInt = 20, skip: NonNegativeInt, orderDirection: queryInput_accountBalances_orderDirection = desc, addresses: [String] = [], delegates: [String] = [], balanceGreaterThan: String, balanceLessThan: String): accountBalances_200_response + """ Returns a mapping of the largest interactions between accounts. Positive amounts signify net token transfers FROM , whilst negative amounts refer to net transfers TO """ accountInteractions(days: queryInput_accountInteractions_days = _90d, limit: PositiveInt = 20, skip: NonNegativeInt, orderDirection: queryInput_accountInteractions_orderDirection = desc, accountId: String!, minAmount: String, maxAmount: String, orderBy: queryInput_accountInteractions_orderBy = count, address: String): accountInteractions_200_response - """Returns current governance parameters for this DAO""" + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/account-balances/{args.accountId}`\nReturns account balance\n" + accountBalanceByAccountId(accountId: String!): accountBalanceByAccountId_200_response + + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/account-balance/interactions`\nReturns a mapping of the largest interactions between accounts. \nPositive amounts signify net token transfers FROM , whilst negative amounts refer to net transfers TO \n" + accountInteractions(days: queryInput_accountInteractions_days = _90d, limit: PositiveInt = 20, skip: NonNegativeInt, orderDirection: queryInput_accountInteractions_orderDirection = desc, accountId: String!, minAmount: String, maxAmount: String, orderBy: queryInput_accountInteractions_orderBy = count, address: String): accountInteractions_200_response + + "\n>**Method**: `GET`\n>**Base URL**: `http://localhost:42069`\n>**Path**: `/dao`\nReturns current governance parameters for this DAO\n" dao: dao_200_response """ @@ -1330,19 +1322,6 @@ input tokenPriceFilter { timestamp_lte: BigInt } -type query_totalAssets_items { - totalAssets: String! - date: String! -} - -enum queryInput_totalAssets_days { - _7d - _30d - _90d - _180d - _365d -} - type query_historicalTokenData_items { price: String! timestamp: Float! @@ -1884,6 +1863,30 @@ enum queryInput_accountBalanceVariations_orderDirection { desc } +type accountBalances_200_response { + items: [query_accountBalances_items_items]! + totalCount: Float! +} + +type query_accountBalances_items_items { + accountId: String! + balance: String! + tokenId: String! + delegate: String! +} + +enum queryInput_accountBalances_orderDirection { + asc + desc +} + +type accountBalanceByAccountId_200_response { + accountId: String! + balance: String! + tokenId: String! + delegate: String! +} + type accountInteractions_200_response { period: query_accountInteractions_period! totalCount: Float! diff --git a/apps/api-gateway/src/resolvers/item.ts b/apps/api-gateway/src/resolvers/item.ts index bc08d62c2..40f9fca30 100644 --- a/apps/api-gateway/src/resolvers/item.ts +++ b/apps/api-gateway/src/resolvers/item.ts @@ -1,6 +1,5 @@ const daoItemQueries = [ "account", - "accountBalance", "accountPower", "daoMetricsDayBucket", "delegation", diff --git a/apps/api-gateway/src/resolvers/list.ts b/apps/api-gateway/src/resolvers/list.ts index 32772408e..929094b2f 100644 --- a/apps/api-gateway/src/resolvers/list.ts +++ b/apps/api-gateway/src/resolvers/list.ts @@ -1,6 +1,5 @@ const daoListQueries = [ - 'accountBalances', 'accountPowers', 'accounts', 'daoMetricsDayBuckets', diff --git a/apps/api-gateway/src/resolvers/rest.ts b/apps/api-gateway/src/resolvers/rest.ts index 0e832aec1..5932ea344 100644 --- a/apps/api-gateway/src/resolvers/rest.ts +++ b/apps/api-gateway/src/resolvers/rest.ts @@ -1,5 +1,8 @@ const daoItemQueries = [ - 'accountInteractions', + "accountBalanceByAccountId", + "accountBalanceVariations", + "accountBalances", + "accountInteractions", "compareActiveSupply", "compareAverageTurnout", "compareCexSupply", @@ -11,23 +14,22 @@ const daoItemQueries = [ "compareTotalSupply", "compareTreasury", "compareVotes", + "dao", + "delegationPercentageByDay", "getTotalAssets", "getVotingPower", - "historicalTokenData", - "proposalsActivity", "historicalBalances", + "historicalTokenData", "historicalVotingPower", - "proposals", - "transactions", "lastUpdate", "proposal", - "votingPowers", "proposalNonVoters", + "proposals", + "proposalsActivity", "token", + "transactions", "votingPowerVariations", - "accountBalanceVariations", - "delegationPercentageByDay", - "dao", + "votingPowers", ]; export const restResolvers = daoItemQueries.reduce((acc, fieldName) => { diff --git a/apps/indexer/src/api/controllers/account-balance/historical-balances.ts b/apps/indexer/src/api/controllers/account-balance/historical-balances.ts index f33a9500f..2380b8d25 100644 --- a/apps/indexer/src/api/controllers/account-balance/historical-balances.ts +++ b/apps/indexer/src/api/controllers/account-balance/historical-balances.ts @@ -19,11 +19,11 @@ export function historicalBalances( createRoute({ method: "get", operationId: "historicalBalances", - path: "/historical-balances", + path: "/account-balances/historical", summary: "Get historical token balances", description: "Fetch historical token balances for multiple addresses at a specific time period using multicall", - tags: ["historical-onchain"], + tags: ["account-balances"], request: { query: z.object({ addresses: z diff --git a/apps/indexer/src/api/controllers/account-balance/index.ts b/apps/indexer/src/api/controllers/account-balance/index.ts index 58f1265b2..4b01e2803 100644 --- a/apps/indexer/src/api/controllers/account-balance/index.ts +++ b/apps/indexer/src/api/controllers/account-balance/index.ts @@ -1,3 +1,4 @@ export * from "./historical-balances"; export * from "./interactions"; export * from "./variations"; +export * from "./listing"; diff --git a/apps/indexer/src/api/controllers/account-balance/listing.ts b/apps/indexer/src/api/controllers/account-balance/listing.ts new file mode 100644 index 000000000..86ac4a895 --- /dev/null +++ b/apps/indexer/src/api/controllers/account-balance/listing.ts @@ -0,0 +1,103 @@ +import { createRoute, OpenAPIHono as Hono, z } from "@hono/zod-openapi"; +import { AccountBalanceService } from "@/api/services"; +import { + AccountBalancesRequestSchema, + AccountBalancesResponseMapper, + AccountBalancesResponseSchema, +} from "@/api/mappers"; +import { + AccountBalanceResponseMapper, + AccountBalanceResponseSchema, +} from "@/api/mappers"; +import { Address, isAddress } from "viem"; +import { DaoIdEnum } from "@/lib/enums"; + +export function accountBalances( + app: Hono, + daoId: DaoIdEnum, + service: AccountBalanceService, +) { + app.openapi( + createRoute({ + method: "get", + operationId: "accountBalances", + path: "/account-balances", + summary: "Get account balance records", + description: "Returns sorted and paginated account balance records", + tags: ["account-balances"], + request: { + query: AccountBalancesRequestSchema, + }, + responses: { + 200: { + description: "Successfully retrieved account balances", + content: { + "application/json": { + schema: AccountBalancesResponseSchema, + }, + }, + }, + }, + }), + async (context) => { + const { + addresses, + delegates, + toValue, + fromValue, + limit, + skip, + orderDirection, + } = context.req.valid("query"); + + const result = await service.getAccountBalances( + daoId, + skip, + limit, + orderDirection, + addresses, + delegates, + { + minAmount: fromValue, + maxAmount: toValue, + }, + ); + + return context.json( + AccountBalancesResponseMapper(result.items, result.totalCount), + ); + }, + ); + + app.openapi( + createRoute({ + method: "get", + operationId: "accountBalanceByAccountId", + path: "/account-balances/{accountId}", + summary: "Get account balance", + description: + "Returns account balance information for a specific address (account)", + tags: ["account-balances"], + request: { + params: z.object({ + accountId: z.string().refine((addr) => isAddress(addr)), + }), + }, + responses: { + 200: { + description: "Successfully retrieved account balance", + content: { + "application/json": { + schema: AccountBalanceResponseSchema, + }, + }, + }, + }, + }), + async (context) => { + const accountId = context.req.param("accountId"); + const result = await service.getAccountBalance(accountId as Address); + return context.json(AccountBalanceResponseMapper(result)); + }, + ); +} diff --git a/apps/indexer/src/api/index.ts b/apps/indexer/src/api/index.ts index f31d2c557..8569d7a7b 100644 --- a/apps/indexer/src/api/index.ts +++ b/apps/indexer/src/api/index.ts @@ -23,13 +23,14 @@ import { accountBalanceVariations, dao, accountInteractions, + accountBalances, } from "@/api/controllers"; import { docs } from "@/api/docs"; import { env } from "@/env"; import { DaoCache } from "@/api/cache/dao-cache"; import { DelegationPercentageRepository, - AccountBalanceRepository, + BalanceVariationsRepository, DrizzleRepository, NFTPriceRepository, TokenRepository, @@ -55,9 +56,11 @@ import { BalanceVariationsService, HistoricalBalancesService, DaoService, + AccountBalanceService, } from "@/api/services"; import { CONTRACT_ADDRESSES } from "@/lib/constants"; import { DaoIdEnum } from "@/lib/enums"; +import { AccountBalanceRepository } from "./repositories/account-balance/listing"; const app = new Hono({ defaultHook: (result, c) => { @@ -113,6 +116,7 @@ const delegationPercentageRepo = new DelegationPercentageRepository(); const delegationPercentageService = new DelegationPercentageService( delegationPercentageRepo, ); +const balanceVariationsRepo = new BalanceVariationsRepository(); const accountBalanceRepo = new AccountBalanceRepository(); const accountInteractionRepo = new AccountInteractionsRepository(); const transactionsService = new TransactionsService(transactionsRepo); @@ -124,10 +128,11 @@ const votingPowerService = new VotingPowerService( ); const daoCache = new DaoCache(); const daoService = new DaoService(daoClient, daoCache, env.CHAIN_ID); -const accountBalanceService = new BalanceVariationsService( - accountBalanceRepo, +const balanceVariationsService = new BalanceVariationsService( + balanceVariationsRepo, accountInteractionRepo, ); +const accountBalanceService = new AccountBalanceService(accountBalanceRepo); if (env.DUNE_API_URL && env.DUNE_API_KEY) { const duneClient = new DuneService(env.DUNE_API_URL, env.DUNE_API_KEY); @@ -168,15 +173,16 @@ historicalBalances( app, env.DAO_ID, new HistoricalVotingPowerService(votingPowerRepo), - new HistoricalBalancesService(accountBalanceRepo), + new HistoricalBalancesService(balanceVariationsRepo), ); transactions(app, transactionsService); lastUpdate(app); delegationPercentage(app, delegationPercentageService); votingPower(app, votingPowerService); votingPowerVariations(app, votingPowerService); -accountBalanceVariations(app, accountBalanceService); -accountInteractions(app, accountBalanceService); +accountBalanceVariations(app, balanceVariationsService); +accountBalances(app, env.DAO_ID, accountBalanceService); +accountInteractions(app, balanceVariationsService); dao(app, daoService); docs(app); diff --git a/apps/indexer/src/api/mappers/account-balance/index.ts b/apps/indexer/src/api/mappers/account-balance/index.ts index 7e3780b7b..87a768d17 100644 --- a/apps/indexer/src/api/mappers/account-balance/index.ts +++ b/apps/indexer/src/api/mappers/account-balance/index.ts @@ -5,6 +5,81 @@ import { PERCENTAGE_NO_BASELINE } from "@/api/mappers/constants"; import { CONTRACT_ADDRESSES } from "@/lib/constants"; import { calculateHistoricalBlockNumber } from "@/lib/blockTime"; +export const AccountBalancesRequestSchema = z.object({ + // TODO: Validate + limit: z.coerce + .number() + .int() + .min(1, "Limit must be a positive integer") + .max(100, "Limit cannot exceed 100") + .optional() + .default(20), + skip: z.coerce + .number() + .int() + .min(0, "Skip must be a non-negative integer") + .optional() + .default(0), + orderDirection: z.enum(["asc", "desc"]).optional().default("desc"), + addresses: z + .array(z.string().refine((addr) => isAddress(addr))) + .optional() + .default([]), + delegates: z + .array(z.string().refine((addr) => isAddress(addr))) + .optional() + .default([]), + fromValue: z + .string() + .transform((val) => BigInt(val)) + .optional(), + toValue: z + .string() + .transform((val) => BigInt(val)) + .optional(), +}); + +export const AccountBalanceResponseSchema = z.object({ + accountId: z.string(), + balance: z.string(), + tokenId: z.string(), + delegate: z.string(), +}); + +export const AccountBalancesResponseSchema = z.object({ + items: z.array(AccountBalanceResponseSchema), + totalCount: z.number(), +}); + +export type AccountBalancesResponse = z.infer< + typeof AccountBalancesResponseSchema +>; + +export type AccountBalanceResponse = z.infer< + typeof AccountBalanceResponseSchema +>; + +export const AccountBalancesResponseMapper = ( + items: DBAccountBalance[], + totalCount: bigint, +): AccountBalancesResponse => { + return { + totalCount: Number(totalCount), + items: items.map((item) => AccountBalanceResponseMapper(item)), + }; +}; + +export const AccountBalanceResponseMapper = ( + item: DBAccountBalance, +): AccountBalanceResponse => { + return { + accountId: item.accountId, + balance: item.balance.toString(), + tokenId: item.tokenId, + delegate: item.delegate, + }; +}; + export const AccountBalanceVariationsRequestSchema = z.object({ days: z .enum(DaysOpts) @@ -102,6 +177,13 @@ export type DBAccountInteraction = DBAccountBalanceVariation & { transferCount: bigint; }; +export type DBAccountBalance = { + accountId: Address; + delegate: string; + tokenId: string; + balance: bigint; +}; + export interface AccountInteractions { interactionCount: number; interactions: DBAccountInteraction[]; diff --git a/apps/indexer/src/api/mappers/index.ts b/apps/indexer/src/api/mappers/index.ts index a40f25bdd..35f8b0def 100644 --- a/apps/indexer/src/api/mappers/index.ts +++ b/apps/indexer/src/api/mappers/index.ts @@ -6,3 +6,4 @@ export * from "./token"; export * from "./delegation-percentage"; export * from "./account-balance"; export * from "./dao"; +export * from "./shared"; diff --git a/apps/indexer/src/api/mappers/shared.ts b/apps/indexer/src/api/mappers/shared.ts new file mode 100644 index 000000000..fe5be8b2d --- /dev/null +++ b/apps/indexer/src/api/mappers/shared.ts @@ -0,0 +1,4 @@ +export type AmountFilter = { + minAmount: number | bigint | undefined; + maxAmount: number | bigint | undefined; +}; diff --git a/apps/indexer/src/api/repositories/account-balance/listing.ts b/apps/indexer/src/api/repositories/account-balance/listing.ts new file mode 100644 index 000000000..892ae2fc1 --- /dev/null +++ b/apps/indexer/src/api/repositories/account-balance/listing.ts @@ -0,0 +1,117 @@ +import { AmountFilter, DBAccountBalance } from "@/api/mappers"; +import { + and, + asc, + desc, + eq, + gt, + inArray, + lt, + not, + SQL, + sql, +} from "drizzle-orm"; +import { db } from "ponder:api"; +import { accountBalance } from "ponder:schema"; +import { Address } from "viem"; + +export class AccountBalanceRepository { + async getAccountBalances( + skip: number, + limit: number, + orderDirection: "asc" | "desc", + addresses: Address[], + delegates: Address[], + excludeAddresses: Address[], + amountfilter: AmountFilter, + ): Promise<{ + items: DBAccountBalance[]; + totalCount: bigint; + }> { + const filter = this.filterToSql( + addresses, + delegates, + excludeAddresses, + amountfilter, + ); + + // Get total count with filters + const totalCount = await db + .select({ + count: sql`COUNT(*)`.as("count"), + }) + .from(accountBalance) + .where(filter); + + // Get paginated results + const page = await db + .select() + .from(accountBalance) + .where(filter) + .orderBy( + orderDirection === "desc" + ? desc(accountBalance.balance) + : asc(accountBalance.balance), + ) + .offset(skip) + .limit(limit); + + return { + items: page, + totalCount: BigInt(totalCount[0]?.count ?? 0), + }; + } + + async getAccountBalance( + accountId: Address, + ): Promise { + const [result] = await db + .select() + .from(accountBalance) + .where(eq(accountBalance.accountId, accountId)) + .limit(1); + + return result; + } + + private filterToSql( + addresses: Address[], + delegates: Address[], + excludeAddresses: Address[], + amountfilter: AmountFilter, + ): SQL | undefined { + const conditions = []; + + if (addresses.length) { + conditions.push(inArray(accountBalance.accountId, addresses)); + } + + if (delegates.length) { + conditions.push(inArray(accountBalance.delegate, delegates)); + } + + if ( + amountfilter.minAmount !== null && + amountfilter.minAmount !== undefined + ) { + conditions.push( + gt(accountBalance.balance, BigInt(amountfilter.minAmount)), + ); + } + + if ( + amountfilter.maxAmount !== null && + amountfilter.maxAmount !== undefined + ) { + conditions.push( + lt(accountBalance.balance, BigInt(amountfilter.maxAmount)), + ); + } + + if (excludeAddresses.length) { + conditions.push(not(inArray(accountBalance.accountId, excludeAddresses))); + } + + return and(...conditions); + } +} diff --git a/apps/indexer/src/api/repositories/account-balance/variations.ts b/apps/indexer/src/api/repositories/account-balance/variations.ts index df64ae922..fbcb5f75f 100644 --- a/apps/indexer/src/api/repositories/account-balance/variations.ts +++ b/apps/indexer/src/api/repositories/account-balance/variations.ts @@ -4,7 +4,7 @@ import { transfer, accountBalance } from "ponder:schema"; import { DBAccountBalanceVariation, DBHistoricalBalance } from "@/api/mappers"; import { Address } from "viem"; -export class AccountBalanceRepository { +export class BalanceVariationsRepository { async getHistoricalBalances( addresses: Address[], timestamp: number, diff --git a/apps/indexer/src/api/services/account-balance/index.ts b/apps/indexer/src/api/services/account-balance/index.ts index cb880d8c5..2844f789f 100644 --- a/apps/indexer/src/api/services/account-balance/index.ts +++ b/apps/indexer/src/api/services/account-balance/index.ts @@ -1,2 +1,3 @@ export * from "./historical"; export * from "./variations"; +export * from "./listing"; diff --git a/apps/indexer/src/api/services/account-balance/listing.ts b/apps/indexer/src/api/services/account-balance/listing.ts new file mode 100644 index 000000000..5a0f6c125 --- /dev/null +++ b/apps/indexer/src/api/services/account-balance/listing.ts @@ -0,0 +1,60 @@ +import { AmountFilter, DBAccountBalance } from "@/api/mappers"; +import { TreasuryAddresses } from "@/lib/constants"; +import { DaoIdEnum } from "@/lib/enums"; +import { Address } from "viem"; + +interface AccountBalanceRepository { + getAccountBalances( + skip: number, + limit: number, + orderDirection: "asc" | "desc", + addresses: Address[], + delegates: Address[], + excludeAddresses: Address[], + amountfilter: AmountFilter, + ): Promise<{ + items: DBAccountBalance[]; + totalCount: bigint; + }>; + + getAccountBalance(accountId: Address): Promise; +} + +export class AccountBalanceService { + constructor(private readonly repo: AccountBalanceRepository) {} + + async getAccountBalances( + daoId: DaoIdEnum, + skip: number, + limit: number, + orderDirection: "asc" | "desc", + addresses: Address[], + delegates: Address[], + amountFilter: AmountFilter, + ): Promise<{ + items: DBAccountBalance[]; + totalCount: bigint; + }> { + const excludeAddresses = Object.values(TreasuryAddresses[daoId]); + + return await this.repo.getAccountBalances( + skip, + limit, + orderDirection, + addresses, + delegates, + excludeAddresses, + amountFilter, + ); + } + + async getAccountBalance(accountId: Address): Promise { + const result = await this.repo.getAccountBalance(accountId); + + if (!result) { + throw new Error("Account not found"); + } + + return result; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4537dad3d..4b6699d9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,7 +60,7 @@ importers: version: 0.108.20(graphql@16.12.0) "@graphql-mesh/graphql": specifier: ^0.104.3 - version: 0.104.18(@types/node@24.10.1)(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10) + version: 0.104.18(@types/node@20.19.25)(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10) "@graphql-mesh/http": specifier: ^0.106.3 version: 0.106.18(graphql@16.12.0) @@ -70,6 +70,9 @@ importers: "@graphql-mesh/runtime": specifier: ^0.106.3 version: 0.106.18(graphql@16.12.0) + "@graphql-mesh/transform-filter-schema": + specifier: ^0.104.18 + version: 0.104.18(graphql@16.12.0) "@graphql-mesh/transform-rename": specifier: ^0.105.7 version: 0.105.18(graphql@16.12.0) @@ -91,10 +94,10 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + version: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) ts-jest: specifier: ^29.4.1 - version: 29.4.6(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.6(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(esbuild@0.25.8)(jest-util@29.7.0)(jest@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)))(typescript@5.9.3) tsx: specifier: ^4.19.4 version: 4.21.0 @@ -3384,6 +3387,15 @@ packages: peerDependencies: graphql: "*" + "@graphql-mesh/cross-helpers@0.4.11": + resolution: + { + integrity: sha512-p4zBxfZ4NDXKzRIpUJnH5ATkxnjDOu1X1Er5RRZhb15crotTyGwyOo/vnRe7Tyl3b9+kBewz1tBZ3g381g9uUQ==, + } + engines: { node: ">=16.0.0" } + peerDependencies: + graphql: "*" + "@graphql-mesh/fusion-composition@0.8.21": resolution: { @@ -3474,6 +3486,15 @@ packages: peerDependencies: graphql: "*" + "@graphql-mesh/transform-filter-schema@0.104.18": + resolution: + { + integrity: sha512-sCmIMkLUOgtcI/dt9SR7Mc876ZD0F+l85x7UKVLfuwD0ztUqbaYYgdytbNH3gPa9JQ/3FMA9S1ZBtLSiRVIDbA==, + } + engines: { node: ">=16.0.0" } + peerDependencies: + graphql: "*" + "@graphql-mesh/transform-rename@0.105.18": resolution: { @@ -3510,6 +3531,15 @@ packages: peerDependencies: graphql: "*" + "@graphql-mesh/types@0.104.18": + resolution: + { + integrity: sha512-bztJSYglXZv7greXC6cyI/j/5kt0TR0k8gV+JOGvHch5bEQZRKdN5fA3oXPwD/Ees+7zWTGN05e9xfkOT3nzUg==, + } + engines: { node: ">=16.0.0" } + peerDependencies: + graphql: "*" + "@graphql-mesh/utils@0.104.17": resolution: { @@ -3519,6 +3549,15 @@ packages: peerDependencies: graphql: "*" + "@graphql-mesh/utils@0.104.18": + resolution: + { + integrity: sha512-iBLlzwMt+6m+aW6QUN26cOotezXlEgLcOPIorDWxtKD49J4urcQgGOGznrXMLb/dx9rVPPPKY0TR7DmTyv5fGQ==, + } + engines: { node: ">=16.0.0" } + peerDependencies: + graphql: "*" + "@graphql-tools/apollo-engine-loader@8.0.22": resolution: { @@ -20537,6 +20576,12 @@ snapshots: graphql: 16.12.0 path-browserify: 1.0.1 + "@graphql-mesh/cross-helpers@0.4.11(graphql@16.12.0)": + dependencies: + "@graphql-tools/utils": 10.11.0(graphql@16.12.0) + graphql: 16.12.0 + path-browserify: 1.0.1 + "@graphql-mesh/fusion-composition@0.8.21(graphql@16.12.0)": dependencies: "@graphql-mesh/utils": 0.104.17(graphql@16.12.0) @@ -20556,7 +20601,7 @@ snapshots: - ioredis - supports-color - "@graphql-mesh/graphql@0.104.18(@types/node@24.10.1)(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10)": + "@graphql-mesh/graphql@0.104.18(@types/node@20.19.25)(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10)": dependencies: "@graphql-mesh/cross-helpers": 0.4.10(graphql@16.12.0) "@graphql-mesh/store": 0.104.18(graphql@16.12.0) @@ -20698,6 +20743,20 @@ snapshots: lodash.get: 4.4.2 tslib: 2.8.1 + "@graphql-mesh/transform-filter-schema@0.104.18(graphql@16.12.0)": + dependencies: + "@graphql-mesh/types": 0.104.18(graphql@16.12.0) + "@graphql-mesh/utils": 0.104.18(graphql@16.12.0) + "@graphql-tools/delegate": 12.0.2(graphql@16.12.0) + "@graphql-tools/utils": 10.11.0(graphql@16.12.0) + "@graphql-tools/wrap": 11.1.2(graphql@16.12.0) + graphql: 16.12.0 + minimatch: 10.1.1 + tslib: 2.8.1 + transitivePeerDependencies: + - "@nats-io/nats-core" + - ioredis + "@graphql-mesh/transform-rename@0.105.18(graphql@16.12.0)": dependencies: "@graphql-mesh/types": 0.104.17(graphql@16.12.0) @@ -20769,6 +20828,21 @@ snapshots: - "@nats-io/nats-core" - ioredis + "@graphql-mesh/types@0.104.18(graphql@16.12.0)": + dependencies: + "@graphql-hive/pubsub": 2.1.1 + "@graphql-tools/batch-delegate": 10.0.8(graphql@16.12.0) + "@graphql-tools/delegate": 12.0.2(graphql@16.12.0) + "@graphql-tools/utils": 10.11.0(graphql@16.12.0) + "@graphql-typed-document-node/core": 3.2.0(graphql@16.12.0) + "@repeaterjs/repeater": 3.0.6 + "@whatwg-node/disposablestack": 0.0.6 + graphql: 16.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - "@nats-io/nats-core" + - ioredis + "@graphql-mesh/utils@0.104.17(graphql@16.12.0)": dependencies: "@envelop/instrumentation": 1.0.0 @@ -20793,6 +20867,30 @@ snapshots: - "@nats-io/nats-core" - ioredis + "@graphql-mesh/utils@0.104.18(graphql@16.12.0)": + dependencies: + "@envelop/instrumentation": 1.0.0 + "@graphql-mesh/cross-helpers": 0.4.11(graphql@16.12.0) + "@graphql-mesh/string-interpolation": 0.5.9(graphql@16.12.0) + "@graphql-mesh/types": 0.104.18(graphql@16.12.0) + "@graphql-tools/batch-delegate": 10.0.8(graphql@16.12.0) + "@graphql-tools/delegate": 12.0.2(graphql@16.12.0) + "@graphql-tools/utils": 10.11.0(graphql@16.12.0) + "@graphql-tools/wrap": 11.1.2(graphql@16.12.0) + "@whatwg-node/disposablestack": 0.0.6 + "@whatwg-node/fetch": 0.10.13 + "@whatwg-node/promise-helpers": 1.3.2 + dset: 3.1.4 + graphql: 16.12.0 + js-yaml: 4.1.1 + lodash.get: 4.4.2 + lodash.topath: 4.5.2 + tiny-lru: 11.4.5 + tslib: 2.8.1 + transitivePeerDependencies: + - "@nats-io/nats-core" + - ioredis + "@graphql-tools/apollo-engine-loader@8.0.22(graphql@16.12.0)": dependencies: "@graphql-tools/utils": 10.9.1(graphql@16.12.0) @@ -20929,7 +21027,7 @@ snapshots: transitivePeerDependencies: - "@types/node" - "@graphql-tools/executor-http@3.0.7(@types/node@24.10.1)(graphql@16.12.0)": + "@graphql-tools/executor-http@3.0.7(@types/node@20.19.25)(graphql@16.12.0)": dependencies: "@graphql-hive/signal": 2.0.0 "@graphql-tools/executor-common": 1.0.5(graphql@16.12.0) @@ -20939,7 +21037,7 @@ snapshots: "@whatwg-node/fetch": 0.10.13 "@whatwg-node/promise-helpers": 1.3.2 graphql: 16.12.0 - meros: 1.3.2(@types/node@24.10.1) + meros: 1.3.2(@types/node@20.19.25) tslib: 2.8.1 transitivePeerDependencies: - "@types/node" @@ -20988,11 +21086,11 @@ snapshots: graphql: 16.12.0 tslib: 2.8.1 - "@graphql-tools/federation@4.2.6(@types/node@24.10.1)(graphql@16.12.0)": + "@graphql-tools/federation@4.2.6(@types/node@20.19.25)(graphql@16.12.0)": dependencies: "@graphql-tools/delegate": 12.0.2(graphql@16.12.0) "@graphql-tools/executor": 1.5.0(graphql@16.12.0) - "@graphql-tools/executor-http": 3.0.7(@types/node@24.10.1)(graphql@16.12.0) + "@graphql-tools/executor-http": 3.0.7(@types/node@20.19.25)(graphql@16.12.0) "@graphql-tools/merge": 9.1.6(graphql@16.12.0) "@graphql-tools/schema": 10.0.30(graphql@16.12.0) "@graphql-tools/stitch": 10.1.6(graphql@16.12.0) @@ -21220,10 +21318,10 @@ snapshots: - uWebSockets.js - utf-8-validate - "@graphql-tools/url-loader@9.0.5(@types/node@24.10.1)(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10)": + "@graphql-tools/url-loader@9.0.5(@types/node@20.19.25)(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10)": dependencies: "@graphql-tools/executor-graphql-ws": 3.1.3(bufferutil@4.0.9)(crossws@0.3.5)(graphql@16.12.0)(utf-8-validate@5.0.10) - "@graphql-tools/executor-http": 3.0.7(@types/node@24.10.1)(graphql@16.12.0) + "@graphql-tools/executor-http": 3.0.7(@types/node@20.19.25)(graphql@16.12.0) "@graphql-tools/executor-legacy-ws": 1.1.24(bufferutil@4.0.9)(graphql@16.12.0)(utf-8-validate@5.0.10) "@graphql-tools/utils": 10.11.0(graphql@16.12.0) "@graphql-tools/wrap": 11.1.2(graphql@16.12.0) @@ -21505,41 +21603,6 @@ snapshots: - supports-color - ts-node - "@jest/core@29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))": - dependencies: - "@jest/console": 29.7.0 - "@jest/reporters": 29.7.0 - "@jest/test-result": 29.7.0 - "@jest/transform": 29.7.0 - "@jest/types": 29.6.3 - "@types/node": 20.19.25 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - "@jest/environment@29.7.0": dependencies: "@jest/fake-timers": 29.7.0 @@ -25865,21 +25928,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): - dependencies: - "@jest/types": 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - "@types/node" - - babel-plugin-macros - - supports-color - - ts-node - create-require@1.1.1: {} cross-fetch@3.2.0: @@ -27941,25 +27989,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): - dependencies: - "@jest/core": 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - "@jest/test-result": 29.7.0 - "@jest/types": 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - "@types/node" - - babel-plugin-macros - - supports-color - - ts-node - jest-config@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)): dependencies: "@babel/core": 7.28.0 @@ -27991,68 +28020,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): - dependencies: - "@babel/core": 7.28.0 - "@jest/test-sequencer": 29.7.0 - "@jest/types": 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - "@types/node": 20.19.25 - ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): - dependencies: - "@babel/core": 7.28.0 - "@jest/test-sequencer": 29.7.0 - "@jest/types": 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - "@types/node": 24.10.1 - ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -28286,18 +28253,6 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): - dependencies: - "@jest/core": 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - "@jest/types": 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - transitivePeerDependencies: - - "@types/node" - - babel-plugin-macros - - supports-color - - ts-node - jiti@1.21.7: {} jiti@2.4.2: {} @@ -28721,9 +28676,9 @@ snapshots: optionalDependencies: "@types/node": 20.19.25 - meros@1.3.2(@types/node@24.10.1): + meros@1.3.2(@types/node@20.19.25): optionalDependencies: - "@types/node": 24.10.1 + "@types/node": 20.19.25 mersenne-twister@1.1.0: {} @@ -30879,26 +30834,6 @@ snapshots: esbuild: 0.25.8 jest-util: 29.7.0 - ts-jest@29.4.6(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)))(typescript@5.9.3): - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 - jest: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.3 - type-fest: 4.41.0 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionalDependencies: - "@babel/core": 7.28.0 - "@jest/transform": 29.7.0 - "@jest/types": 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.0) - jest-util: 29.7.0 - ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.8)(jest-util@29.7.0)(jest@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 @@ -30942,25 +30877,6 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3): - dependencies: - "@cspotcode/source-map-support": 0.8.1 - "@tsconfig/node10": 1.0.11 - "@tsconfig/node12": 1.0.11 - "@tsconfig/node14": 1.0.3 - "@tsconfig/node16": 1.0.4 - "@types/node": 24.10.1 - acorn: 8.15.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.9.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - tsconfck@3.1.6(typescript@5.9.3): optionalDependencies: typescript: 5.9.3