import { type Context } from 'hono'; import { Hex } from 'ox'; import * as z from 'zod/mini'; import type * as App from '../../../App.js'; import * as Response from '../../../internal/Response.js'; import * as Schema from '../../../internal/Schema.js'; import * as Tidx from '../../../internal/Tidx.js'; import * as Valuation from './valuation.js'; export declare const signature = "event Transfer(address indexed from, address indexed to, uint256 value)"; /** * Half-open `[start, end)` bounds of the deterministic TIP-20 contract range. * Full-width so the values compare correctly as PostgreSQL bytea, ClickHouse * hex strings, and lowercased JavaScript strings. */ export declare const tip20AddressRange: readonly ['0x20c0000000000000000000000000000000000000', '0x20c1000000000000000000000000000000000000']; /** Zod schemas owned by the transfer handlers. */ export declare namespace schema { /** A transfer token reference with RPC metadata. Inlined to avoid the `transfers`/`tokens` module cycle. */ const SourceToken: z.ZodMiniObject<{ address: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; logoUri: z.ZodMiniOptional>; name: z.ZodMiniString; symbol: z.ZodMiniString; verified: z.ZodMiniOptional>; }, z.core.$strip>; /** * Filter predicates shared verbatim by the `GET /transfers` list query * ({@link getTransfers.Query}, which spreads `Predicates.shape`) and `transfer` * webhook subscriptions (`Webhooks.schema.TransferFilters`), so the two never * drift. Read-only concerns (pagination, windowing, ordering, `include`, * `chainId`) are *not* here — they live only on the list query and have no * meaning for an open-ended subscription. Consumers that need different * per-field docs (e.g. the list query's mutually-exclusive `address`) override * that one field. */ const Predicates: z.ZodMiniObject<{ address: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; recipient: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; sender: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; token: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; }, z.core.$strip>; /** Schemas for the getTransfers (list) operation. */ namespace getTransfers { /** Optional related resources that callers opt into via `include`. */ const Include: z.ZodMiniEnum<{ crossToken: "crossToken"; memo: "memo"; "token.logoUri": "token.logoUri"; "token.verified": "token.verified"; totalCount: "totalCount"; }>; /** * Parses a comma-separated `include` query value into a list of optional * resources to embed. Extra lookups run only when their resource is * requested, keeping the base transfer page fast. */ const includeQuery: z.ZodMiniPipe, z.ZodMiniTransform>, z.ZodMiniArray>>; /** * Query parameters for top-level transfer list requests. `address` is a * convenience for "either side" — passing it alongside an explicit * `sender` or `recipient` is rejected as ambiguous (the helper would have * to AND/OR them in a non-obvious way). */ const Query: z.ZodMiniObject<{ recipient: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; sender: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; token: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; address: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>>; 'blockNumber.from': z.ZodMiniOptional>; 'blockNumber.to': z.ZodMiniOptional>; chainId: z.ZodMiniOptional, z.ZodMiniTransform<4217 | 42431, "mainnet" | "testnet">>, z.ZodMiniPipe, z.ZodMiniTransform>]>, z.ZodMiniNumber>>; cursor: z.ZodMiniOptional>; include: z.ZodMiniPipe, z.ZodMiniTransform>, z.ZodMiniArray>>; limit: z.ZodMiniDefault, z.ZodMiniTransform>>; order: z.ZodMiniDefault>; page: z.ZodMiniOptional>; 'timestamp.from': z.ZodMiniOptional>>; 'timestamp.to': z.ZodMiniOptional>>; 'valuation.currency': z.ZodMiniOptional, z.ZodMiniTransform>>; }, z.core.$strict>; /** * A single transfer row. The token contract is carried per-row via * `sourceToken.address` because `/transfers` is unscoped and rows can span * multiple contracts. */ const Transfer: z.ZodMiniObject<{ attribution: z.ZodMiniOptional>; blockNumber: z.ZodMiniNumber; destinationAmount: z.ZodMiniOptional; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; formatted: z.ZodMiniString; valuation: z.ZodMiniOptional; currency: z.ZodMiniString; }, z.core.$strip>>>; }, z.core.$strip>>; destinationToken: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; logoUri: z.ZodMiniOptional>; name: z.ZodMiniString; symbol: z.ZodMiniString; verified: z.ZodMiniOptional>; }, z.core.$strip>>; id: z.ZodMiniString; logIndex: z.ZodMiniNumber; memo: z.ZodMiniOptional>; recipient: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; sender: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; sourceAmount: z.ZodMiniObject<{ baseUnits: z.ZodMiniString; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; formatted: z.ZodMiniString; valuation: z.ZodMiniOptional; currency: z.ZodMiniString; }, z.core.$strip>>>; }, z.core.$strip>; sourceToken: z.ZodMiniObject<{ address: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; logoUri: z.ZodMiniOptional>; name: z.ZodMiniString; symbol: z.ZodMiniString; verified: z.ZodMiniOptional>; }, z.core.$strip>; timestamp: z.ZodMiniISODateTime; transactionHash: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; }, z.core.$strip>; /** Page-level resources: opt-in counts plus valuation rate provenance. */ const Meta: z.ZodMiniObject<{ valuation: z.ZodMiniOptional; source: z.ZodMiniString; }, z.core.$strip>>; totalCount: z.ZodMiniOptional>; totalCountCapped: z.ZodMiniOptional>; }, z.core.$strip>; /** Page of TIP-20 token transfers across the chain, ordered by block then log index. */ const Response: z.ZodMiniObject<{ data: z.ZodMiniArray>; blockNumber: z.ZodMiniNumber; destinationAmount: z.ZodMiniOptional; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; formatted: z.ZodMiniString; valuation: z.ZodMiniOptional; currency: z.ZodMiniString; }, z.core.$strip>>>; }, z.core.$strip>>; destinationToken: z.ZodMiniOptional, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; logoUri: z.ZodMiniOptional>; name: z.ZodMiniString; symbol: z.ZodMiniString; verified: z.ZodMiniOptional>; }, z.core.$strip>>; id: z.ZodMiniString; logIndex: z.ZodMiniNumber; memo: z.ZodMiniOptional>; recipient: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; sender: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; sourceAmount: z.ZodMiniObject<{ baseUnits: z.ZodMiniString; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; formatted: z.ZodMiniString; valuation: z.ZodMiniOptional; currency: z.ZodMiniString; }, z.core.$strip>>>; }, z.core.$strip>; sourceToken: z.ZodMiniObject<{ address: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; currency: z.ZodMiniString; decimals: z.ZodMiniNumber; logoUri: z.ZodMiniOptional>; name: z.ZodMiniString; symbol: z.ZodMiniString; verified: z.ZodMiniOptional>; }, z.core.$strip>; timestamp: z.ZodMiniISODateTime; transactionHash: z.ZodMiniPipe, z.ZodMiniTransform<`0x${string}`, `0x${string}`>>; }, z.core.$strip>>; meta: z.ZodMiniOptional; source: z.ZodMiniString; }, z.core.$strip>>; totalCount: z.ZodMiniOptional>; totalCountCapped: z.ZodMiniOptional>; }, z.core.$strip>>; nextCursor: z.ZodMiniNullable>; }, z.core.$strip>; } } /** Creates transfer handlers. */ export declare function transfers(options?: transfers.Options): import("hono/hono-base").HonoBase; export declare namespace transfers { /** Options for the transfer handlers. */ type Options = { /** FX configuration backing leg valuation. */ fx?: Valuation.addresses.Fx | undefined; }; } /** * Shared TIP-20 `Transfer` page query. Used by `GET /transfers` and the * webhook poller. Builds signature-decoded `Transfer` CTE queries, applies * opaque keyset pagination on `(block_num, log_idx)`, and decodes rows into a * strongly-typed intermediate shape that each caller maps to its own public * response. * * `eitherSide` is exposed alongside one-sided `sender`/`recipient` filters * because it requires two upstream scans. The helper owns their ordered merge. * * Memoization lives inside the helper under one canonical `transfers:v1:…` * key derived from every filter — so equivalent filter combinations share a * single cache entry. Callers supply only a `timing` label for Server-Timing. */ export declare function query(c: Context, options: query.Options): Promise<{ data: query.Row[]; nextCursor: string | null; }>; export declare namespace query { /** Options for the shared TIP-20 `Transfer` page query. */ type Options = { chainId: z.output; /** Opaque keyset cursor anchoring the page; omit for the head page. */ cursor?: string | undefined; /** Restrict to rows where this address appears on either side. */ eitherSide?: z.output | undefined; fromBlock?: number | undefined; fromTimestamp?: string | undefined; limit: number; /** 1-indexed page number (positional pagination; exclusive with `cursor`). */ page?: number | undefined; order: 'asc' | 'desc'; recipient?: z.output | undefined; sender?: z.output | undefined; /** Server-Timing label for the caller's surface. */ timing: string; toBlock?: number | undefined; toTimestamp?: string | undefined; /** Restrict to a single TIP-20 token contract. */ token?: z.output | undefined; }; /** A decoded `Transfer` row before per-surface shaping. */ type Row = { /** Token contract that emitted the event. */ address: z.output; amount: string; blockNumber: number; logIndex: number; recipient: z.output; sender: z.output; timestamp: string; transactionHash: z.output; }; type Page = { data: readonly Row[]; nextCursor: string | null; }; } /** * Context-free core of {@link query}: reads token-scoped and global rows from * `token_transfers`, otherwise the decoded `Transfer` CTE, then paginates and * decodes them. Also used by the webhook poller. */ export declare function scan(deps: scan.Deps, options: scan.Options): Promise; /** * Collates per-side transfer streams into one ordered page window: split and * sampled streams are deduped, globally ordered, and paged app-side, while a * lone engine-paged stream passes through. Ordered capped streams may carry a * safe raw continuation; unordered samples cannot. */ export declare function collate(fetched: readonly collate.Stream[], options: collate.Options): collate.Result; export declare namespace collate { /** One fetched transfer stream. */ type Stream = { /** Whether upstream bounds truncated this stream. */ capped: boolean; /** Raw TIDX rows. */ rows: readonly Record[]; /** Ordered raw position and unmatched positional offset after a bounded scan. */ resume?: Resume | undefined; /** Whether the rows are an unordered, unpaged capped fallback sample. */ sample: boolean; }; /** Progress retained after a bounded ordered scan. */ type Resume = { /** Filtered rows that a positional continuation still needs to skip. */ offset: number; /** Last ordered position safely inspected by the bounded scan. */ position: readonly [blockNumber: number, logIndex: number]; }; /** The page window applied to the merged stream. */ type Options = { /** Maximum merged rows to return. */ limit: number; /** Number of globally ordered rows to skip. */ offset: number; /** Global transfer ordering. */ order: 'asc' | 'desc'; }; /** The collated page window. */ type Result = { /** Whether an upstream work bound truncated any stream. */ capped: boolean; /** The ordered page window rows. */ rows: readonly Record[]; /** Ordered continuation state, absent for unordered capped samples. */ resume?: Resume | undefined; }; } export declare namespace scan { /** Dependencies for {@link scan}. */ type Deps = { /** TIDX query client for the target chain. */ tidx: Tidx.Client; }; /** Options for {@link scan} (everything {@link query.Options} has except `timing`). */ type Options = Omit; /** A decoded `Transfer` row plus its `(block_num, log_idx)` cursor. */ type Item = { /** Keyset position of the row. */ cursor: readonly [blockNumber: number, logIndex: number]; /** The decoded row before per-surface shaping. */ data: query.Row; }; /** A page of scanned `Transfer` rows. */ type Page = { /** Whether an upstream work bound truncated the page. */ capped: boolean; hasMore: boolean; items: readonly Item[]; limit: number; nextCursor: string | null; /** Last ordered position safely inspected, when a capped scan can resume. */ resumeCursor?: readonly [blockNumber: number, logIndex: number] | undefined; }; } /** * Builds shared transfer filters. Token-scoped queries target `token_transfers`; * unscoped queries constrain the decoded `Transfer` emitter to TIP-20 contracts. */ declare function filterClauses(options: filterClauses.Options): string[]; declare namespace filterClauses { /** The non-pagination filter fields of {@link scan.Options}. */ type Options = Pick; } /** * Capped total-row count for the `Transfer` feed, sharing {@link query}'s * filters (minus pagination) so the count matches the page it annotates. Timed * and memoized like {@link query}; the result feeds `meta.totalCount`/`totalCountCapped` * when a caller opts in via `include=totalCount`. */ export declare function count(c: Context, options: count.Options): Promise; export declare namespace count { /** Options for the capped `Transfer` count: {@link query}'s filters plus `timing`. */ type Options = Pick & { /** Count folded cross-token transfers instead of raw `Transfer` logs. */ crossToken?: boolean | undefined; }; /** A capped count result: `totalCountCapped` is `true` when the count hit `Schema.countCap`. */ type Result = { totalCountCapped: boolean; totalCount: number; }; } /** * Context-free core of {@link count}: counts token-scoped rows from * `token_transfers`, otherwise the decoded `Transfer` CTE. The capped inner * scan bounds work while preserving each source's established query engine. */ export declare function countScan(deps: scan.Deps, options: countScan.Options): Promise; export declare namespace countScan { /** Options for {@link countScan}: the shared filters plus the target chain. */ type Options = filterClauses.Options & Pick & { /** Count folded cross-token transfers instead of raw `Transfer` logs. */ crossToken?: boolean | undefined; }; } /** * Folds a capped `Transfer` scan into the cross-token count. Classified * bundles subtract their folded-away legs; a cap-truncated sample can hold * partial bundles that defeat classification, so it subtracts every DEX and * fee leg instead, keeping the reported lower bound from overcounting. */ export declare function foldCount(rows: readonly Record[], options: foldCount.Options): count.Result; export declare namespace foldCount { /** Options for {@link foldCount}. */ type Options = { /** Whether the rows include a cap-truncated fallback sample. */ capped: boolean; /** Complete transaction legs used to classify the filtered rows. */ classificationRows?: readonly Record[] | undefined; }; } /** * Attempts to decode a bytes32 memo into a human-readable string. MPP * attribution memos encode a service fingerprint, not text — they are * suppressed from the display memo (and resolved to `attribution` instead). * Shared with the activity feed's transfer entries. */ export declare function memoToString(memo: Hex.Hex): string | undefined; export {}; //# sourceMappingURL=transfers.d.ts.map