Skip to content

Commit 0d3173f

Browse files
committed
Getting more specific with the return types.
1 parent 0211adf commit 0d3173f

File tree

4 files changed

+129
-50
lines changed

4 files changed

+129
-50
lines changed

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import {
44
FetchRequest,
55
OtterscanProvider,
66
JsonRpcProvider,
7-
type OtsInternalOp,
8-
type OtsBlockDetails,
9-
type OtsBlockTxPage,
10-
type OtsSearchPage,
11-
type OtsContractCreator,
127
} from "../index.js";
138

9+
import type {
10+
OtsInternalOp,
11+
OtsBlockDetails,
12+
OtsBlockTxPage,
13+
OtsSearchResult,
14+
OtsContractCreator,
15+
} from "../providers/provider-otterscan.js";
16+
1417
describe("Test Otterscan Provider", function() {
1518
// Mock OTS responses for testing
1619
function createMockOtsProvider() {
@@ -258,7 +261,7 @@ describe("Test Otterscan Provider", function() {
258261
const internalOps: OtsInternalOp[] = await provider.getInternalOperations("0x123");
259262
const blockDetails: OtsBlockDetails = await provider.getBlockDetails(4096);
260263
const blockTxs: OtsBlockTxPage = await provider.getBlockTransactions(4096, 0, 10);
261-
const searchResults: OtsSearchPage = await provider.searchTransactionsBefore("0x123", 4096, 10);
264+
const searchResults: OtsSearchResult = await provider.searchTransactionsBefore("0x123", 4096, 10);
262265
const creator: OtsContractCreator | null = await provider.getContractCreator("0x123");
263266

264267
// Basic type assertions

src.ts/ethers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ export type {
176176
Eip6963ProviderInfo, EventFilter, Filter, FilterByBlockHash,
177177
GasCostParameters, JsonRpcApiProviderOptions, JsonRpcError,
178178
JsonRpcPayload, JsonRpcResult, JsonRpcTransactionRequest,
179-
Hex, OtsInternalOp, OtsBlockDetails, OtsBlockTxPage, OtsSearchPage, OtsContractCreator,
180179
LogParams,
181180
MinedBlock, MinedTransactionResponse, Networkish, OrphanFilter,
182181
PerformActionFilter, PerformActionRequest, PerformActionTransaction,

src.ts/providers/index.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ export { JsonRpcApiProvider, JsonRpcProvider, JsonRpcSigner } from "./provider-j
5959

6060
export { OtterscanProvider } from "./provider-otterscan.js";
6161

62+
export type {
63+
OtsLog, OtsTransaction, OtsReceipt, OtsSearchResult,
64+
OtsInternalOp, OtsBlockDetails, OtsBlockTxPage, OtsSearchPage,
65+
OtsContractCreator
66+
} from "./provider-otterscan.js";
67+
6268
export { BrowserProvider } from "./provider-browser.js";
6369

6470
export { AlchemyProvider } from "./provider-alchemy.js";
@@ -129,14 +135,6 @@ export type {
129135
JsonRpcTransactionRequest,
130136
} from "./provider-jsonrpc.js";
131137

132-
export type {
133-
Hex,
134-
OtsInternalOp,
135-
OtsBlockDetails,
136-
OtsBlockTxPage,
137-
OtsSearchPage,
138-
OtsContractCreator
139-
} from "./provider-otterscan.js";
140138

141139
export type {
142140
WebSocketCreator, WebSocketLike

src.ts/providers/provider-otterscan.ts

Lines changed: 114 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* efficient access to transaction details, internal operations,
88
* and paginated transaction history.
99
*
10-
* @_section: api/providers/otterscan:Otterscan Provider [about-otterscanProvider]
10+
* @_subsection: api/providers/thirdparty:Otterscan [providers-otterscan]
1111
*/
1212

1313
import { Interface } from "../abi/index.js";
@@ -17,8 +17,84 @@ import { JsonRpcProvider } from "./provider-jsonrpc.js";
1717
import type { JsonRpcApiProviderOptions } from "./provider-jsonrpc.js";
1818
import type { Networkish } from "./network.js";
1919
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";
2024

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+
}
2298

2399
/**
24100
* Internal operation types returned by ots_getInternalOperations
@@ -31,45 +107,45 @@ export interface OtsInternalOp {
31107
/** Target address (null for self-destruct operations) */
32108
to: string | null;
33109
/** Value transferred (hex quantity) */
34-
value: Hex;
110+
value: HexString;
35111
}
36112

37113
/**
38114
* Block details with issuance and fee information
39115
*/
40116
export interface OtsBlockDetails {
41117
/** Raw block data */
42-
block: any;
118+
block: BlockParams;
43119
/** Number of transactions in the block */
44120
transactionCount: number;
45121
/** Block reward information (optional) */
46122
issuance?: {
47-
blockReward: Hex;
48-
uncleReward: Hex;
49-
issuance: Hex;
123+
blockReward: HexString;
124+
uncleReward: HexString;
125+
issuance: HexString;
50126
};
51127
/** Total fees collected in the block */
52-
totalFees?: Hex;
128+
totalFees?: HexString;
53129
}
54130

55131
/**
56132
* Paginated block transactions with receipts
57133
*/
58134
export interface OtsBlockTxPage {
59135
/** Transaction bodies with input truncated to 4-byte selector */
60-
transactions: Array<any>;
136+
transactions: Array<OtsTransaction>;
61137
/** Receipts with logs and bloom set to null */
62-
receipts: Array<any>;
138+
receipts: Array<OtsReceipt>;
63139
}
64140

65141
/**
66142
* Paginated search results for address transaction history
67143
*/
68144
export interface OtsSearchPage {
69145
/** Array of transactions */
70-
txs: Array<any>;
146+
txs: OtsTransaction[];
71147
/** Array of corresponding receipts */
72-
receipts: Array<any>;
148+
receipts: OtsReceipt[];
73149
/** Whether this is the first page */
74150
firstPage: boolean;
75151
/** Whether this is the last page */
@@ -89,15 +165,14 @@ export interface OtsContractCreator {
89165
/**
90166
* The OtterscanProvider extends JsonRpcProvider to add support for
91167
* Erigon's OTS (Otterscan) namespace methods.
92-
*
168+
*
93169
* These methods provide efficient access to blockchain data optimized
94170
* for explorer applications.
95-
*
171+
*
96172
* **Note**: OTS methods are only available on Erigon nodes with the
97173
* ots namespace enabled via --http.api "eth,erigon,trace,ots"
98174
*/
99175
export class OtterscanProvider extends JsonRpcProvider {
100-
101176
constructor(url?: string | FetchRequest, network?: Networkish, options?: JsonRpcApiProviderOptions) {
102177
super(url, network, options);
103178
}
@@ -135,7 +210,7 @@ export class OtterscanProvider extends JsonRpcProvider {
135210
* @param txHash - Transaction hash
136211
* @returns Raw revert data as hex string, "0x" if no error
137212
*/
138-
async getTransactionErrorData(txHash: string): Promise<Hex> {
213+
async getTransactionErrorData(txHash: string): Promise<HexString> {
139214
return await this.send("ots_getTransactionError", [txHash]);
140215
}
141216

@@ -145,7 +220,7 @@ export class OtterscanProvider extends JsonRpcProvider {
145220
* @param customAbi - Optional custom ABI for decoding custom errors
146221
* @returns Decoded error message or null if no error
147222
*/
148-
async getTransactionRevertReason(txHash: string, customAbi?: any[]): Promise<string | null> {
223+
async getTransactionRevertReason(txHash: string, customAbi?: Fragment[]): Promise<string | null> {
149224
const data: string = await this.getTransactionErrorData(txHash);
150225
if (data === "0x") return null;
151226

@@ -167,13 +242,15 @@ export class OtterscanProvider extends JsonRpcProvider {
167242
const iface = new Interface(customAbi);
168243
const parsed = iface.parseError(data);
169244
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(",")})`;
177254
}
178255
} catch {
179256
// Fall through to selector display
@@ -220,7 +297,7 @@ export class OtterscanProvider extends JsonRpcProvider {
220297
* @param pageSize - Maximum results to return
221298
* @returns Page of transactions
222299
*/
223-
async searchTransactionsBefore(address: string, blockNumber: number, pageSize: number): Promise<OtsSearchPage> {
300+
async searchTransactionsBefore(address: string, blockNumber: number, pageSize: number): Promise<OtsSearchResult> {
224301
return await this.send("ots_searchTransactionsBefore", [address, blockNumber, pageSize]);
225302
}
226303

@@ -231,7 +308,7 @@ export class OtterscanProvider extends JsonRpcProvider {
231308
* @param pageSize - Maximum results to return
232309
* @returns Page of transactions
233310
*/
234-
async searchTransactionsAfter(address: string, blockNumber: number, pageSize: number): Promise<OtsSearchPage> {
311+
async searchTransactionsAfter(address: string, blockNumber: number, pageSize: number): Promise<OtsSearchResult> {
235312
return await this.send("ots_searchTransactionsAfter", [address, blockNumber, pageSize]);
236313
}
237314

@@ -265,8 +342,9 @@ export class OtterscanProvider extends JsonRpcProvider {
265342
if (level < minLevel) {
266343
throw new Error(`ots_getApiLevel ${level} < required ${minLevel}`);
267344
}
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}`);
270348
(err as any).code = "OTS_UNAVAILABLE";
271349
throw err;
272350
}
@@ -285,13 +363,14 @@ export class OtterscanProvider extends JsonRpcProvider {
285363
direction: "before" | "after",
286364
startBlock: number,
287365
pageSize: number = 500
288-
): AsyncGenerator<{ tx: any; receipt: any }, void, unknown> {
366+
): AsyncGenerator<{ tx: OtsTransaction; receipt: OtsReceipt }, void, unknown> {
289367
let currentBlock = startBlock;
290368

291369
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);
295374

296375
// Yield each transaction with its receipt
297376
for (let i = 0; i < page.txs.length; i++) {
@@ -309,13 +388,13 @@ export class OtterscanProvider extends JsonRpcProvider {
309388
if (!lastTx) break;
310389

311390
const nextBlock = Number(lastTx.blockNumber);
312-
391+
313392
// Prevent infinite loops from malformed API responses
314393
if (nextBlock === currentBlock) {
315394
throw new Error(`Iterator stuck on block ${currentBlock}. API returned same block number.`);
316395
}
317-
396+
318397
currentBlock = nextBlock;
319398
}
320399
}
321-
}
400+
}

0 commit comments

Comments
 (0)