@0xsequence/catapult 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +27 -0
  2. package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
  3. package/dist/lib/core/__tests__/resolver.spec.js +22 -0
  4. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
  5. package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
  6. package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
  7. package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
  8. package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
  9. package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
  10. package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
  11. package/dist/lib/core/__tests__/signer.spec.js +40 -0
  12. package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
  13. package/dist/lib/core/context.d.ts +3 -2
  14. package/dist/lib/core/context.d.ts.map +1 -1
  15. package/dist/lib/core/context.js +3 -2
  16. package/dist/lib/core/context.js.map +1 -1
  17. package/dist/lib/core/engine.d.ts +4 -0
  18. package/dist/lib/core/engine.d.ts.map +1 -1
  19. package/dist/lib/core/engine.js +173 -0
  20. package/dist/lib/core/engine.js.map +1 -1
  21. package/dist/lib/core/signer.d.ts +7 -0
  22. package/dist/lib/core/signer.d.ts.map +1 -0
  23. package/dist/lib/core/signer.js +60 -0
  24. package/dist/lib/core/signer.js.map +1 -0
  25. package/dist/lib/parsers/__tests__/source.spec.js +37 -0
  26. package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
  27. package/dist/lib/parsers/source.js +1 -1
  28. package/dist/lib/parsers/source.js.map +1 -1
  29. package/dist/lib/provenance.js +51 -2
  30. package/dist/lib/provenance.js.map +1 -1
  31. package/dist/lib/types/actions.d.ts +26 -2
  32. package/dist/lib/types/actions.d.ts.map +1 -1
  33. package/dist/lib/types/actions.js +3 -0
  34. package/dist/lib/types/actions.js.map +1 -1
  35. package/dist/lib/types/source.d.ts +2 -0
  36. package/dist/lib/types/source.d.ts.map +1 -1
  37. package/package.json +4 -1
  38. package/.eslintrc.json +0 -29
  39. package/.github/workflows/ci.yml +0 -181
  40. package/CONCEPT.md +0 -24
  41. package/contracts/checked-call.huff +0 -65
  42. package/eslint.config.js +0 -48
  43. package/examples/jobs/guards-v1.yaml +0 -17
  44. package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
  45. package/examples/jobs/sequence-v1.yaml +0 -59
  46. package/examples/templates/sequence-factory-v1.yaml +0 -56
  47. package/jest.config.js +0 -25
  48. package/src/cli.ts +0 -18
  49. package/src/commands/common.ts +0 -61
  50. package/src/commands/dry.ts +0 -209
  51. package/src/commands/etherscan.ts +0 -360
  52. package/src/commands/index.ts +0 -6
  53. package/src/commands/list.ts +0 -262
  54. package/src/commands/provenance.ts +0 -120
  55. package/src/commands/run.ts +0 -146
  56. package/src/commands/utils.ts +0 -215
  57. package/src/index.ts +0 -67
  58. package/src/lib/__tests__/deployer-events.spec.ts +0 -338
  59. package/src/lib/__tests__/deployer.spec.ts +0 -2269
  60. package/src/lib/__tests__/network-loader.spec.ts +0 -150
  61. package/src/lib/__tests__/network-selection.spec.ts +0 -41
  62. package/src/lib/__tests__/network-utils.spec.ts +0 -230
  63. package/src/lib/__tests__/provenance.spec.ts +0 -208
  64. package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
  65. package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
  66. package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
  67. package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
  68. package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
  69. package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
  70. package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
  71. package/src/lib/contracts/repository.ts +0 -411
  72. package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
  73. package/src/lib/core/__tests__/context.spec.ts +0 -37
  74. package/src/lib/core/__tests__/engine.spec.ts +0 -2005
  75. package/src/lib/core/__tests__/graph.spec.ts +0 -125
  76. package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
  77. package/src/lib/core/__tests__/loader.spec.ts +0 -367
  78. package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
  79. package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
  80. package/src/lib/core/__tests__/static-action.spec.ts +0 -172
  81. package/src/lib/core/context.ts +0 -127
  82. package/src/lib/core/engine.ts +0 -1834
  83. package/src/lib/core/graph.ts +0 -252
  84. package/src/lib/core/loader.ts +0 -253
  85. package/src/lib/core/resolver.ts +0 -873
  86. package/src/lib/deployer.ts +0 -1005
  87. package/src/lib/events/__tests__/event-system.spec.ts +0 -392
  88. package/src/lib/events/cli-adapter.ts +0 -369
  89. package/src/lib/events/emitter.ts +0 -62
  90. package/src/lib/events/index.ts +0 -3
  91. package/src/lib/events/types.ts +0 -520
  92. package/src/lib/index.ts +0 -17
  93. package/src/lib/network-loader.ts +0 -90
  94. package/src/lib/network-selection.ts +0 -73
  95. package/src/lib/network-utils.ts +0 -64
  96. package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
  97. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
  98. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
  99. package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
  100. package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
  101. package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
  102. package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
  103. package/src/lib/parsers/__tests__/job.spec.ts +0 -439
  104. package/src/lib/parsers/__tests__/source.spec.ts +0 -134
  105. package/src/lib/parsers/__tests__/template.spec.ts +0 -111
  106. package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
  107. package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
  108. package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
  109. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
  110. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
  111. package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
  112. package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
  113. package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
  114. package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
  115. package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
  116. package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
  117. package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
  118. package/src/lib/parsers/artifact/index.ts +0 -27
  119. package/src/lib/parsers/artifact/types.ts +0 -9
  120. package/src/lib/parsers/buildinfo.ts +0 -127
  121. package/src/lib/parsers/constants.ts +0 -56
  122. package/src/lib/parsers/index.ts +0 -6
  123. package/src/lib/parsers/job.ts +0 -160
  124. package/src/lib/parsers/source.ts +0 -129
  125. package/src/lib/parsers/template.ts +0 -135
  126. package/src/lib/provenance.ts +0 -785
  127. package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
  128. package/src/lib/std/templates/assured-deployment.yaml +0 -46
  129. package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
  130. package/src/lib/std/templates/erc-2470.yaml +0 -70
  131. package/src/lib/std/templates/min-balance.yaml +0 -35
  132. package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
  133. package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
  134. package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
  135. package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
  136. package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
  137. package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
  138. package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
  139. package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
  140. package/src/lib/types/actions.ts +0 -148
  141. package/src/lib/types/artifacts.ts +0 -21
  142. package/src/lib/types/buildinfo.ts +0 -116
  143. package/src/lib/types/conditions.ts +0 -50
  144. package/src/lib/types/contracts.ts +0 -26
  145. package/src/lib/types/definitions.ts +0 -77
  146. package/src/lib/types/index.ts +0 -9
  147. package/src/lib/types/network.ts +0 -33
  148. package/src/lib/types/project.ts +0 -9
  149. package/src/lib/types/source.ts +0 -26
  150. package/src/lib/types/task.ts +0 -9
  151. package/src/lib/types/values.ts +0 -221
  152. package/src/lib/utils/assertion.ts +0 -24
  153. package/src/lib/utils/validation.ts +0 -116
  154. package/src/lib/validation/contract-references.ts +0 -210
  155. package/src/lib/validation/index.ts +0 -1
  156. package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
  157. package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
  158. package/src/lib/verification/etherscan.ts +0 -547
  159. package/src/lib/verification/sourcify.ts +0 -248
  160. package/test_validation/artifacts/TestContract.json +0 -9
  161. package/test_validation/jobs/test-missing.yaml +0 -16
  162. package/test_validation/networks.yaml +0 -3
  163. package/tsconfig.json +0 -36
@@ -1,710 +0,0 @@
1
- import { submitVerification, checkVerificationStatus, waitForVerification, isContractAlreadyVerified } from '../etherscan'
2
- import { Network } from '../../types/network'
3
- import { BuildInfo } from '../../types/buildinfo'
4
-
5
- // Mock global fetch
6
- global.fetch = jest.fn()
7
- const mockedFetch = fetch as jest.MockedFunction<typeof fetch>
8
-
9
- describe('Etherscan Verification', () => {
10
- const mockNetwork: Network = {
11
- name: 'Ethereum Mainnet',
12
- chainId: 1,
13
- rpcUrl: 'https://mainnet.infura.io/v3/test',
14
- supports: ['etherscan_v2']
15
- }
16
-
17
- const mockBuildInfo: BuildInfo = {
18
- _format: 'hh-sol-build-info-1',
19
- id: 'test-build-id',
20
- solcVersion: 'v0.8.25+commit.b61c2a91',
21
- solcLongVersion: '0.8.25+commit.b61c2a91.Linux.gcc',
22
- input: {
23
- language: 'Solidity',
24
- sources: {
25
- 'contracts/MyToken.sol': {
26
- content: 'pragma solidity ^0.8.0; contract MyToken { }'
27
- }
28
- },
29
- settings: {
30
- optimizer: {
31
- enabled: true,
32
- runs: 200
33
- },
34
- outputSelection: {
35
- '*': {
36
- '*': ['abi', 'evm.bytecode', 'evm.deployedBytecode']
37
- }
38
- }
39
- }
40
- },
41
- output: {
42
- contracts: {
43
- 'contracts/MyToken.sol': {
44
- MyToken: {
45
- abi: [],
46
- evm: {
47
- bytecode: { object: '0x608060405234801561001057600080fd5b50' },
48
- deployedBytecode: { object: '0x6080604052348015600f57600080fd5b50' }
49
- }
50
- }
51
- }
52
- },
53
- sources: {
54
- 'contracts/MyToken.sol': { id: 0 }
55
- }
56
- }
57
- }
58
-
59
- const mockContract = {
60
- contractName: 'MyToken',
61
- sourceName: 'contracts/MyToken.sol',
62
- abi: [],
63
- creationCode: '0x608060405234801561001057600080fd5b50',
64
- uniqueHash: 'test-hash',
65
- _sources: new Set(['/test/path'])
66
- } as any
67
-
68
- const createMockResponse = (data: any, ok = true, status = 200) => ({
69
- ok,
70
- status,
71
- statusText: ok ? 'OK' : 'Error',
72
- json: jest.fn().mockResolvedValue(data)
73
- } as any)
74
-
75
- beforeEach(() => {
76
- jest.clearAllMocks()
77
- })
78
-
79
- describe('submitVerification', () => {
80
- it('should submit verification successfully', async () => {
81
- const mockResponseData = {
82
- status: '1',
83
- result: 'test-guid-123'
84
- }
85
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
86
-
87
-
88
-
89
- const result = await submitVerification({
90
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
91
- buildInfo: mockBuildInfo,
92
- contract: mockContract,
93
- network: mockNetwork
94
- }, 'test-api-key')
95
-
96
- expect(result.success).toBe(true)
97
- expect(result.guid).toBe('test-guid-123')
98
- expect(result.message).toBe('Verification submitted successfully')
99
-
100
- expect(mockedFetch).toHaveBeenCalledWith(
101
- 'https://api.etherscan.io/v2/api?chainid=1',
102
- expect.objectContaining({
103
- method: 'POST',
104
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
105
- body: expect.any(String)
106
- })
107
- )
108
- })
109
-
110
- it('should handle verification submission failure', async () => {
111
- const mockResponseData = {
112
- status: '0',
113
- result: 'Invalid contract address'
114
- }
115
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
116
-
117
- const result = await submitVerification({
118
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
119
- buildInfo: mockBuildInfo,
120
- contract: mockContract,
121
- network: mockNetwork
122
- }, 'test-api-key')
123
-
124
- expect(result.success).toBe(false)
125
- expect(result.message).toBe('Invalid contract address')
126
- expect(result.guid).toBeUndefined()
127
- })
128
-
129
- it('should include constructor arguments when provided', async () => {
130
- const mockResponseData = {
131
- status: '1',
132
- result: 'test-guid-456'
133
- }
134
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
135
-
136
- await submitVerification({
137
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
138
- buildInfo: mockBuildInfo,
139
- contract: mockContract,
140
- constructorArguments: '0x000000000000000000000000742d35cc6596c743b2c8d12cd84d5b8fba4f3c',
141
- network: mockNetwork
142
- }, 'test-api-key')
143
-
144
- const fetchCall = mockedFetch.mock.calls[0]
145
- const requestBody = fetchCall[1]?.body as string
146
- expect(requestBody).toContain('constructorArguements=000000000000000000000000742d35cc6596c743b2c8d12cd84d5b8fba4f3c')
147
- })
148
-
149
- it('should handle network request errors', async () => {
150
- mockedFetch.mockRejectedValueOnce(new Error('Network error'))
151
-
152
- const result = await submitVerification({
153
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
154
- buildInfo: mockBuildInfo,
155
- contract: mockContract,
156
- network: mockNetwork
157
- }, 'test-api-key')
158
-
159
- expect(result.success).toBe(false)
160
- expect(result.message).toContain('API request failed: Network error')
161
- })
162
-
163
- it('should use correct API URL for different networks', async () => {
164
- const sepoliaNetwork: Network = {
165
- name: 'Sepolia',
166
- chainId: 11155111,
167
- rpcUrl: 'https://sepolia.infura.io/v3/test'
168
- }
169
-
170
- const mockResponseData = {
171
- status: '1',
172
- result: 'test-guid-sepolia'
173
- }
174
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
175
-
176
- await submitVerification({
177
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
178
- buildInfo: mockBuildInfo,
179
- contract: mockContract,
180
- network: sepoliaNetwork
181
- }, 'test-api-key')
182
-
183
- expect(mockedFetch).toHaveBeenCalledWith(
184
- 'https://api.etherscan.io/v2/api?chainid=11155111',
185
- expect.any(Object)
186
- )
187
- })
188
-
189
- it('should use v2 API format for any network', async () => {
190
- const customNetwork: Network = {
191
- name: 'Custom Network',
192
- chainId: 999999,
193
- rpcUrl: 'https://custom.rpc'
194
- }
195
-
196
- const mockResponseData = {
197
- status: '0',
198
- result: 'Invalid chain ID'
199
- }
200
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
201
-
202
- const result = await submitVerification({
203
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
204
- buildInfo: mockBuildInfo,
205
- contract: mockContract,
206
- network: customNetwork
207
- }, 'test-api-key')
208
-
209
- expect(result.success).toBe(false)
210
- expect(result.message).toBe('Invalid chain ID')
211
- expect(mockedFetch).toHaveBeenCalledWith(
212
- 'https://api.etherscan.io/v2/api?chainid=999999',
213
- expect.any(Object)
214
- )
215
- })
216
-
217
- it('should treat "Already Verified" as success', async () => {
218
- const mockResponseData = {
219
- status: '0',
220
- result: 'Already Verified'
221
- }
222
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
223
-
224
- const result = await submitVerification({
225
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
226
- buildInfo: mockBuildInfo,
227
- contract: mockContract,
228
- network: mockNetwork
229
- }, 'test-api-key')
230
-
231
- expect(result.success).toBe(true)
232
- expect(result.message).toBe('Contract is already verified')
233
- expect(result.guid).toBeUndefined()
234
- })
235
-
236
- it('should treat "Contract source code already verified" as success', async () => {
237
- const mockResponseData = {
238
- status: '0',
239
- result: 'Contract source code already verified'
240
- }
241
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
242
-
243
- const result = await submitVerification({
244
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
245
- buildInfo: mockBuildInfo,
246
- contract: mockContract,
247
- network: mockNetwork
248
- }, 'test-api-key')
249
-
250
- expect(result.success).toBe(true)
251
- expect(result.message).toBe('Contract is already verified')
252
- expect(result.guid).toBeUndefined()
253
- })
254
-
255
- describe('retry logic', () => {
256
- beforeEach(() => {
257
- // Mock console.log to avoid noise in test output
258
- jest.spyOn(console, 'log').mockImplementation(() => {})
259
- // Mock setTimeout to make tests run faster
260
- jest.spyOn(global, 'setTimeout').mockImplementation((callback: any) => {
261
- callback()
262
- return {} as any
263
- })
264
- })
265
-
266
- afterEach(() => {
267
- jest.restoreAllMocks()
268
- })
269
-
270
- it('should retry for "unable to locate contractcode" error', async () => {
271
- const contractNotFoundResponse = {
272
- status: '0',
273
- result: 'Unable to locate ContractCode at 0x123...'
274
- }
275
- const successResponse = {
276
- status: '1',
277
- result: 'test-guid-retry'
278
- }
279
-
280
- mockedFetch
281
- .mockResolvedValueOnce(createMockResponse(contractNotFoundResponse))
282
- .mockResolvedValueOnce(createMockResponse(successResponse))
283
-
284
- const result = await submitVerification({
285
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
286
- buildInfo: mockBuildInfo,
287
- contract: mockContract,
288
- network: mockNetwork,
289
- maxRetries: 3,
290
- retryDelayMs: 100
291
- }, 'test-api-key')
292
-
293
- expect(result.success).toBe(true)
294
- expect(result.guid).toBe('test-guid-retry')
295
- expect(mockedFetch).toHaveBeenCalledTimes(2)
296
- })
297
-
298
- it('should retry for "contract source code not verified" error', async () => {
299
- const contractNotFoundResponse = {
300
- status: '0',
301
- result: 'Contract source code not verified'
302
- }
303
- const successResponse = {
304
- status: '1',
305
- result: 'test-guid-retry-2'
306
- }
307
-
308
- mockedFetch
309
- .mockResolvedValueOnce(createMockResponse(contractNotFoundResponse))
310
- .mockResolvedValueOnce(createMockResponse(successResponse))
311
-
312
- const result = await submitVerification({
313
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
314
- buildInfo: mockBuildInfo,
315
- contract: mockContract,
316
- network: mockNetwork,
317
- maxRetries: 2
318
- }, 'test-api-key')
319
-
320
- expect(result.success).toBe(true)
321
- expect(result.guid).toBe('test-guid-retry-2')
322
- expect(mockedFetch).toHaveBeenCalledTimes(2)
323
- })
324
-
325
- it('should retry for "contract not found" error', async () => {
326
- const contractNotFoundResponse = {
327
- status: '0',
328
- result: 'Contract not found'
329
- }
330
- const successResponse = {
331
- status: '1',
332
- result: 'test-guid-retry-3'
333
- }
334
-
335
- mockedFetch
336
- .mockResolvedValueOnce(createMockResponse(contractNotFoundResponse))
337
- .mockResolvedValueOnce(createMockResponse(successResponse))
338
-
339
- const result = await submitVerification({
340
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
341
- buildInfo: mockBuildInfo,
342
- contract: mockContract,
343
- network: mockNetwork
344
- }, 'test-api-key')
345
-
346
- expect(result.success).toBe(true)
347
- expect(result.guid).toBe('test-guid-retry-3')
348
- expect(mockedFetch).toHaveBeenCalledTimes(2)
349
- })
350
-
351
- it('should not retry for non-contract-not-found errors', async () => {
352
- const invalidResponse = {
353
- status: '0',
354
- result: 'Invalid API key'
355
- }
356
-
357
- mockedFetch.mockResolvedValueOnce(createMockResponse(invalidResponse))
358
-
359
- const result = await submitVerification({
360
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
361
- buildInfo: mockBuildInfo,
362
- contract: mockContract,
363
- network: mockNetwork,
364
- maxRetries: 3
365
- }, 'test-api-key')
366
-
367
- expect(result.success).toBe(false)
368
- expect(result.message).toBe('Invalid API key')
369
- expect(mockedFetch).toHaveBeenCalledTimes(1) // No retries
370
- })
371
-
372
- it('should exhaust all retries and return failure message', async () => {
373
- const contractNotFoundResponse = {
374
- status: '0',
375
- result: 'Unable to locate ContractCode'
376
- }
377
-
378
- mockedFetch.mockResolvedValue(createMockResponse(contractNotFoundResponse))
379
-
380
- const result = await submitVerification({
381
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
382
- buildInfo: mockBuildInfo,
383
- contract: mockContract,
384
- network: mockNetwork,
385
- maxRetries: 2,
386
- retryDelayMs: 10
387
- }, 'test-api-key')
388
-
389
- expect(result.success).toBe(false)
390
- expect(result.message).toBe('Verification failed after 3 attempts. Last error: Unable to locate ContractCode')
391
- expect(mockedFetch).toHaveBeenCalledTimes(3) // Initial + 2 retries
392
- })
393
-
394
- it('should use default retry settings when not specified', async () => {
395
- const contractNotFoundResponse = {
396
- status: '0',
397
- result: 'Contract not found'
398
- }
399
-
400
- mockedFetch.mockResolvedValue(createMockResponse(contractNotFoundResponse))
401
-
402
- const result = await submitVerification({
403
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
404
- buildInfo: mockBuildInfo,
405
- contract: mockContract,
406
- network: mockNetwork
407
- // No maxRetries or retryDelayMs specified - should use defaults
408
- }, 'test-api-key')
409
-
410
- expect(result.success).toBe(false)
411
- expect(result.message).toBe('Verification failed after 4 attempts. Last error: Contract not found')
412
- expect(mockedFetch).toHaveBeenCalledTimes(4) // Initial + 3 default retries
413
- })
414
-
415
- it('should retry for network errors with contract not found message', async () => {
416
- const contractNotFoundError = new Error('Unable to locate ContractCode')
417
- const successResponse = {
418
- status: '1',
419
- result: 'test-guid-network-retry'
420
- }
421
-
422
- mockedFetch
423
- .mockRejectedValueOnce(contractNotFoundError)
424
- .mockResolvedValueOnce(createMockResponse(successResponse))
425
-
426
- const result = await submitVerification({
427
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
428
- buildInfo: mockBuildInfo,
429
- contract: mockContract,
430
- network: mockNetwork,
431
- maxRetries: 2
432
- }, 'test-api-key')
433
-
434
- expect(result.success).toBe(true)
435
- expect(result.guid).toBe('test-guid-network-retry')
436
- expect(mockedFetch).toHaveBeenCalledTimes(2)
437
- })
438
-
439
- it('should not retry for network errors without contract not found message', async () => {
440
- const networkError = new Error('Network timeout')
441
-
442
- mockedFetch.mockRejectedValueOnce(networkError)
443
-
444
- const result = await submitVerification({
445
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
446
- buildInfo: mockBuildInfo,
447
- contract: mockContract,
448
- network: mockNetwork,
449
- maxRetries: 3
450
- }, 'test-api-key')
451
-
452
- expect(result.success).toBe(false)
453
- expect(result.message).toBe('API request failed: Network timeout')
454
- expect(mockedFetch).toHaveBeenCalledTimes(1) // No retries
455
- })
456
-
457
- it('should exhaust retries for network errors with contract not found message', async () => {
458
- const contractNotFoundError = new Error('Contract source code not verified')
459
-
460
- mockedFetch.mockRejectedValue(contractNotFoundError)
461
-
462
- const result = await submitVerification({
463
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
464
- buildInfo: mockBuildInfo,
465
- contract: mockContract,
466
- network: mockNetwork,
467
- maxRetries: 1,
468
- retryDelayMs: 10
469
- }, 'test-api-key')
470
-
471
- expect(result.success).toBe(false)
472
- expect(result.message).toBe('API request failed: Contract source code not verified')
473
- expect(mockedFetch).toHaveBeenCalledTimes(2) // Initial + 1 retry
474
- })
475
-
476
- it('should handle case-insensitive error message matching', async () => {
477
- const contractNotFoundResponse = {
478
- status: '0',
479
- result: 'UNABLE TO LOCATE CONTRACTCODE'
480
- }
481
- const successResponse = {
482
- status: '1',
483
- result: 'test-guid-case-insensitive'
484
- }
485
-
486
- mockedFetch
487
- .mockResolvedValueOnce(createMockResponse(contractNotFoundResponse))
488
- .mockResolvedValueOnce(createMockResponse(successResponse))
489
-
490
- const result = await submitVerification({
491
- address: '0x742d35Cc6596C743B2c8d12Cd84d5B8FbA4F3C',
492
- buildInfo: mockBuildInfo,
493
- contract: mockContract,
494
- network: mockNetwork,
495
- maxRetries: 1
496
- }, 'test-api-key')
497
-
498
- expect(result.success).toBe(true)
499
- expect(result.guid).toBe('test-guid-case-insensitive')
500
- expect(mockedFetch).toHaveBeenCalledTimes(2)
501
- })
502
- })
503
- })
504
-
505
- describe('checkVerificationStatus', () => {
506
- it('should return success status', async () => {
507
- const mockResponseData = {
508
- status: '1',
509
- result: 'Pass - Verified'
510
- }
511
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
512
-
513
- const status = await checkVerificationStatus('test-guid', 'test-api-key', mockNetwork)
514
-
515
- expect(status.isComplete).toBe(true)
516
- expect(status.isSuccess).toBe(true)
517
- expect(status.message).toBe('Verification successful')
518
- })
519
-
520
- it('should return pending status', async () => {
521
- const mockResponseData = {
522
- status: '0',
523
- result: 'Pending in queue'
524
- }
525
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
526
-
527
- const status = await checkVerificationStatus('test-guid', 'test-api-key', mockNetwork)
528
-
529
- expect(status.isComplete).toBe(false)
530
- expect(status.isSuccess).toBe(false)
531
- expect(status.message).toBe('Verification pending')
532
- })
533
-
534
- it('should return failure status', async () => {
535
- const mockResponseData = {
536
- status: '0',
537
- result: 'Fail - Unable to verify'
538
- }
539
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
540
-
541
- const status = await checkVerificationStatus('test-guid', 'test-api-key', mockNetwork)
542
-
543
- expect(status.isComplete).toBe(true)
544
- expect(status.isSuccess).toBe(false)
545
- expect(status.message).toBe('Fail - Unable to verify')
546
- })
547
-
548
- it('should treat "Already Verified" as success in status check', async () => {
549
- const mockResponseData = {
550
- status: '0',
551
- result: 'Already Verified'
552
- }
553
- mockedFetch.mockResolvedValueOnce(createMockResponse(mockResponseData))
554
-
555
- const status = await checkVerificationStatus('test-guid', 'test-api-key', mockNetwork)
556
-
557
- expect(status.isComplete).toBe(true)
558
- expect(status.isSuccess).toBe(true)
559
- expect(status.message).toBe('Contract is already verified')
560
- })
561
- })
562
-
563
- describe('waitForVerification', () => {
564
- it('should return when verification completes successfully', async () => {
565
- mockedFetch
566
- .mockResolvedValueOnce(createMockResponse({
567
- status: '0', result: 'Pending in queue'
568
- }))
569
- .mockResolvedValueOnce(createMockResponse({
570
- status: '1', result: 'Pass - Verified'
571
- }))
572
-
573
- const status = await waitForVerification('test-guid', 'test-api-key', mockNetwork, 10000)
574
-
575
- expect(status.isComplete).toBe(true)
576
- expect(status.isSuccess).toBe(true)
577
- expect(mockedFetch).toHaveBeenCalledTimes(2)
578
- })
579
-
580
- it('should timeout after specified duration', async () => {
581
- mockedFetch.mockResolvedValue(createMockResponse({
582
- status: '0', result: 'Pending in queue'
583
- }))
584
-
585
- await expect(
586
- waitForVerification('test-guid', 'test-api-key', mockNetwork, 1000)
587
- ).rejects.toThrow('Verification timed out after 1 seconds')
588
-
589
- expect(mockedFetch).toHaveBeenCalledTimes(1)
590
- })
591
- })
592
-
593
- describe('isContractAlreadyVerified', () => {
594
- const testAddress = '0x1234567890123456789012345678901234567890'
595
- const testApiKey = 'test-api-key'
596
-
597
- beforeEach(() => {
598
- mockedFetch.mockClear()
599
- })
600
-
601
- it('should return true when contract is verified', async () => {
602
- mockedFetch.mockResolvedValueOnce(createMockResponse({
603
- status: '1',
604
- result: [{
605
- SourceCode: 'pragma solidity ^0.8.0; contract MyToken { }'
606
- }]
607
- }))
608
-
609
- const result = await isContractAlreadyVerified(testAddress, testApiKey, mockNetwork)
610
-
611
- expect(result).toBe(true)
612
- expect(mockedFetch).toHaveBeenCalledWith(
613
- expect.stringContaining('chainid=1'),
614
- expect.objectContaining({
615
- method: 'GET'
616
- })
617
- )
618
- })
619
-
620
- it('should return false when contract is not verified (empty source code)', async () => {
621
- mockedFetch.mockResolvedValueOnce(createMockResponse({
622
- status: '1',
623
- result: [{
624
- SourceCode: ''
625
- }]
626
- }))
627
-
628
- const result = await isContractAlreadyVerified(testAddress, testApiKey, mockNetwork)
629
-
630
- expect(result).toBe(false)
631
- })
632
-
633
- it('should return false when API returns status 0', async () => {
634
- mockedFetch.mockResolvedValueOnce(createMockResponse({
635
- status: '0',
636
- result: 'Contract source code not verified'
637
- }))
638
-
639
- const result = await isContractAlreadyVerified(testAddress, testApiKey, mockNetwork)
640
-
641
- expect(result).toBe(false)
642
- })
643
-
644
- it('should return false when result array is empty', async () => {
645
- mockedFetch.mockResolvedValueOnce(createMockResponse({
646
- status: '1',
647
- result: []
648
- }))
649
-
650
- const result = await isContractAlreadyVerified(testAddress, testApiKey, mockNetwork)
651
-
652
- expect(result).toBe(false)
653
- })
654
-
655
- it('should return false and log warning when API request fails', async () => {
656
- const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
657
- mockedFetch.mockRejectedValueOnce(new Error('Network error'))
658
-
659
- const result = await isContractAlreadyVerified(testAddress, testApiKey, mockNetwork)
660
-
661
- expect(result).toBe(false)
662
- expect(consoleSpy).toHaveBeenCalledWith(
663
- expect.stringContaining(`Failed to check verification status for ${testAddress}`)
664
- )
665
-
666
- consoleSpy.mockRestore()
667
- })
668
-
669
- it('should return false when HTTP response is not ok', async () => {
670
- const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
671
- mockedFetch.mockResolvedValueOnce({
672
- ok: false,
673
- status: 500,
674
- statusText: 'Internal Server Error'
675
- } as Response)
676
-
677
- const result = await isContractAlreadyVerified(testAddress, testApiKey, mockNetwork)
678
-
679
- expect(result).toBe(false)
680
- expect(consoleSpy).toHaveBeenCalledWith(
681
- expect.stringContaining('Failed to check verification status')
682
- )
683
-
684
- consoleSpy.mockRestore()
685
- })
686
-
687
- it('should use correct chainId in API call for different networks', async () => {
688
- const arbitrumNetwork: Network = {
689
- name: 'Arbitrum One',
690
- chainId: 42161,
691
- rpcUrl: 'https://arb1.arbitrum.io/rpc',
692
- supports: ['etherscan_v2']
693
- }
694
-
695
- mockedFetch.mockResolvedValueOnce(createMockResponse({
696
- status: '1',
697
- result: [{
698
- SourceCode: 'contract code'
699
- }]
700
- }))
701
-
702
- await isContractAlreadyVerified(testAddress, testApiKey, arbitrumNetwork)
703
-
704
- expect(mockedFetch).toHaveBeenCalledWith(
705
- expect.stringContaining('chainid=42161'),
706
- expect.any(Object)
707
- )
708
- })
709
- })
710
- })