7
7
* efficient access to transaction details, internal operations,
8
8
* and paginated transaction history.
9
9
*
10
- * @_section : api/providers/otterscan :Otterscan Provider [about-otterscanProvider ]
10
+ * @_subsection : api/providers/thirdparty :Otterscan [providers-otterscan ]
11
11
*/
12
12
13
13
import { Interface } from "../abi/index.js" ;
@@ -17,8 +17,84 @@ import { JsonRpcProvider } from "./provider-jsonrpc.js";
17
17
import type { JsonRpcApiProviderOptions } from "./provider-jsonrpc.js" ;
18
18
import type { Networkish } from "./network.js" ;
19
19
import type { FetchRequest } from "../utils/index.js" ;
20
+ import type { BlockParams } from "./formatting.js" ;
21
+ import type { Fragment } from "../abi/index.js" ;
22
+ import type { AccessList } from "../transaction/index.js" ;
23
+ import type { HexString } from "../utils/data.js" ;
20
24
21
- export type Hex = `0x${string } `;
25
+ // Log entry in transaction receipts - matches ethers LogParams but with hex strings from raw RPC
26
+ export interface OtsLog {
27
+ address : string ;
28
+ topics : ReadonlyArray < string > ;
29
+ data : string ;
30
+ blockNumber : string ; // hex string from RPC, not parsed number
31
+ transactionHash : string ;
32
+ transactionIndex : string ; // hex string from RPC, not parsed number
33
+ blockHash : string ;
34
+ logIndex : string ; // hex string from RPC, not parsed number
35
+ removed : boolean ;
36
+ }
37
+
38
+ // Otterscan transaction type - raw RPC response with hex-encoded values
39
+ export interface OtsTransaction {
40
+ // Core transaction fields (always present)
41
+ blockHash : string ;
42
+ blockNumber : string ; // hex-encoded number
43
+ from : string ;
44
+ gas : string ; // hex-encoded gasLimit
45
+ gasPrice : string ; // hex-encoded bigint
46
+ hash : string ;
47
+ input : string ; // transaction data
48
+ nonce : string ; // hex-encoded number
49
+ to : string ;
50
+ transactionIndex : string ; // hex-encoded number
51
+ value : string ; // hex-encoded bigint
52
+ type : string ; // hex-encoded transaction type (0x0, 0x1, 0x2, etc.)
53
+ chainId : string ; // hex-encoded bigint
54
+
55
+ // Signature components (always present)
56
+ v : string ; // hex-encoded
57
+ r : string ; // hex signature component
58
+ s : string ; // hex signature component
59
+
60
+ // EIP-1559 fields (present in type 0x2 transactions)
61
+ maxPriorityFeePerGas ?: string ; // hex-encoded bigint
62
+ maxFeePerGas ?: string ; // hex-encoded bigint
63
+ yParity ?: string ; // hex-encoded (0x0 or 0x1)
64
+
65
+ // EIP-2930/EIP-1559 field
66
+ accessList ?: AccessList ;
67
+ }
68
+
69
+ // Otterscan receipt type - raw RPC response format
70
+ export interface OtsReceipt {
71
+ // Core receipt fields
72
+ blockHash : string ;
73
+ blockNumber : string ; // hex-encoded number
74
+ contractAddress : string | null ; // null for non-contract-creating txs
75
+ cumulativeGasUsed : string ; // hex-encoded bigint
76
+ effectiveGasPrice : string ; // hex-encoded bigint
77
+ from : string ;
78
+ gasUsed : string ; // hex-encoded bigint
79
+ logs : OtsLog [ ] ;
80
+ logsBloom : string ; // hex-encoded bloom filter
81
+ status : string ; // hex-encoded: "0x1" success, "0x0" failure
82
+ to : string ;
83
+ transactionHash : string ;
84
+ transactionIndex : string ; // hex-encoded number
85
+ type : string ; // hex-encoded transaction type
86
+
87
+ // Otterscan-specific extension
88
+ timestamp : number ; // Unix timestamp as actual number (not hex)
89
+ }
90
+
91
+ // Otterscan search page result
92
+ export interface OtsSearchResult {
93
+ txs : OtsTransaction [ ] ;
94
+ receipts : OtsReceipt [ ] ;
95
+ firstPage : boolean ;
96
+ lastPage : boolean ;
97
+ }
22
98
23
99
/**
24
100
* Internal operation types returned by ots_getInternalOperations
@@ -31,45 +107,45 @@ export interface OtsInternalOp {
31
107
/** Target address (null for self-destruct operations) */
32
108
to : string | null ;
33
109
/** Value transferred (hex quantity) */
34
- value : Hex ;
110
+ value : HexString ;
35
111
}
36
112
37
113
/**
38
114
* Block details with issuance and fee information
39
115
*/
40
116
export interface OtsBlockDetails {
41
117
/** Raw block data */
42
- block : any ;
118
+ block : BlockParams ;
43
119
/** Number of transactions in the block */
44
120
transactionCount : number ;
45
121
/** Block reward information (optional) */
46
122
issuance ?: {
47
- blockReward : Hex ;
48
- uncleReward : Hex ;
49
- issuance : Hex ;
123
+ blockReward : HexString ;
124
+ uncleReward : HexString ;
125
+ issuance : HexString ;
50
126
} ;
51
127
/** Total fees collected in the block */
52
- totalFees ?: Hex ;
128
+ totalFees ?: HexString ;
53
129
}
54
130
55
131
/**
56
132
* Paginated block transactions with receipts
57
133
*/
58
134
export interface OtsBlockTxPage {
59
135
/** Transaction bodies with input truncated to 4-byte selector */
60
- transactions : Array < any > ;
136
+ transactions : Array < OtsTransaction > ;
61
137
/** Receipts with logs and bloom set to null */
62
- receipts : Array < any > ;
138
+ receipts : Array < OtsReceipt > ;
63
139
}
64
140
65
141
/**
66
142
* Paginated search results for address transaction history
67
143
*/
68
144
export interface OtsSearchPage {
69
145
/** Array of transactions */
70
- txs : Array < any > ;
146
+ txs : OtsTransaction [ ] ;
71
147
/** Array of corresponding receipts */
72
- receipts : Array < any > ;
148
+ receipts : OtsReceipt [ ] ;
73
149
/** Whether this is the first page */
74
150
firstPage : boolean ;
75
151
/** Whether this is the last page */
@@ -89,15 +165,14 @@ export interface OtsContractCreator {
89
165
/**
90
166
* The OtterscanProvider extends JsonRpcProvider to add support for
91
167
* Erigon's OTS (Otterscan) namespace methods.
92
- *
168
+ *
93
169
* These methods provide efficient access to blockchain data optimized
94
170
* for explorer applications.
95
- *
171
+ *
96
172
* **Note**: OTS methods are only available on Erigon nodes with the
97
173
* ots namespace enabled via --http.api "eth,erigon,trace,ots"
98
174
*/
99
175
export class OtterscanProvider extends JsonRpcProvider {
100
-
101
176
constructor ( url ?: string | FetchRequest , network ?: Networkish , options ?: JsonRpcApiProviderOptions ) {
102
177
super ( url , network , options ) ;
103
178
}
@@ -135,7 +210,7 @@ export class OtterscanProvider extends JsonRpcProvider {
135
210
* @param txHash - Transaction hash
136
211
* @returns Raw revert data as hex string, "0x" if no error
137
212
*/
138
- async getTransactionErrorData ( txHash : string ) : Promise < Hex > {
213
+ async getTransactionErrorData ( txHash : string ) : Promise < HexString > {
139
214
return await this . send ( "ots_getTransactionError" , [ txHash ] ) ;
140
215
}
141
216
@@ -145,7 +220,7 @@ export class OtterscanProvider extends JsonRpcProvider {
145
220
* @param customAbi - Optional custom ABI for decoding custom errors
146
221
* @returns Decoded error message or null if no error
147
222
*/
148
- async getTransactionRevertReason ( txHash : string , customAbi ?: any [ ] ) : Promise < string | null > {
223
+ async getTransactionRevertReason ( txHash : string , customAbi ?: Fragment [ ] ) : Promise < string | null > {
149
224
const data : string = await this . getTransactionErrorData ( txHash ) ;
150
225
if ( data === "0x" ) return null ;
151
226
@@ -167,13 +242,15 @@ export class OtterscanProvider extends JsonRpcProvider {
167
242
const iface = new Interface ( customAbi ) ;
168
243
const parsed = iface . parseError ( data ) ;
169
244
if ( parsed ) {
170
- return `${ parsed . name } (${ parsed . args . map ( ( a ) => {
171
- try {
172
- return JSON . stringify ( a ) ;
173
- } catch {
174
- return String ( a ) ;
175
- }
176
- } ) . join ( "," ) } )`;
245
+ return `${ parsed . name } (${ parsed . args
246
+ . map ( a => {
247
+ try {
248
+ return JSON . stringify ( a ) ;
249
+ } catch {
250
+ return String ( a ) ;
251
+ }
252
+ } )
253
+ . join ( "," ) } )`;
177
254
}
178
255
} catch {
179
256
// Fall through to selector display
@@ -220,7 +297,7 @@ export class OtterscanProvider extends JsonRpcProvider {
220
297
* @param pageSize - Maximum results to return
221
298
* @returns Page of transactions
222
299
*/
223
- async searchTransactionsBefore ( address : string , blockNumber : number , pageSize : number ) : Promise < OtsSearchPage > {
300
+ async searchTransactionsBefore ( address : string , blockNumber : number , pageSize : number ) : Promise < OtsSearchResult > {
224
301
return await this . send ( "ots_searchTransactionsBefore" , [ address , blockNumber , pageSize ] ) ;
225
302
}
226
303
@@ -231,7 +308,7 @@ export class OtterscanProvider extends JsonRpcProvider {
231
308
* @param pageSize - Maximum results to return
232
309
* @returns Page of transactions
233
310
*/
234
- async searchTransactionsAfter ( address : string , blockNumber : number , pageSize : number ) : Promise < OtsSearchPage > {
311
+ async searchTransactionsAfter ( address : string , blockNumber : number , pageSize : number ) : Promise < OtsSearchResult > {
235
312
return await this . send ( "ots_searchTransactionsAfter" , [ address , blockNumber , pageSize ] ) ;
236
313
}
237
314
@@ -265,8 +342,9 @@ export class OtterscanProvider extends JsonRpcProvider {
265
342
if ( level < minLevel ) {
266
343
throw new Error ( `ots_getApiLevel ${ level } < required ${ minLevel } ` ) ;
267
344
}
268
- } catch ( error : any ) {
269
- const err = new Error ( `Erigon OTS namespace unavailable or too old: ${ error ?. message ?? error } ` ) ;
345
+ } catch ( error : unknown ) {
346
+ const errorMsg = error instanceof Error ? error . message : String ( error ) ;
347
+ const err = new Error ( `Erigon OTS namespace unavailable or too old: ${ errorMsg } ` ) ;
270
348
( err as any ) . code = "OTS_UNAVAILABLE" ;
271
349
throw err ;
272
350
}
@@ -285,13 +363,14 @@ export class OtterscanProvider extends JsonRpcProvider {
285
363
direction : "before" | "after" ,
286
364
startBlock : number ,
287
365
pageSize : number = 500
288
- ) : AsyncGenerator < { tx : any ; receipt : any } , void , unknown > {
366
+ ) : AsyncGenerator < { tx : OtsTransaction ; receipt : OtsReceipt } , void , unknown > {
289
367
let currentBlock = startBlock ;
290
368
291
369
while ( true ) {
292
- const page = direction === "before"
293
- ? await this . searchTransactionsBefore ( address , currentBlock , pageSize )
294
- : await this . searchTransactionsAfter ( address , currentBlock , pageSize ) ;
370
+ const page =
371
+ direction === "before"
372
+ ? await this . searchTransactionsBefore ( address , currentBlock , pageSize )
373
+ : await this . searchTransactionsAfter ( address , currentBlock , pageSize ) ;
295
374
296
375
// Yield each transaction with its receipt
297
376
for ( let i = 0 ; i < page . txs . length ; i ++ ) {
@@ -309,13 +388,13 @@ export class OtterscanProvider extends JsonRpcProvider {
309
388
if ( ! lastTx ) break ;
310
389
311
390
const nextBlock = Number ( lastTx . blockNumber ) ;
312
-
391
+
313
392
// Prevent infinite loops from malformed API responses
314
393
if ( nextBlock === currentBlock ) {
315
394
throw new Error ( `Iterator stuck on block ${ currentBlock } . API returned same block number.` ) ;
316
395
}
317
-
396
+
318
397
currentBlock = nextBlock ;
319
398
}
320
399
}
321
- }
400
+ }
0 commit comments