@logswap/sdk 0.5.61 → 0.5.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/history.d.ts CHANGED
@@ -36,6 +36,13 @@ export interface EventLogsQuery {
36
36
  toBlock?: bigint;
37
37
  /** Unix seconds: only events at or after this time. On the chain path it becomes a block search. */
38
38
  since?: number;
39
+ /**
40
+ * Only the newest `latest` events (still returned oldest first) — a pool's tape, not its history.
41
+ * On the chain path the node is asked for the last {@link LATEST_SCAN_BLOCKS} blocks only.
42
+ */
43
+ latest?: number;
44
+ /** Only events from these transactions — e.g. the `RouterCall`s behind a page of routed swaps. */
45
+ transactionHashes?: Hex[];
39
46
  signal?: AbortSignal;
40
47
  }
41
48
  /**
@@ -50,3 +57,5 @@ export declare function tableFor(c: LogswapClient, address: Address, event: stri
50
57
  field: string;
51
58
  type: string;
52
59
  };
60
+ /** How far back the chain path looks for a `latest` tape: a tape is recent by definition. */
61
+ export declare const LATEST_SCAN_BLOCKS = 5000n;
package/dist/history.js CHANGED
@@ -97,9 +97,19 @@ async function fromIndexer(c, q) {
97
97
  filter.blockNumber = range;
98
98
  if (q.since !== undefined)
99
99
  filter.blockTimestamp = { greaterThanOrEqualTo: new Date(q.since * 1000).toISOString() };
100
+ if (q.transactionHashes)
101
+ filter.txHash = { in: q.transactionHashes.map((h) => h.toLowerCase()) };
102
+ // a tape is one page from the head, newest first; history is every page from the start
103
+ const pageSize = q.latest ?? PAGE;
104
+ const order = q.latest !== undefined ? "[BLOCK_NUMBER_DESC, LOG_INDEX_DESC]" : "PRIMARY_KEY_ASC";
100
105
  const selection = [...inputs.map((i) => column(i.name)), "contractAddress", "blockNumber", "blockTimestamp", "txHash", "logIndex"].join(" ");
101
- const query = `query History($after: Cursor, $condition: ${type}Condition, $filter: ${type}Filter) {
102
- ${field}(first: ${PAGE}, after: $after, orderBy: PRIMARY_KEY_ASC, condition: $condition, filter: $filter) {
106
+ // postgraphile refuses a null or an empty filter, so each argument appears only when it has content
107
+ const hasCondition = Object.keys(condition).length > 0;
108
+ const hasFilter = Object.keys(filter).length > 0;
109
+ const decl = `$after: Cursor${hasCondition ? `, $condition: ${type}Condition` : ""}${hasFilter ? `, $filter: ${type}Filter` : ""}`;
110
+ const argsOf = `first: ${pageSize}, after: $after, orderBy: ${order}${hasCondition ? ", condition: $condition" : ""}${hasFilter ? ", filter: $filter" : ""}`;
111
+ const query = `query History(${decl}) {
112
+ ${field}(${argsOf}) {
103
113
  pageInfo { hasNextPage endCursor } nodes { ${selection} }
104
114
  }
105
115
  }`;
@@ -108,10 +118,10 @@ async function fromIndexer(c, q) {
108
118
  for (let i = 0;; i++) {
109
119
  if (i === MAX_PAGES)
110
120
  throw new Error(`history: ${field} has more than ${PAGE * MAX_PAGES} rows`);
111
- const d = await gql(q.indexer, query, { after, condition: Object.keys(condition).length ? condition : null, filter: Object.keys(filter).length ? filter : null }, q.signal);
121
+ const d = await gql(q.indexer, query, { after, ...(hasCondition ? { condition } : {}), ...(hasFilter ? { filter } : {}) }, q.signal);
112
122
  const page = d[field];
113
123
  rows.push(...page.nodes);
114
- if (!page.pageInfo.hasNextPage || !page.pageInfo.endCursor)
124
+ if (q.latest !== undefined || !page.pageInfo.hasNextPage || !page.pageInfo.endCursor)
115
125
  break;
116
126
  after = page.pageInfo.endCursor;
117
127
  }
@@ -130,12 +140,22 @@ async function fromIndexer(c, q) {
130
140
  .sort((a, b) => (a.blockNumber === b.blockNumber ? a.logIndex - b.logIndex : a.blockNumber < b.blockNumber ? -1 : 1));
131
141
  }
132
142
  // ─── the chain path ──────────────────────────────────────────────────────────
143
+ /** How far back the chain path looks for a `latest` tape: a tape is recent by definition. */
144
+ export const LATEST_SCAN_BLOCKS = 5000n;
133
145
  async function fromChain(c, q) {
134
146
  let fromBlock = q.fromBlock ?? 0n;
135
147
  const toBlock = q.toBlock ?? (await c.public.getBlockNumber());
136
148
  if (q.since !== undefined)
137
149
  fromBlock = await blockAtOrAfter(c, BigInt(q.since), fromBlock, toBlock);
138
- const logs = await c.public.getLogs({ address: q.address, event: q.event, args: q.args, fromBlock, toBlock });
150
+ if (q.latest !== undefined && toBlock - fromBlock > LATEST_SCAN_BLOCKS)
151
+ fromBlock = toBlock - LATEST_SCAN_BLOCKS;
152
+ let logs = (await c.public.getLogs({ address: q.address, event: q.event, args: q.args, fromBlock, toBlock }));
153
+ if (q.transactionHashes) {
154
+ const want = new Set(q.transactionHashes.map((h) => h.toLowerCase()));
155
+ logs = logs.filter((l) => want.has(l.transactionHash.toLowerCase()));
156
+ }
157
+ if (q.latest !== undefined)
158
+ logs = logs.slice(-q.latest);
139
159
  return logs.map((l) => ({
140
160
  address: q.address,
141
161
  args: l.args,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logswap/sdk",
3
- "version": "0.5.61",
3
+ "version": "0.5.62",
4
4
  "description": "TypeScript SDK for Logswap, the liquidity primitive introducing the logarithmic invariant — typed wrappers over the CPoolManager / FPoolManager / Router / Lens surface.",
5
5
  "license": "GPL-2.0-or-later",
6
6
  "type": "module",
package/src/history.ts CHANGED
@@ -40,6 +40,13 @@ export interface EventLogsQuery {
40
40
  toBlock?: bigint;
41
41
  /** Unix seconds: only events at or after this time. On the chain path it becomes a block search. */
42
42
  since?: number;
43
+ /**
44
+ * Only the newest `latest` events (still returned oldest first) — a pool's tape, not its history.
45
+ * On the chain path the node is asked for the last {@link LATEST_SCAN_BLOCKS} blocks only.
46
+ */
47
+ latest?: number;
48
+ /** Only events from these transactions — e.g. the `RouterCall`s behind a page of routed swaps. */
49
+ transactionHashes?: Hex[];
43
50
  signal?: AbortSignal;
44
51
  }
45
52
 
@@ -119,10 +126,19 @@ async function fromIndexer<A>(c: LogswapClient, q: EventLogsQuery): Promise<Hist
119
126
  if (q.toBlock !== undefined) range.lessThanOrEqualTo = q.toBlock.toString();
120
127
  if (Object.keys(range).length) filter.blockNumber = range;
121
128
  if (q.since !== undefined) filter.blockTimestamp = { greaterThanOrEqualTo: new Date(q.since * 1000).toISOString() };
129
+ if (q.transactionHashes) filter.txHash = { in: q.transactionHashes.map((h) => h.toLowerCase()) };
130
+ // a tape is one page from the head, newest first; history is every page from the start
131
+ const pageSize = q.latest ?? PAGE;
132
+ const order = q.latest !== undefined ? "[BLOCK_NUMBER_DESC, LOG_INDEX_DESC]" : "PRIMARY_KEY_ASC";
122
133
 
123
134
  const selection = [...inputs.map((i) => column(i.name!)), "contractAddress", "blockNumber", "blockTimestamp", "txHash", "logIndex"].join(" ");
124
- const query = `query History($after: Cursor, $condition: ${type}Condition, $filter: ${type}Filter) {
125
- ${field}(first: ${PAGE}, after: $after, orderBy: PRIMARY_KEY_ASC, condition: $condition, filter: $filter) {
135
+ // postgraphile refuses a null or an empty filter, so each argument appears only when it has content
136
+ const hasCondition = Object.keys(condition).length > 0;
137
+ const hasFilter = Object.keys(filter).length > 0;
138
+ const decl = `$after: Cursor${hasCondition ? `, $condition: ${type}Condition` : ""}${hasFilter ? `, $filter: ${type}Filter` : ""}`;
139
+ const argsOf = `first: ${pageSize}, after: $after, orderBy: ${order}${hasCondition ? ", condition: $condition" : ""}${hasFilter ? ", filter: $filter" : ""}`;
140
+ const query = `query History(${decl}) {
141
+ ${field}(${argsOf}) {
126
142
  pageInfo { hasNextPage endCursor } nodes { ${selection} }
127
143
  }
128
144
  }`;
@@ -135,12 +151,12 @@ async function fromIndexer<A>(c: LogswapClient, q: EventLogsQuery): Promise<Hist
135
151
  const d: Record<string, Conn> = await gql<Record<string, Conn>>(
136
152
  q.indexer!,
137
153
  query,
138
- { after, condition: Object.keys(condition).length ? condition : null, filter: Object.keys(filter).length ? filter : null },
154
+ { after, ...(hasCondition ? { condition } : {}), ...(hasFilter ? { filter } : {}) },
139
155
  q.signal,
140
156
  );
141
157
  const page: Conn = d[field]!;
142
158
  rows.push(...page.nodes);
143
- if (!page.pageInfo.hasNextPage || !page.pageInfo.endCursor) break;
159
+ if (q.latest !== undefined || !page.pageInfo.hasNextPage || !page.pageInfo.endCursor) break;
144
160
  after = page.pageInfo.endCursor;
145
161
  }
146
162
  // a table can hold a previous deployment's rows (an indexer not yet reset): keep this contract's
@@ -160,12 +176,26 @@ async function fromIndexer<A>(c: LogswapClient, q: EventLogsQuery): Promise<Hist
160
176
 
161
177
  // ─── the chain path ──────────────────────────────────────────────────────────
162
178
 
179
+ /** How far back the chain path looks for a `latest` tape: a tape is recent by definition. */
180
+ export const LATEST_SCAN_BLOCKS = 5_000n;
181
+
163
182
  async function fromChain<A>(c: LogswapClient, q: EventLogsQuery): Promise<HistoryLog<A>[]> {
164
183
  let fromBlock = q.fromBlock ?? 0n;
165
184
  const toBlock = q.toBlock ?? (await c.public.getBlockNumber());
166
185
  if (q.since !== undefined) fromBlock = await blockAtOrAfter(c, BigInt(q.since), fromBlock, toBlock);
167
- const logs = await c.public.getLogs({ address: q.address, event: q.event, args: q.args, fromBlock, toBlock } as never);
168
- return (logs as unknown as Array<{ args: A; blockNumber: bigint; transactionHash: Hex; logIndex: number }>).map((l) => ({
186
+ if (q.latest !== undefined && toBlock - fromBlock > LATEST_SCAN_BLOCKS) fromBlock = toBlock - LATEST_SCAN_BLOCKS;
187
+ let logs = (await c.public.getLogs({ address: q.address, event: q.event, args: q.args, fromBlock, toBlock } as never)) as unknown as Array<{
188
+ args: A;
189
+ blockNumber: bigint;
190
+ transactionHash: Hex;
191
+ logIndex: number;
192
+ }>;
193
+ if (q.transactionHashes) {
194
+ const want = new Set(q.transactionHashes.map((h) => h.toLowerCase()));
195
+ logs = logs.filter((l) => want.has(l.transactionHash.toLowerCase()));
196
+ }
197
+ if (q.latest !== undefined) logs = logs.slice(-q.latest);
198
+ return logs.map((l) => ({
169
199
  address: q.address,
170
200
  args: l.args,
171
201
  blockNumber: l.blockNumber,