Skip to content

Commit 26617c6

Browse files
committed
Using standard ethers.js types where Otterscan is extending existing response types. Adding missing getBlockDetailsByHash() method. Added real ots_ raw data to the unit tests to make them more accurate. Added the "reasoning" from the otterscan docs to the JSDoc comments.
1 parent d0ba304 commit 26617c6

File tree

4 files changed

+247
-192
lines changed

4 files changed

+247
-192
lines changed
Lines changed: 133 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
import assert from "assert";
22

3-
import {
4-
FetchRequest,
5-
OtterscanProvider,
6-
JsonRpcProvider,
7-
} from "../index.js";
3+
import { FetchRequest, OtterscanProvider, JsonRpcProvider } from "../index.js";
84

95
import type {
106
OtsInternalOp,
117
OtsBlockDetails,
12-
OtsBlockTxPage,
13-
OtsSearchResult,
14-
OtsContractCreator,
8+
OtsBlockTransactionsPage,
9+
OtsAddressTransactionsPage,
10+
OtsContractCreator
1511
} from "../providers/provider-otterscan.js";
1612

17-
describe("Test Otterscan Provider", function() {
13+
describe("Test Otterscan Provider", function () {
1814
// Mock OTS responses for testing
1915
function createMockOtsProvider() {
2016
const req = new FetchRequest("http://localhost:8545/");
21-
17+
2218
req.getUrlFunc = async (_req, signal) => {
23-
const bodyStr = typeof _req.body === "string" ? _req.body : new TextDecoder().decode(_req.body || new Uint8Array());
19+
const bodyStr =
20+
typeof _req.body === "string" ? _req.body : new TextDecoder().decode(_req.body || new Uint8Array());
2421
const request = JSON.parse(bodyStr || "{}");
25-
22+
2623
let result: any;
27-
24+
2825
switch (request.method) {
2926
case "ots_getApiLevel":
3027
result = 8;
@@ -34,12 +31,14 @@ describe("Test Otterscan Provider", function() {
3431
result = request.params[0] !== "0x0000000000000000000000000000000000000000";
3532
break;
3633
case "ots_getInternalOperations":
37-
result = [{
38-
type: 0,
39-
from: "0x1234567890123456789012345678901234567890",
40-
to: "0x0987654321098765432109876543210987654321",
41-
value: "0x1000000000000000000"
42-
}];
34+
result = [
35+
{
36+
type: 0,
37+
from: "0x1234567890123456789012345678901234567890",
38+
to: "0x0987654321098765432109876543210987654321",
39+
value: "0x1000000000000000000"
40+
}
41+
];
4342
break;
4443
case "ots_getTransactionError":
4544
result = "0x";
@@ -51,36 +50,86 @@ describe("Test Otterscan Provider", function() {
5150
result = {
5251
block: {
5352
hash: "0x123abc",
54-
number: "0x1000"
53+
number: "0x1000",
54+
timestamp: "0x499602d2",
55+
parentHash: "0x000abc",
56+
nonce: "0x0",
57+
difficulty: "0x0",
58+
gasLimit: "0x1c9c380",
59+
gasUsed: "0x5208",
60+
miner: "0x0000000000000000000000000000000000000000",
61+
extraData: "0x",
62+
baseFeePerGas: "0x0",
63+
logsBloom: null
5564
},
56-
transactionCount: 5,
5765
totalFees: "0x5000000000000000"
5866
};
5967
break;
6068
case "ots_getBlockTransactions":
6169
result = {
62-
transactions: [{
63-
hash: "0x456def",
64-
from: "0x1111111111111111111111111111111111111111",
65-
to: "0x2222222222222222222222222222222222222222",
66-
value: "0x1000000000000000000"
67-
}],
68-
receipts: [{
69-
status: "0x1",
70-
gasUsed: "0x5208"
71-
}]
70+
transactions: [
71+
{
72+
hash: "0x456def",
73+
from: "0x1111111111111111111111111111111111111111",
74+
to: "0x2222222222222222222222222222222222222222",
75+
value: "0x1000000000000000000"
76+
}
77+
],
78+
receipts: [
79+
{
80+
status: "0x1",
81+
gasUsed: "0x5208"
82+
}
83+
]
7284
};
7385
break;
7486
case "ots_searchTransactionsBefore":
7587
case "ots_searchTransactionsAfter":
7688
result = {
77-
txs: [{
78-
hash: "0x789ghi",
79-
blockNumber: "0x1000"
80-
}],
81-
receipts: [{
82-
status: "0x1"
83-
}],
89+
txs: [
90+
{
91+
blockHash: "0x5adc8e4d5d8eee95a3b390e9cbed9f1633f94dae073b70f2c890d419b7bb7ca0",
92+
blockNumber: "0x121e890",
93+
from: "0x17076a2bdff9db26544a9201faad098b76b51b31",
94+
gas: "0x5208",
95+
gasPrice: "0x44721f210",
96+
maxPriorityFeePerGas: "0x5f5e100",
97+
maxFeePerGas: "0x5e2b132fb",
98+
hash: "0xe689a340d805b0e6d5f26a4498caecec81752003b9352bb5802819641baaf9a9",
99+
input: "0x",
100+
nonce: "0x1f",
101+
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
102+
transactionIndex: "0x11",
103+
value: "0x38d7ea4c68000",
104+
type: "0x2",
105+
accessList: [],
106+
chainId: "0x1",
107+
v: "0x0",
108+
yParity: "0x0",
109+
r: "0xd3ecccab74bc708d2ead0d913c38990870c31f3f3eee3c7354752c1fdd826b19",
110+
s: "0x275c7656704d35da38b197d3365ef73388d42a8ca2304ce849547ce2348b087"
111+
}
112+
],
113+
receipts: [
114+
{
115+
blockHash: "0x5adc8e4d5d8eee95a3b390e9cbed9f1633f94dae073b70f2c890d419b7bb7ca0",
116+
blockNumber: "0x121e890",
117+
contractAddress: null,
118+
cumulativeGasUsed: "0x2654c4",
119+
effectiveGasPrice: "0x44721f210",
120+
from: "0x17076a2bdff9db26544a9201faad098b76b51b31",
121+
gasUsed: "0x5208",
122+
logs: [],
123+
logsBloom:
124+
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
125+
status: "0x1",
126+
timestamp: 1705166675,
127+
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
128+
transactionHash: "0xe689a340d805b0e6d5f26a4498caecec81752003b9352bb5802819641baaf9a9",
129+
transactionIndex: "0x11",
130+
type: "0x2"
131+
}
132+
],
84133
firstPage: true,
85134
lastPage: false
86135
};
@@ -103,138 +152,146 @@ describe("Test Otterscan Provider", function() {
103152
default:
104153
throw new Error(`Unsupported method: ${request.method}`);
105154
}
106-
155+
107156
const response = {
108157
id: request.id,
109158
jsonrpc: "2.0",
110159
result
111160
};
112-
161+
113162
return {
114163
statusCode: 200,
115164
statusMessage: "OK",
116165
headers: { "content-type": "application/json" },
117166
body: new TextEncoder().encode(JSON.stringify(response))
118167
};
119168
};
120-
169+
121170
return new OtterscanProvider(req, 1, { staticNetwork: true });
122171
}
123172

124-
it("should extend JsonRpcProvider", function() {
173+
it("should extend JsonRpcProvider", function () {
125174
const provider = createMockOtsProvider();
126175
assert(provider instanceof OtterscanProvider, "should be OtterscanProvider instance");
127176
assert(provider instanceof JsonRpcProvider, "should extend JsonRpcProvider");
128177
});
129178

130-
it("should get OTS API level", async function() {
179+
it("should get OTS API level", async function () {
131180
const provider = createMockOtsProvider();
132181
const apiLevel = await provider.otsApiLevel();
133182
assert.strictEqual(apiLevel, 8, "should return API level 8");
134183
});
135184

136-
it("should check if address has code", async function() {
185+
it("should check if address has code", async function () {
137186
const provider = createMockOtsProvider();
138-
187+
139188
const hasCodeTrue = await provider.hasCode("0x1234567890123456789012345678901234567890");
140189
assert.strictEqual(hasCodeTrue, true, "should return true for non-zero address");
141-
190+
142191
const hasCodeFalse = await provider.hasCode("0x0000000000000000000000000000000000000000");
143192
assert.strictEqual(hasCodeFalse, false, "should return false for zero address");
144193
});
145194

146-
it("should get internal operations", async function() {
195+
it("should get internal operations", async function () {
147196
const provider = createMockOtsProvider();
148197
const internalOps = await provider.getInternalOperations("0x123");
149-
198+
150199
assert(Array.isArray(internalOps), "should return array");
151200
assert.strictEqual(internalOps.length, 1, "should have one operation");
152-
201+
153202
const op = internalOps[0];
154203
assert.strictEqual(op.type, 0, "should have type 0");
155204
assert.strictEqual(op.from, "0x1234567890123456789012345678901234567890", "should have correct from");
156205
assert.strictEqual(op.to, "0x0987654321098765432109876543210987654321", "should have correct to");
157206
assert.strictEqual(op.value, "0x1000000000000000000", "should have correct value");
158207
});
159208

160-
it("should get transaction error data", async function() {
209+
it("should get transaction error data", async function () {
161210
const provider = createMockOtsProvider();
162211
const errorData = await provider.getTransactionErrorData("0x123");
163212
assert.strictEqual(errorData, "0x", "should return empty error data");
164213
});
165214

166-
it("should get transaction revert reason", async function() {
215+
it("should get transaction revert reason", async function () {
167216
const provider = createMockOtsProvider();
168217
const revertReason = await provider.getTransactionRevertReason("0x123");
169218
assert.strictEqual(revertReason, null, "should return null for no error");
170219
});
171220

172-
it("should trace transaction", async function() {
221+
it("should trace transaction", async function () {
173222
const provider = createMockOtsProvider();
174223
const trace = await provider.traceTransaction("0x123");
175224
assert(typeof trace === "object", "should return trace object");
176225
assert(Array.isArray(trace.calls), "should have calls array");
177226
});
178227

179-
it("should get block details", async function() {
228+
it("should get block details", async function () {
180229
const provider = createMockOtsProvider();
181230
const blockDetails = await provider.getBlockDetails(4096);
182-
231+
183232
assert(typeof blockDetails === "object", "should return object");
184-
assert.strictEqual(blockDetails.transactionCount, 5, "should have transaction count");
233+
assert.strictEqual(
234+
blockDetails.block.logsBloom,
235+
null,
236+
"should have null logsBloom (removed for efficiency)"
237+
);
185238
assert.strictEqual(blockDetails.totalFees, "0x5000000000000000", "should have total fees");
186239
assert(blockDetails.block, "should have block data");
187240
});
188241

189-
it("should get block transactions", async function() {
242+
it("should get block transactions", async function () {
190243
const provider = createMockOtsProvider();
191244
const blockTxs = await provider.getBlockTransactions(4096, 0, 10);
192-
245+
193246
assert(Array.isArray(blockTxs.transactions), "should have transactions array");
194247
assert(Array.isArray(blockTxs.receipts), "should have receipts array");
195248
assert.strictEqual(blockTxs.transactions.length, 1, "should have one transaction");
196249
assert.strictEqual(blockTxs.receipts.length, 1, "should have one receipt");
197250
});
198251

199-
it("should search transactions before", async function() {
252+
it("should search transactions before", async function () {
200253
const provider = createMockOtsProvider();
201254
const searchResults = await provider.searchTransactionsBefore("0x123", 4096, 10);
202-
255+
203256
assert(Array.isArray(searchResults.txs), "should have txs array");
204257
assert(Array.isArray(searchResults.receipts), "should have receipts array");
205258
assert.strictEqual(searchResults.firstPage, true, "should be first page");
206259
assert.strictEqual(searchResults.lastPage, false, "should not be last page");
207260
});
208261

209-
it("should search transactions after", async function() {
262+
it("should search transactions after", async function () {
210263
const provider = createMockOtsProvider();
211264
const searchResults = await provider.searchTransactionsAfter("0x123", 4096, 10);
212-
265+
213266
assert(Array.isArray(searchResults.txs), "should have txs array");
214267
assert(Array.isArray(searchResults.receipts), "should have receipts array");
215268
});
216269

217-
it("should get transaction by sender and nonce", async function() {
270+
it("should get transaction by sender and nonce", async function () {
218271
const provider = createMockOtsProvider();
219272
const txHash = await provider.getTransactionBySenderAndNonce("0x123", 0);
220273
assert.strictEqual(txHash, "0xabcdef123456789", "should return transaction hash");
221274
});
222275

223-
it("should get contract creator", async function() {
276+
it("should get contract creator", async function () {
224277
const provider = createMockOtsProvider();
225278
const creator = await provider.getContractCreator("0x123");
226-
279+
227280
assert(typeof creator === "object", "should return object");
228281
assert.strictEqual(creator?.hash, "0x987654321", "should have creation hash");
229-
assert.strictEqual(creator?.creator, "0x1111111111111111111111111111111111111111", "should have creator address");
282+
assert.strictEqual(
283+
creator?.creator,
284+
"0x1111111111111111111111111111111111111111",
285+
"should have creator address"
286+
);
230287
});
231288

232-
it("should ensure OTS capability", async function() {
289+
it("should ensure OTS capability", async function () {
233290
const provider = createMockOtsProvider();
234-
291+
235292
// Should not throw
236293
await provider.ensureOts(8);
237-
294+
238295
// Should throw for higher requirement
239296
try {
240297
await provider.ensureOts(10);
@@ -245,25 +302,25 @@ describe("Test Otterscan Provider", function() {
245302
}
246303
});
247304

248-
it("should have async iterator for address history", function() {
305+
it("should have async iterator for address history", function () {
249306
const provider = createMockOtsProvider();
250307
const iterator = provider.iterateAddressHistory("0x123", "before", 4096);
251-
308+
252309
assert(typeof iterator[Symbol.asyncIterator] === "function", "should be async iterable");
253310
});
254311

255-
it("should properly type return values", async function() {
312+
it("should properly type return values", async function () {
256313
const provider = createMockOtsProvider();
257-
314+
258315
// Test TypeScript typing works correctly
259316
const apiLevel: number = await provider.otsApiLevel();
260317
const hasCode: boolean = await provider.hasCode("0x123");
261318
const internalOps: OtsInternalOp[] = await provider.getInternalOperations("0x123");
262319
const blockDetails: OtsBlockDetails = await provider.getBlockDetails(4096);
263-
const blockTxs: OtsBlockTxPage = await provider.getBlockTransactions(4096, 0, 10);
264-
const searchResults: OtsSearchResult = await provider.searchTransactionsBefore("0x123", 4096, 10);
320+
const blockTxs: OtsBlockTransactionsPage = await provider.getBlockTransactions(4096, 0, 10);
321+
const searchResults: OtsAddressTransactionsPage = await provider.searchTransactionsBefore("0x123", 4096, 10);
265322
const creator: OtsContractCreator | null = await provider.getContractCreator("0x123");
266-
323+
267324
// Basic type assertions
268325
assert.strictEqual(typeof apiLevel, "number");
269326
assert.strictEqual(typeof hasCode, "boolean");
@@ -273,4 +330,4 @@ describe("Test Otterscan Provider", function() {
273330
assert(typeof searchResults === "object" && Array.isArray(searchResults.txs));
274331
assert(creator === null || typeof creator === "object");
275332
});
276-
});
333+
});

src.ts/ethers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ export type {
178178
JsonRpcPayload, JsonRpcResult, JsonRpcTransactionRequest,
179179
LogParams,
180180
MinedBlock, MinedTransactionResponse, Networkish, OrphanFilter,
181-
OtsLog, OtsTransaction, OtsReceipt, OtsSearchResult,
182-
OtsInternalOp, OtsBlockDetails, OtsBlockTxPage, OtsSearchPage,
183-
OtsContractCreator,
181+
OtsTransactionReceiptParams, OtsBlockTransactionReceipt,
182+
OtsBlockParams, OtsInternalOp, OtsBlockDetails, OtsBlockTransactionsPage,
183+
OtsAddressTransactionsPage, OtsContractCreator,
184184
PerformActionFilter, PerformActionRequest, PerformActionTransaction,
185185
PreparedTransactionRequest, ProviderEvent, Subscriber, Subscription,
186186
TopicFilter, TransactionReceiptParams, TransactionRequest,

0 commit comments

Comments
 (0)