1
+ import assert from "assert" ;
2
+
3
+ import {
4
+ FetchRequest ,
5
+ OtterscanProvider ,
6
+ JsonRpcProvider ,
7
+ type OtsInternalOp ,
8
+ type OtsBlockDetails ,
9
+ type OtsBlockTxPage ,
10
+ type OtsSearchPage ,
11
+ type OtsContractCreator ,
12
+ } from "../index.js" ;
13
+
14
+ describe ( "Test Otterscan Provider" , function ( ) {
15
+ // Mock OTS responses for testing
16
+ function createMockOtsProvider ( ) {
17
+ const req = new FetchRequest ( "http://localhost:8545/" ) ;
18
+
19
+ req . getUrlFunc = async ( _req , signal ) => {
20
+ const bodyStr = typeof _req . body === "string" ? _req . body : new TextDecoder ( ) . decode ( _req . body || new Uint8Array ( ) ) ;
21
+ const request = JSON . parse ( bodyStr || "{}" ) ;
22
+
23
+ let result : any ;
24
+
25
+ switch ( request . method ) {
26
+ case "ots_getApiLevel" :
27
+ result = 8 ;
28
+ break ;
29
+ case "ots_hasCode" :
30
+ // Mock: return true for non-zero addresses
31
+ result = request . params [ 0 ] !== "0x0000000000000000000000000000000000000000" ;
32
+ break ;
33
+ case "ots_getInternalOperations" :
34
+ result = [ {
35
+ type : 0 ,
36
+ from : "0x1234567890123456789012345678901234567890" ,
37
+ to : "0x0987654321098765432109876543210987654321" ,
38
+ value : "0x1000000000000000000"
39
+ } ] ;
40
+ break ;
41
+ case "ots_getTransactionError" :
42
+ result = "0x" ;
43
+ break ;
44
+ case "ots_traceTransaction" :
45
+ result = { calls : [ ] } ;
46
+ break ;
47
+ case "ots_getBlockDetails" :
48
+ result = {
49
+ block : {
50
+ hash : "0x123abc" ,
51
+ number : "0x1000"
52
+ } ,
53
+ transactionCount : 5 ,
54
+ totalFees : "0x5000000000000000"
55
+ } ;
56
+ break ;
57
+ case "ots_getBlockTransactions" :
58
+ result = {
59
+ transactions : [ {
60
+ hash : "0x456def" ,
61
+ from : "0x1111111111111111111111111111111111111111" ,
62
+ to : "0x2222222222222222222222222222222222222222" ,
63
+ value : "0x1000000000000000000"
64
+ } ] ,
65
+ receipts : [ {
66
+ status : "0x1" ,
67
+ gasUsed : "0x5208"
68
+ } ]
69
+ } ;
70
+ break ;
71
+ case "ots_searchTransactionsBefore" :
72
+ case "ots_searchTransactionsAfter" :
73
+ result = {
74
+ txs : [ {
75
+ hash : "0x789ghi" ,
76
+ blockNumber : "0x1000"
77
+ } ] ,
78
+ receipts : [ {
79
+ status : "0x1"
80
+ } ] ,
81
+ firstPage : true ,
82
+ lastPage : false
83
+ } ;
84
+ break ;
85
+ case "ots_getTransactionBySenderAndNonce" :
86
+ result = "0xabcdef123456789" ;
87
+ break ;
88
+ case "ots_getContractCreator" :
89
+ result = {
90
+ hash : "0x987654321" ,
91
+ creator : "0x1111111111111111111111111111111111111111"
92
+ } ;
93
+ break ;
94
+ case "eth_chainId" :
95
+ result = "0x1" ;
96
+ break ;
97
+ case "eth_blockNumber" :
98
+ result = "0x1000" ;
99
+ break ;
100
+ default :
101
+ throw new Error ( `Unsupported method: ${ request . method } ` ) ;
102
+ }
103
+
104
+ const response = {
105
+ id : request . id ,
106
+ jsonrpc : "2.0" ,
107
+ result
108
+ } ;
109
+
110
+ return {
111
+ statusCode : 200 ,
112
+ statusMessage : "OK" ,
113
+ headers : { "content-type" : "application/json" } ,
114
+ body : new TextEncoder ( ) . encode ( JSON . stringify ( response ) )
115
+ } ;
116
+ } ;
117
+
118
+ return new OtterscanProvider ( req , 1 , { staticNetwork : true } ) ;
119
+ }
120
+
121
+ it ( "should extend JsonRpcProvider" , function ( ) {
122
+ const provider = createMockOtsProvider ( ) ;
123
+ assert ( provider instanceof OtterscanProvider , "should be OtterscanProvider instance" ) ;
124
+ assert ( provider instanceof JsonRpcProvider , "should extend JsonRpcProvider" ) ;
125
+ } ) ;
126
+
127
+ it ( "should get OTS API level" , async function ( ) {
128
+ const provider = createMockOtsProvider ( ) ;
129
+ const apiLevel = await provider . otsApiLevel ( ) ;
130
+ assert . strictEqual ( apiLevel , 8 , "should return API level 8" ) ;
131
+ } ) ;
132
+
133
+ it ( "should check if address has code" , async function ( ) {
134
+ const provider = createMockOtsProvider ( ) ;
135
+
136
+ const hasCodeTrue = await provider . hasCode ( "0x1234567890123456789012345678901234567890" ) ;
137
+ assert . strictEqual ( hasCodeTrue , true , "should return true for non-zero address" ) ;
138
+
139
+ const hasCodeFalse = await provider . hasCode ( "0x0000000000000000000000000000000000000000" ) ;
140
+ assert . strictEqual ( hasCodeFalse , false , "should return false for zero address" ) ;
141
+ } ) ;
142
+
143
+ it ( "should get internal operations" , async function ( ) {
144
+ const provider = createMockOtsProvider ( ) ;
145
+ const internalOps = await provider . getInternalOperations ( "0x123" ) ;
146
+
147
+ assert ( Array . isArray ( internalOps ) , "should return array" ) ;
148
+ assert . strictEqual ( internalOps . length , 1 , "should have one operation" ) ;
149
+
150
+ const op = internalOps [ 0 ] ;
151
+ assert . strictEqual ( op . type , 0 , "should have type 0" ) ;
152
+ assert . strictEqual ( op . from , "0x1234567890123456789012345678901234567890" , "should have correct from" ) ;
153
+ assert . strictEqual ( op . to , "0x0987654321098765432109876543210987654321" , "should have correct to" ) ;
154
+ assert . strictEqual ( op . value , "0x1000000000000000000" , "should have correct value" ) ;
155
+ } ) ;
156
+
157
+ it ( "should get transaction error data" , async function ( ) {
158
+ const provider = createMockOtsProvider ( ) ;
159
+ const errorData = await provider . getTransactionErrorData ( "0x123" ) ;
160
+ assert . strictEqual ( errorData , "0x" , "should return empty error data" ) ;
161
+ } ) ;
162
+
163
+ it ( "should get transaction revert reason" , async function ( ) {
164
+ const provider = createMockOtsProvider ( ) ;
165
+ const revertReason = await provider . getTransactionRevertReason ( "0x123" ) ;
166
+ assert . strictEqual ( revertReason , null , "should return null for no error" ) ;
167
+ } ) ;
168
+
169
+ it ( "should trace transaction" , async function ( ) {
170
+ const provider = createMockOtsProvider ( ) ;
171
+ const trace = await provider . traceTransaction ( "0x123" ) ;
172
+ assert ( typeof trace === "object" , "should return trace object" ) ;
173
+ assert ( Array . isArray ( trace . calls ) , "should have calls array" ) ;
174
+ } ) ;
175
+
176
+ it ( "should get block details" , async function ( ) {
177
+ const provider = createMockOtsProvider ( ) ;
178
+ const blockDetails = await provider . getBlockDetails ( 4096 ) ;
179
+
180
+ assert ( typeof blockDetails === "object" , "should return object" ) ;
181
+ assert . strictEqual ( blockDetails . transactionCount , 5 , "should have transaction count" ) ;
182
+ assert . strictEqual ( blockDetails . totalFees , "0x5000000000000000" , "should have total fees" ) ;
183
+ assert ( blockDetails . block , "should have block data" ) ;
184
+ } ) ;
185
+
186
+ it ( "should get block transactions" , async function ( ) {
187
+ const provider = createMockOtsProvider ( ) ;
188
+ const blockTxs = await provider . getBlockTransactions ( 4096 , 0 , 10 ) ;
189
+
190
+ assert ( Array . isArray ( blockTxs . transactions ) , "should have transactions array" ) ;
191
+ assert ( Array . isArray ( blockTxs . receipts ) , "should have receipts array" ) ;
192
+ assert . strictEqual ( blockTxs . transactions . length , 1 , "should have one transaction" ) ;
193
+ assert . strictEqual ( blockTxs . receipts . length , 1 , "should have one receipt" ) ;
194
+ } ) ;
195
+
196
+ it ( "should search transactions before" , async function ( ) {
197
+ const provider = createMockOtsProvider ( ) ;
198
+ const searchResults = await provider . searchTransactionsBefore ( "0x123" , 4096 , 10 ) ;
199
+
200
+ assert ( Array . isArray ( searchResults . txs ) , "should have txs array" ) ;
201
+ assert ( Array . isArray ( searchResults . receipts ) , "should have receipts array" ) ;
202
+ assert . strictEqual ( searchResults . firstPage , true , "should be first page" ) ;
203
+ assert . strictEqual ( searchResults . lastPage , false , "should not be last page" ) ;
204
+ } ) ;
205
+
206
+ it ( "should search transactions after" , async function ( ) {
207
+ const provider = createMockOtsProvider ( ) ;
208
+ const searchResults = await provider . searchTransactionsAfter ( "0x123" , 4096 , 10 ) ;
209
+
210
+ assert ( Array . isArray ( searchResults . txs ) , "should have txs array" ) ;
211
+ assert ( Array . isArray ( searchResults . receipts ) , "should have receipts array" ) ;
212
+ } ) ;
213
+
214
+ it ( "should get transaction by sender and nonce" , async function ( ) {
215
+ const provider = createMockOtsProvider ( ) ;
216
+ const txHash = await provider . getTransactionBySenderAndNonce ( "0x123" , 0 ) ;
217
+ assert . strictEqual ( txHash , "0xabcdef123456789" , "should return transaction hash" ) ;
218
+ } ) ;
219
+
220
+ it ( "should get contract creator" , async function ( ) {
221
+ const provider = createMockOtsProvider ( ) ;
222
+ const creator = await provider . getContractCreator ( "0x123" ) ;
223
+
224
+ assert ( typeof creator === "object" , "should return object" ) ;
225
+ assert . strictEqual ( creator ?. hash , "0x987654321" , "should have creation hash" ) ;
226
+ assert . strictEqual ( creator ?. creator , "0x1111111111111111111111111111111111111111" , "should have creator address" ) ;
227
+ } ) ;
228
+
229
+ it ( "should ensure OTS capability" , async function ( ) {
230
+ const provider = createMockOtsProvider ( ) ;
231
+
232
+ // Should not throw
233
+ await provider . ensureOts ( 8 ) ;
234
+
235
+ // Should throw for higher requirement
236
+ try {
237
+ await provider . ensureOts ( 10 ) ;
238
+ assert . fail ( "should have thrown for unsupported API level" ) ;
239
+ } catch ( error : any ) {
240
+ assert ( error . message . includes ( "ots_getApiLevel" ) , "should mention API level" ) ;
241
+ assert . strictEqual ( error . code , "OTS_UNAVAILABLE" , "should have correct error code" ) ;
242
+ }
243
+ } ) ;
244
+
245
+ it ( "should have async iterator for address history" , function ( ) {
246
+ const provider = createMockOtsProvider ( ) ;
247
+ const iterator = provider . iterateAddressHistory ( "0x123" , "before" , 4096 ) ;
248
+
249
+ assert ( typeof iterator [ Symbol . asyncIterator ] === "function" , "should be async iterable" ) ;
250
+ } ) ;
251
+
252
+ it ( "should properly type return values" , async function ( ) {
253
+ const provider = createMockOtsProvider ( ) ;
254
+
255
+ // Test TypeScript typing works correctly
256
+ const apiLevel : number = await provider . otsApiLevel ( ) ;
257
+ const hasCode : boolean = await provider . hasCode ( "0x123" ) ;
258
+ const internalOps : OtsInternalOp [ ] = await provider . getInternalOperations ( "0x123" ) ;
259
+ const blockDetails : OtsBlockDetails = await provider . getBlockDetails ( 4096 ) ;
260
+ const blockTxs : OtsBlockTxPage = await provider . getBlockTransactions ( 4096 , 0 , 10 ) ;
261
+ const searchResults : OtsSearchPage = await provider . searchTransactionsBefore ( "0x123" , 4096 , 10 ) ;
262
+ const creator : OtsContractCreator | null = await provider . getContractCreator ( "0x123" ) ;
263
+
264
+ // Basic type assertions
265
+ assert . strictEqual ( typeof apiLevel , "number" ) ;
266
+ assert . strictEqual ( typeof hasCode , "boolean" ) ;
267
+ assert ( Array . isArray ( internalOps ) ) ;
268
+ assert ( typeof blockDetails === "object" ) ;
269
+ assert ( typeof blockTxs === "object" && Array . isArray ( blockTxs . transactions ) ) ;
270
+ assert ( typeof searchResults === "object" && Array . isArray ( searchResults . txs ) ) ;
271
+ assert ( creator === null || typeof creator === "object" ) ;
272
+ } ) ;
273
+ } ) ;
0 commit comments