@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.
- package/README.md +27 -0
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +22 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.js +40 -0
- package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +3 -2
- package/dist/lib/core/context.d.ts.map +1 -1
- package/dist/lib/core/context.js +3 -2
- package/dist/lib/core/context.js.map +1 -1
- package/dist/lib/core/engine.d.ts +4 -0
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +173 -0
- package/dist/lib/core/engine.js.map +1 -1
- package/dist/lib/core/signer.d.ts +7 -0
- package/dist/lib/core/signer.d.ts.map +1 -0
- package/dist/lib/core/signer.js +60 -0
- package/dist/lib/core/signer.js.map +1 -0
- package/dist/lib/parsers/__tests__/source.spec.js +37 -0
- package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
- package/dist/lib/parsers/source.js +1 -1
- package/dist/lib/parsers/source.js.map +1 -1
- package/dist/lib/provenance.js +51 -2
- package/dist/lib/provenance.js.map +1 -1
- package/dist/lib/types/actions.d.ts +26 -2
- package/dist/lib/types/actions.d.ts.map +1 -1
- package/dist/lib/types/actions.js +3 -0
- package/dist/lib/types/actions.js.map +1 -1
- package/dist/lib/types/source.d.ts +2 -0
- package/dist/lib/types/source.d.ts.map +1 -1
- package/package.json +4 -1
- package/.eslintrc.json +0 -29
- package/.github/workflows/ci.yml +0 -181
- package/CONCEPT.md +0 -24
- package/contracts/checked-call.huff +0 -65
- package/eslint.config.js +0 -48
- package/examples/jobs/guards-v1.yaml +0 -17
- package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
- package/examples/jobs/sequence-v1.yaml +0 -59
- package/examples/templates/sequence-factory-v1.yaml +0 -56
- package/jest.config.js +0 -25
- package/src/cli.ts +0 -18
- package/src/commands/common.ts +0 -61
- package/src/commands/dry.ts +0 -209
- package/src/commands/etherscan.ts +0 -360
- package/src/commands/index.ts +0 -6
- package/src/commands/list.ts +0 -262
- package/src/commands/provenance.ts +0 -120
- package/src/commands/run.ts +0 -146
- package/src/commands/utils.ts +0 -215
- package/src/index.ts +0 -67
- package/src/lib/__tests__/deployer-events.spec.ts +0 -338
- package/src/lib/__tests__/deployer.spec.ts +0 -2269
- package/src/lib/__tests__/network-loader.spec.ts +0 -150
- package/src/lib/__tests__/network-selection.spec.ts +0 -41
- package/src/lib/__tests__/network-utils.spec.ts +0 -230
- package/src/lib/__tests__/provenance.spec.ts +0 -208
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
- package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
- package/src/lib/contracts/repository.ts +0 -411
- package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
- package/src/lib/core/__tests__/context.spec.ts +0 -37
- package/src/lib/core/__tests__/engine.spec.ts +0 -2005
- package/src/lib/core/__tests__/graph.spec.ts +0 -125
- package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
- package/src/lib/core/__tests__/loader.spec.ts +0 -367
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
- package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
- package/src/lib/core/__tests__/static-action.spec.ts +0 -172
- package/src/lib/core/context.ts +0 -127
- package/src/lib/core/engine.ts +0 -1834
- package/src/lib/core/graph.ts +0 -252
- package/src/lib/core/loader.ts +0 -253
- package/src/lib/core/resolver.ts +0 -873
- package/src/lib/deployer.ts +0 -1005
- package/src/lib/events/__tests__/event-system.spec.ts +0 -392
- package/src/lib/events/cli-adapter.ts +0 -369
- package/src/lib/events/emitter.ts +0 -62
- package/src/lib/events/index.ts +0 -3
- package/src/lib/events/types.ts +0 -520
- package/src/lib/index.ts +0 -17
- package/src/lib/network-loader.ts +0 -90
- package/src/lib/network-selection.ts +0 -73
- package/src/lib/network-utils.ts +0 -64
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
- package/src/lib/parsers/__tests__/job.spec.ts +0 -439
- package/src/lib/parsers/__tests__/source.spec.ts +0 -134
- package/src/lib/parsers/__tests__/template.spec.ts +0 -111
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
- package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
- package/src/lib/parsers/artifact/index.ts +0 -27
- package/src/lib/parsers/artifact/types.ts +0 -9
- package/src/lib/parsers/buildinfo.ts +0 -127
- package/src/lib/parsers/constants.ts +0 -56
- package/src/lib/parsers/index.ts +0 -6
- package/src/lib/parsers/job.ts +0 -160
- package/src/lib/parsers/source.ts +0 -129
- package/src/lib/parsers/template.ts +0 -135
- package/src/lib/provenance.ts +0 -785
- package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
- package/src/lib/std/templates/assured-deployment.yaml +0 -46
- package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
- package/src/lib/std/templates/erc-2470.yaml +0 -70
- package/src/lib/std/templates/min-balance.yaml +0 -35
- package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
- package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
- package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
- package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
- package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
- package/src/lib/types/actions.ts +0 -148
- package/src/lib/types/artifacts.ts +0 -21
- package/src/lib/types/buildinfo.ts +0 -116
- package/src/lib/types/conditions.ts +0 -50
- package/src/lib/types/contracts.ts +0 -26
- package/src/lib/types/definitions.ts +0 -77
- package/src/lib/types/index.ts +0 -9
- package/src/lib/types/network.ts +0 -33
- package/src/lib/types/project.ts +0 -9
- package/src/lib/types/source.ts +0 -26
- package/src/lib/types/task.ts +0 -9
- package/src/lib/types/values.ts +0 -221
- package/src/lib/utils/assertion.ts +0 -24
- package/src/lib/utils/validation.ts +0 -116
- package/src/lib/validation/contract-references.ts +0 -210
- package/src/lib/validation/index.ts +0 -1
- package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
- package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
- package/src/lib/verification/etherscan.ts +0 -547
- package/src/lib/verification/sourcify.ts +0 -248
- package/test_validation/artifacts/TestContract.json +0 -9
- package/test_validation/jobs/test-missing.yaml +0 -16
- package/test_validation/networks.yaml +0 -3
- 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
|
-
})
|