Skip to content

Commit 0485401

Browse files
committed
Creating a proper type for ots_traceTransaction
Actually utilizing the OtsAddressTransactionsPage type for searchTransactionsBefore/After Addressing 2 notes from Otterscan maintainer: the page numbers will be ascending/descending based on search direction, and updating the docstrings to note the pageSize limit is a soft limit
1 parent 26617c6 commit 0485401

File tree

4 files changed

+80
-28
lines changed

4 files changed

+80
-28
lines changed

src.ts/_tests/test-providers-otterscan.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
OtsBlockDetails,
88
OtsBlockTransactionsPage,
99
OtsAddressTransactionsPage,
10+
OtsTraceEntry,
1011
OtsContractCreator
1112
} from "../providers/provider-otterscan.js";
1213

@@ -44,7 +45,32 @@ describe("Test Otterscan Provider", function () {
4445
result = "0x";
4546
break;
4647
case "ots_traceTransaction":
47-
result = { calls: [] };
48+
result = [
49+
{
50+
type: "CALL",
51+
depth: 0,
52+
from: "0x737d16748aa3f93d6ff1b0aefa3eca7fffca868e",
53+
to: "0x545ec8c956d307cc3bf7f9ba1e413217eff1bc7a",
54+
value: "0x0",
55+
input: "0xff02000000000000001596d80c86b939000000000000000019cfaf37a98833fed61000a72a288205ead800f1978fc2d943013f0e9f56d3a1077a294dde1b09bb078844df40758a5d0f9a27017ed0000000011e00000300000191ccf22538c30f09d14be27569c5bdb61f99b3c9f33c0bb40d1bbf6eafaaea2adfb7d2d3ebc1e49c01857e0000000000000000"
56+
},
57+
{
58+
type: "DELEGATECALL",
59+
depth: 1,
60+
from: "0x545ec8c956d307cc3bf7f9ba1e413217eff1bc7a",
61+
to: "0x998f7f745f61a910da86d8aa65db60b67a40da6d",
62+
value: null,
63+
input: "0x5697217300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000036a72a288205ead800f1978fc2d943013f0e9f56d3a1077a294dde1b09bb078844df40758a5d0f9a27017ed0000000011e000003000001"
64+
},
65+
{
66+
type: "STATICCALL",
67+
depth: 2,
68+
from: "0x545ec8c956d307cc3bf7f9ba1e413217eff1bc7a",
69+
to: "0x91ccf22538c30f09d14be27569c5bdb61f99b3c9",
70+
value: null,
71+
input: "0x0902f1ac"
72+
}
73+
];
4874
break;
4975
case "ots_getBlockDetails":
5076
result = {
@@ -221,8 +247,24 @@ describe("Test Otterscan Provider", function () {
221247
it("should trace transaction", async function () {
222248
const provider = createMockOtsProvider();
223249
const trace = await provider.traceTransaction("0x123");
224-
assert(typeof trace === "object", "should return trace object");
225-
assert(Array.isArray(trace.calls), "should have calls array");
250+
assert(Array.isArray(trace), "should return array of trace entries");
251+
assert.strictEqual(trace.length, 3, "should have three trace entries");
252+
253+
const firstEntry = trace[0];
254+
assert.strictEqual(firstEntry.type, "CALL", "first entry should be CALL");
255+
assert.strictEqual(firstEntry.depth, 0, "first entry should have depth 0");
256+
assert.strictEqual(firstEntry.from, "0x737d16748aa3f93d6ff1b0aefa3eca7fffca868e", "should have correct from");
257+
assert.strictEqual(firstEntry.to, "0x545ec8c956d307cc3bf7f9ba1e413217eff1bc7a", "should have correct to");
258+
assert.strictEqual(firstEntry.value, "0x0", "should have correct value");
259+
assert(firstEntry.input?.startsWith("0xff02"), "should have input data");
260+
261+
const delegateCall = trace[1];
262+
assert.strictEqual(delegateCall.type, "DELEGATECALL", "second entry should be DELEGATECALL");
263+
assert.strictEqual(delegateCall.value, null, "delegatecall should have null value");
264+
265+
const staticCall = trace[2];
266+
assert.strictEqual(staticCall.type, "STATICCALL", "third entry should be STATICCALL");
267+
assert.strictEqual(staticCall.value, null, "staticcall should have null value");
226268
});
227269

228270
it("should get block details", async function () {
@@ -319,6 +361,7 @@ describe("Test Otterscan Provider", function () {
319361
const blockDetails: OtsBlockDetails = await provider.getBlockDetails(4096);
320362
const blockTxs: OtsBlockTransactionsPage = await provider.getBlockTransactions(4096, 0, 10);
321363
const searchResults: OtsAddressTransactionsPage = await provider.searchTransactionsBefore("0x123", 4096, 10);
364+
const traceEntries: OtsTraceEntry[] = await provider.traceTransaction("0x123");
322365
const creator: OtsContractCreator | null = await provider.getContractCreator("0x123");
323366

324367
// Basic type assertions
@@ -328,6 +371,7 @@ describe("Test Otterscan Provider", function () {
328371
assert(typeof blockDetails === "object");
329372
assert(typeof blockTxs === "object" && Array.isArray(blockTxs.transactions));
330373
assert(typeof searchResults === "object" && Array.isArray(searchResults.txs));
374+
assert(Array.isArray(traceEntries));
331375
assert(creator === null || typeof creator === "object");
332376
});
333377
});

src.ts/ethers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export type {
180180
MinedBlock, MinedTransactionResponse, Networkish, OrphanFilter,
181181
OtsTransactionReceiptParams, OtsBlockTransactionReceipt,
182182
OtsBlockParams, OtsInternalOp, OtsBlockDetails, OtsBlockTransactionsPage,
183-
OtsAddressTransactionsPage, OtsContractCreator,
183+
OtsAddressTransactionsPage, OtsTraceEntry, OtsContractCreator,
184184
PerformActionFilter, PerformActionRequest, PerformActionTransaction,
185185
PreparedTransactionRequest, ProviderEvent, Subscriber, Subscription,
186186
TopicFilter, TransactionReceiptParams, TransactionRequest,

src.ts/providers/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export { OtterscanProvider } from "./provider-otterscan.js";
6262
export type {
6363
OtsTransactionReceiptParams, OtsBlockTransactionReceipt,
6464
OtsBlockParams, OtsInternalOp, OtsBlockDetails, OtsBlockTransactionsPage,
65-
OtsAddressTransactionsPage, OtsContractCreator
65+
OtsAddressTransactionsPage, OtsTraceEntry, OtsContractCreator
6666
} from "./provider-otterscan.js";
6767

6868
export { BrowserProvider } from "./provider-browser.js";

src.ts/providers/provider-otterscan.ts

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,24 @@ export interface OtsAddressTransactionsPage {
9999
lastPage: boolean;
100100
}
101101

102+
/**
103+
* Trace entry from ots_traceTransaction
104+
*/
105+
export interface OtsTraceEntry {
106+
/** Type of operation (CALL, DELEGATECALL, STATICCALL, CREATE, etc.) */
107+
type: string;
108+
/** Call depth in the execution stack */
109+
depth: number;
110+
/** Source address */
111+
from: string;
112+
/** Target address */
113+
to: string;
114+
/** Value transferred (hex string, null for delegate/static calls) */
115+
value: string | null;
116+
/** Input data for the call */
117+
input?: string;
118+
}
119+
102120
/**
103121
* Contract creator information
104122
*/
@@ -212,9 +230,9 @@ export class OtterscanProvider extends JsonRpcProvider {
212230
/**
213231
* Get execution trace for a transaction
214232
* @param txHash - Transaction hash
215-
* @returns Trace data
233+
* @returns Array of trace entries showing call execution flow
216234
*/
217-
async traceTransaction(txHash: string): Promise<any> {
235+
async traceTransaction(txHash: string): Promise<OtsTraceEntry[]> {
218236
return await this.send("ots_traceTransaction", [txHash]);
219237
}
220238

@@ -243,7 +261,7 @@ export class OtterscanProvider extends JsonRpcProvider {
243261
* Removes verbose fields like logs from receipts to save bandwidth
244262
* @param blockNumber - Block number
245263
* @param page - Page number (0-based)
246-
* @param pageSize - Number of transactions per page
264+
* @param pageSize - Soft limit on transactions per page (actual results may exceed this if a block contains more transactions)
247265
* @returns Page of transactions and receipts (with logs removed)
248266
*/
249267
async getBlockTransactions(blockNumber: number, page: number, pageSize: number): Promise<OtsBlockTransactionsPage> {
@@ -255,24 +273,19 @@ export class OtterscanProvider extends JsonRpcProvider {
255273
* Provides paginated transaction history with in-node search (no external indexer needed)
256274
* @param address - Address to search for
257275
* @param blockNumber - Starting block number
258-
* @param pageSize - Maximum results to return
276+
* @param pageSize - Soft limit on results to return (actual results may exceed this if a block contains more transactions)
259277
* @returns Page of transactions and receipts
260278
*/
261279
async searchTransactionsBefore(
262280
address: string,
263281
blockNumber: number,
264282
pageSize: number
265283
): Promise<OtsAddressTransactionsPage> {
266-
const result = (await this.send("ots_searchTransactionsBefore", [address, blockNumber, pageSize])) as {
267-
txs: any[];
268-
receipts: any[];
269-
firstPage: boolean;
270-
lastPage: boolean;
271-
};
284+
const result = await this.send("ots_searchTransactionsBefore", [address, blockNumber, pageSize]) as OtsAddressTransactionsPage;
272285
return {
273286
...result,
274-
txs: result.txs.map((tx: any) => formatTransactionResponse(tx)),
275-
receipts: result.receipts.map((receipt: any) => ({
287+
txs: result.txs.map(tx => formatTransactionResponse(tx)),
288+
receipts: result.receipts.map(receipt => ({
276289
...formatTransactionReceipt(receipt),
277290
timestamp: receipt.timestamp
278291
}))
@@ -284,24 +297,19 @@ export class OtterscanProvider extends JsonRpcProvider {
284297
* Provides paginated transaction history with in-node search (no external indexer needed)
285298
* @param address - Address to search for
286299
* @param blockNumber - Starting block number
287-
* @param pageSize - Maximum results to return
300+
* @param pageSize - Soft limit on results to return (actual results may exceed this if a block contains more transactions)
288301
* @returns Page of transactions and receipts
289302
*/
290303
async searchTransactionsAfter(
291304
address: string,
292305
blockNumber: number,
293306
pageSize: number
294307
): Promise<OtsAddressTransactionsPage> {
295-
const result = (await this.send("ots_searchTransactionsAfter", [address, blockNumber, pageSize])) as {
296-
txs: any[];
297-
receipts: any[];
298-
firstPage: boolean;
299-
lastPage: boolean;
300-
};
308+
const result = await this.send("ots_searchTransactionsAfter", [address, blockNumber, pageSize]) as OtsAddressTransactionsPage;
301309
return {
302310
...result,
303-
txs: result.txs.map((tx: any) => formatTransactionResponse(tx)),
304-
receipts: result.receipts.map((receipt: any) => ({
311+
txs: result.txs.map(tx => formatTransactionResponse(tx)),
312+
receipts: result.receipts.map(receipt => ({
305313
...formatTransactionReceipt(receipt),
306314
timestamp: receipt.timestamp
307315
}))
@@ -353,7 +361,7 @@ export class OtterscanProvider extends JsonRpcProvider {
353361
* @param address - Address to search
354362
* @param direction - Search direction ("before" or "after")
355363
* @param startBlock - Starting block number
356-
* @param pageSize - Results per page (default: 500)
364+
* @param pageSize - Soft limit on results per page (default: 500, actual results may exceed this if a block contains more transactions)
357365
* @yields Object with tx and receipt for each transaction
358366
*/
359367
async *iterateAddressHistory(
@@ -379,7 +387,7 @@ export class OtterscanProvider extends JsonRpcProvider {
379387
}
380388

381389
// Check if we've reached the end
382-
if (page.lastPage) break;
390+
if (direction === "before" ? page.lastPage : page.firstPage) break;
383391

384392
// Update block cursor for next iteration
385393
const lastTx = page.txs[page.txs.length - 1];

0 commit comments

Comments
 (0)