@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,547 +0,0 @@
|
|
|
1
|
-
import { Network } from '../types/network'
|
|
2
|
-
import { BuildInfo } from '../types/buildinfo'
|
|
3
|
-
import { Contract } from '../types/contracts'
|
|
4
|
-
import { DeploymentEventEmitter } from '../events/emitter'
|
|
5
|
-
|
|
6
|
-
// Generic verification platform interface
|
|
7
|
-
export interface VerificationPlatform {
|
|
8
|
-
/**
|
|
9
|
-
* The name of this verification platform (e.g., 'etherscan_v2', 'sourcify')
|
|
10
|
-
*/
|
|
11
|
-
readonly name: string
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check if this platform supports verification on the given network
|
|
15
|
-
*/
|
|
16
|
-
supportsNetwork(network: Network): boolean
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Check if the required configuration (API keys, etc.) is available
|
|
20
|
-
*/
|
|
21
|
-
isConfigured(): boolean
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get a description of what configuration is missing (for error messages)
|
|
25
|
-
*/
|
|
26
|
-
getConfigurationRequirements(): string
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Check if a contract is already verified on this platform
|
|
30
|
-
*/
|
|
31
|
-
isContractAlreadyVerified(address: string, network: Network): Promise<boolean>
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Submit a contract for verification
|
|
35
|
-
*/
|
|
36
|
-
verifyContract(request: VerificationRequest): Promise<VerificationResult>
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface VerificationRequest {
|
|
40
|
-
contract: Contract // Contract object
|
|
41
|
-
buildInfo: BuildInfo
|
|
42
|
-
address: string
|
|
43
|
-
constructorArguments?: string // Hex encoded constructor args
|
|
44
|
-
network: Network
|
|
45
|
-
maxRetries?: number // Number of retries for "contract not found" errors
|
|
46
|
-
retryDelayMs?: number // Delay between retries in milliseconds
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface VerificationResult {
|
|
50
|
-
success: boolean
|
|
51
|
-
guid?: string
|
|
52
|
-
message: string
|
|
53
|
-
isAlreadyVerified?: boolean // Indicates if verification was skipped because already verified
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface VerificationStatus {
|
|
57
|
-
isComplete: boolean
|
|
58
|
-
isSuccess: boolean
|
|
59
|
-
message: string
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Checks if an error message indicates that the contract code was not found
|
|
64
|
-
*/
|
|
65
|
-
function isContractNotFoundError(message: string): boolean {
|
|
66
|
-
return message.toLowerCase().includes('unable to locate contractcode') ||
|
|
67
|
-
message.toLowerCase().includes('contract source code not verified') ||
|
|
68
|
-
message.toLowerCase().includes('contract not found')
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Checks if an error message indicates that the contract is already verified
|
|
73
|
-
*/
|
|
74
|
-
function isAlreadyVerifiedError(message: string): boolean {
|
|
75
|
-
return message.toLowerCase().includes('already verified') ||
|
|
76
|
-
message.toLowerCase().includes('contract source code already verified')
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Extracts the full compiler version with commit hash from contract metadata
|
|
81
|
-
*/
|
|
82
|
-
function getFullCompilerVersion(buildInfo: BuildInfo): string {
|
|
83
|
-
// Try to extract from any contract's metadata
|
|
84
|
-
for (const [sourceName, contracts] of Object.entries(buildInfo.output.contracts)) {
|
|
85
|
-
for (const [contractName, contract] of Object.entries(contracts)) {
|
|
86
|
-
if (contract.metadata) {
|
|
87
|
-
try {
|
|
88
|
-
const metadata = JSON.parse(contract.metadata)
|
|
89
|
-
if (metadata.compiler?.version) {
|
|
90
|
-
return metadata.compiler.version
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
// Continue to next contract if metadata parsing fails
|
|
94
|
-
continue
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Fallback to the basic version if metadata extraction fails
|
|
101
|
-
// Use solcLongVersion if available, otherwise fallback to solcVersion
|
|
102
|
-
return buildInfo.solcLongVersion || buildInfo.solcVersion
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Gets the Etherscan v2 API URL (unified endpoint for all chains)
|
|
107
|
-
*/
|
|
108
|
-
function getEtherscanApiUrl(chainId: number): string {
|
|
109
|
-
return `https://api.etherscan.io/v2/api?chainid=${chainId}`
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Checks if a contract is already verified on Etherscan using the v2 API
|
|
114
|
-
*/
|
|
115
|
-
export async function isContractAlreadyVerified(
|
|
116
|
-
address: string,
|
|
117
|
-
apiKey: string,
|
|
118
|
-
network: Network
|
|
119
|
-
): Promise<boolean> {
|
|
120
|
-
const apiUrl = getEtherscanApiUrl(network.chainId)
|
|
121
|
-
|
|
122
|
-
const params = new URLSearchParams({
|
|
123
|
-
module: 'contract',
|
|
124
|
-
action: 'getsourcecode',
|
|
125
|
-
address: address,
|
|
126
|
-
apikey: apiKey
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
const response = await fetch(`${apiUrl}&${params.toString()}`, {
|
|
131
|
-
method: 'GET',
|
|
132
|
-
signal: AbortSignal.timeout(15000), // 15 second timeout
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
if (!response.ok) {
|
|
136
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const data = await response.json() as { status: string; result: any }
|
|
140
|
-
|
|
141
|
-
// If status is "1" and result contains source code, the contract is verified
|
|
142
|
-
if (data.status === '1' && Array.isArray(data.result) && data.result.length > 0) {
|
|
143
|
-
const sourceCode = data.result[0]?.SourceCode
|
|
144
|
-
return !!(sourceCode && sourceCode.length > 0)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return false
|
|
148
|
-
} catch (error) {
|
|
149
|
-
// If we can't determine the verification status, assume it's not verified
|
|
150
|
-
// and let the verification attempt proceed (which will handle any errors)
|
|
151
|
-
console.warn(`Failed to check verification status for ${address}: ${error instanceof Error ? error.message : String(error)}`)
|
|
152
|
-
return false
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Internal function to perform a single verification attempt
|
|
158
|
-
*/
|
|
159
|
-
async function submitVerificationAttempt(request: VerificationRequest, apiKey: string): Promise<VerificationResult> {
|
|
160
|
-
const apiUrl = getEtherscanApiUrl(request.network.chainId)
|
|
161
|
-
|
|
162
|
-
// Extract the fully qualified contract name from the contract object
|
|
163
|
-
const contractName = `${request.contract.sourceName}:${request.contract.contractName}`
|
|
164
|
-
|
|
165
|
-
// Clean the input to only include standard Solidity compiler input format keys
|
|
166
|
-
const cleanedInput = {
|
|
167
|
-
language: request.buildInfo.input.language,
|
|
168
|
-
sources: request.buildInfo.input.sources,
|
|
169
|
-
settings: {
|
|
170
|
-
// Only include standard settings that Etherscan supports
|
|
171
|
-
...(request.buildInfo.input.settings.optimizer && { optimizer: request.buildInfo.input.settings.optimizer }),
|
|
172
|
-
...(request.buildInfo.input.settings.evmVersion && { evmVersion: request.buildInfo.input.settings.evmVersion }),
|
|
173
|
-
...(request.buildInfo.input.settings.remappings && { remappings: request.buildInfo.input.settings.remappings }),
|
|
174
|
-
...(request.buildInfo.input.settings.viaIR && { viaIR: request.buildInfo.input.settings.viaIR }),
|
|
175
|
-
...(request.buildInfo.input.settings.libraries && { libraries: request.buildInfo.input.settings.libraries }),
|
|
176
|
-
outputSelection: request.buildInfo.input.settings.outputSelection,
|
|
177
|
-
...(request.buildInfo.input.settings.metadata && { metadata: request.buildInfo.input.settings.metadata })
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const sourceCode = JSON.stringify(cleanedInput)
|
|
182
|
-
|
|
183
|
-
// Extract the full compiler version with commit hash from contract metadata
|
|
184
|
-
const fullCompilerVersion = getFullCompilerVersion(request.buildInfo)
|
|
185
|
-
|
|
186
|
-
const formData = new URLSearchParams({
|
|
187
|
-
module: 'contract',
|
|
188
|
-
action: 'verifysourcecode',
|
|
189
|
-
codeformat: 'solidity-standard-json-input',
|
|
190
|
-
sourceCode,
|
|
191
|
-
contractaddress: request.address,
|
|
192
|
-
contractname: contractName,
|
|
193
|
-
compilerversion: `v${fullCompilerVersion}`,
|
|
194
|
-
apikey: apiKey,
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
if (request.constructorArguments) {
|
|
198
|
-
// Remove 0x prefix if present
|
|
199
|
-
const constructorArgs = request.constructorArguments.startsWith('0x')
|
|
200
|
-
? request.constructorArguments.slice(2)
|
|
201
|
-
: request.constructorArguments
|
|
202
|
-
formData.append('constructorArguements', constructorArgs) // Note: Etherscan API has this typo
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const response = await fetch(apiUrl, {
|
|
206
|
-
method: 'POST',
|
|
207
|
-
headers: {
|
|
208
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
209
|
-
},
|
|
210
|
-
body: formData.toString(),
|
|
211
|
-
signal: AbortSignal.timeout(30000), // 30 second timeout
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
if (!response.ok) {
|
|
215
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const data = await response.json() as { status: string; result: string }
|
|
219
|
-
|
|
220
|
-
if (data.status === '1') {
|
|
221
|
-
return {
|
|
222
|
-
success: true,
|
|
223
|
-
guid: data.result,
|
|
224
|
-
message: 'Verification submitted successfully'
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
const errorMessage = data.result || 'Unknown error occurred'
|
|
228
|
-
|
|
229
|
-
// Treat "Already Verified" as a success case
|
|
230
|
-
if (isAlreadyVerifiedError(errorMessage)) {
|
|
231
|
-
return {
|
|
232
|
-
success: true,
|
|
233
|
-
message: 'Contract is already verified'
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
success: false,
|
|
239
|
-
message: errorMessage
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Submits a contract for verification to Etherscan using the v2 API with retry logic
|
|
246
|
-
*/
|
|
247
|
-
export async function submitVerification(
|
|
248
|
-
request: VerificationRequest,
|
|
249
|
-
apiKey: string,
|
|
250
|
-
eventEmitter?: DeploymentEventEmitter
|
|
251
|
-
): Promise<VerificationResult> {
|
|
252
|
-
const maxRetries = request.maxRetries ?? 3
|
|
253
|
-
const retryDelayMs = request.retryDelayMs ?? 5000 // 5 seconds default
|
|
254
|
-
|
|
255
|
-
let lastError: string = ''
|
|
256
|
-
|
|
257
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
258
|
-
try {
|
|
259
|
-
const result = await submitVerificationAttempt(request, apiKey)
|
|
260
|
-
|
|
261
|
-
// If successful or if it's not a "contract not found" error, return immediately
|
|
262
|
-
if (result.success || !isContractNotFoundError(result.message)) {
|
|
263
|
-
return result
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Store the error message for potential retry
|
|
267
|
-
lastError = result.message
|
|
268
|
-
|
|
269
|
-
// If this is the last attempt, don't wait
|
|
270
|
-
if (attempt === maxRetries) {
|
|
271
|
-
break
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Emit retry event if emitter is available
|
|
275
|
-
if (eventEmitter) {
|
|
276
|
-
eventEmitter.emitEvent({
|
|
277
|
-
type: 'verification_retry',
|
|
278
|
-
level: 'info',
|
|
279
|
-
data: {
|
|
280
|
-
platform: 'etherscan_v2',
|
|
281
|
-
attempt: attempt + 1,
|
|
282
|
-
maxRetries: maxRetries + 1,
|
|
283
|
-
error: lastError
|
|
284
|
-
}
|
|
285
|
-
})
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Wait before retrying
|
|
289
|
-
await new Promise(resolve => setTimeout(resolve, retryDelayMs))
|
|
290
|
-
|
|
291
|
-
} catch (error) {
|
|
292
|
-
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
293
|
-
|
|
294
|
-
// If it's a "contract not found" type error and we have retries left, continue
|
|
295
|
-
if (isContractNotFoundError(errorMessage) && attempt < maxRetries) {
|
|
296
|
-
lastError = errorMessage
|
|
297
|
-
await new Promise(resolve => setTimeout(resolve, retryDelayMs))
|
|
298
|
-
continue
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// For other errors or if we're out of retries, return the error
|
|
302
|
-
return {
|
|
303
|
-
success: false,
|
|
304
|
-
message: `API request failed: ${errorMessage}`
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// All retries exhausted
|
|
310
|
-
return {
|
|
311
|
-
success: false,
|
|
312
|
-
message: `Verification failed after ${maxRetries + 1} attempts. Last error: ${lastError}`
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Checks the verification status of a submitted contract
|
|
318
|
-
*/
|
|
319
|
-
export async function checkVerificationStatus(
|
|
320
|
-
guid: string,
|
|
321
|
-
apiKey: string,
|
|
322
|
-
network: Network
|
|
323
|
-
): Promise<VerificationStatus> {
|
|
324
|
-
const apiUrl = getEtherscanApiUrl(network.chainId)
|
|
325
|
-
|
|
326
|
-
const params = new URLSearchParams({
|
|
327
|
-
module: 'contract',
|
|
328
|
-
action: 'checkverifystatus',
|
|
329
|
-
guid,
|
|
330
|
-
apikey: apiKey
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
try {
|
|
334
|
-
const response = await fetch(`${apiUrl}&${params.toString()}`, {
|
|
335
|
-
method: 'GET',
|
|
336
|
-
signal: AbortSignal.timeout(15000), // 15 second timeout
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
if (!response.ok) {
|
|
340
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const data = await response.json() as { status: string; result: string }
|
|
344
|
-
|
|
345
|
-
if (data.status === '1') {
|
|
346
|
-
return {
|
|
347
|
-
isComplete: true,
|
|
348
|
-
isSuccess: true,
|
|
349
|
-
message: 'Verification successful'
|
|
350
|
-
}
|
|
351
|
-
} else if (data.status === '0') {
|
|
352
|
-
const result = data.result || ''
|
|
353
|
-
if (result.includes('Pending')) {
|
|
354
|
-
return {
|
|
355
|
-
isComplete: false,
|
|
356
|
-
isSuccess: false,
|
|
357
|
-
message: 'Verification pending'
|
|
358
|
-
}
|
|
359
|
-
} else if (isAlreadyVerifiedError(result)) {
|
|
360
|
-
// Treat "Already Verified" as success during status check
|
|
361
|
-
return {
|
|
362
|
-
isComplete: true,
|
|
363
|
-
isSuccess: true,
|
|
364
|
-
message: 'Contract is already verified'
|
|
365
|
-
}
|
|
366
|
-
} else {
|
|
367
|
-
return {
|
|
368
|
-
isComplete: true,
|
|
369
|
-
isSuccess: false,
|
|
370
|
-
message: result || 'Verification failed'
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
} else {
|
|
374
|
-
return {
|
|
375
|
-
isComplete: true,
|
|
376
|
-
isSuccess: false,
|
|
377
|
-
message: data.result || 'Unknown verification status'
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
} catch (error) {
|
|
381
|
-
throw new Error(`Failed to check verification status: ${error instanceof Error ? error.message : String(error)}`)
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Polls verification status until completion or timeout
|
|
387
|
-
*/
|
|
388
|
-
export async function waitForVerification(
|
|
389
|
-
guid: string,
|
|
390
|
-
apiKey: string,
|
|
391
|
-
network: Network,
|
|
392
|
-
timeoutMs: number = 300000 // 5 minute default timeout
|
|
393
|
-
): Promise<VerificationStatus> {
|
|
394
|
-
const startTime = Date.now()
|
|
395
|
-
const pollInterval = 5000 // Poll every 5 seconds
|
|
396
|
-
|
|
397
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
398
|
-
const status = await checkVerificationStatus(guid, apiKey, network)
|
|
399
|
-
|
|
400
|
-
if (status.isComplete) {
|
|
401
|
-
return status
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Wait before polling again
|
|
405
|
-
await new Promise(resolve => setTimeout(resolve, pollInterval))
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
throw new Error(`Verification timed out after ${timeoutMs / 1000} seconds`)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Etherscan verification platform implementation
|
|
413
|
-
*/
|
|
414
|
-
export class EtherscanVerificationPlatform implements VerificationPlatform {
|
|
415
|
-
readonly name = 'etherscan_v2'
|
|
416
|
-
private apiKey?: string
|
|
417
|
-
|
|
418
|
-
constructor(apiKey?: string) {
|
|
419
|
-
this.apiKey = apiKey
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
supportsNetwork(network: Network): boolean {
|
|
423
|
-
// Only support networks that explicitly include this platform in their supports list
|
|
424
|
-
return Array.isArray(network.supports) && network.supports.includes(this.name)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
isConfigured(): boolean {
|
|
428
|
-
return !!this.apiKey
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
getConfigurationRequirements(): string {
|
|
432
|
-
return 'Etherscan API key is required. Set --etherscan-api-key or ETHERSCAN_API_KEY environment variable.'
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
async isContractAlreadyVerified(address: string, network: Network): Promise<boolean> {
|
|
436
|
-
if (!this.apiKey) {
|
|
437
|
-
throw new Error('Etherscan API key not configured')
|
|
438
|
-
}
|
|
439
|
-
return isContractAlreadyVerified(address, this.apiKey, network)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
async verifyContract(request: VerificationRequest): Promise<VerificationResult> {
|
|
443
|
-
if (!this.apiKey) {
|
|
444
|
-
throw new Error('Etherscan API key not configured')
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Check if already verified first
|
|
448
|
-
const alreadyVerified = await this.isContractAlreadyVerified(request.address, request.network)
|
|
449
|
-
if (alreadyVerified) {
|
|
450
|
-
return {
|
|
451
|
-
success: true,
|
|
452
|
-
message: 'Contract was already verified (checked before attempting verification)',
|
|
453
|
-
isAlreadyVerified: true
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Submit verification with API key
|
|
458
|
-
const verificationResult = await submitVerification(request, this.apiKey)
|
|
459
|
-
|
|
460
|
-
if (!verificationResult.success) {
|
|
461
|
-
return verificationResult
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// If we have a guid, wait for verification to complete
|
|
465
|
-
if (verificationResult.guid) {
|
|
466
|
-
const verificationStatus = await waitForVerification(verificationResult.guid, this.apiKey, request.network)
|
|
467
|
-
|
|
468
|
-
if (!verificationStatus.isSuccess) {
|
|
469
|
-
return {
|
|
470
|
-
success: false,
|
|
471
|
-
message: `Verification failed: ${verificationStatus.message}`
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return {
|
|
476
|
-
success: true,
|
|
477
|
-
guid: verificationResult.guid,
|
|
478
|
-
message: 'Contract verified successfully'
|
|
479
|
-
}
|
|
480
|
-
} else {
|
|
481
|
-
// Contract was already verified during submission
|
|
482
|
-
return {
|
|
483
|
-
success: true,
|
|
484
|
-
message: 'Contract was already verified',
|
|
485
|
-
isAlreadyVerified: true
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Registry for verification platforms
|
|
493
|
-
*/
|
|
494
|
-
export class VerificationPlatformRegistry {
|
|
495
|
-
private platforms = new Map<string, VerificationPlatform>()
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Register a verification platform
|
|
499
|
-
*/
|
|
500
|
-
register(platform: VerificationPlatform): void {
|
|
501
|
-
this.platforms.set(platform.name, platform)
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Get a verification platform by name
|
|
506
|
-
*/
|
|
507
|
-
get(platformName: string): VerificationPlatform | undefined {
|
|
508
|
-
return this.platforms.get(platformName)
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Get all available platforms
|
|
513
|
-
*/
|
|
514
|
-
getAll(): VerificationPlatform[] {
|
|
515
|
-
return Array.from(this.platforms.values())
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/**
|
|
519
|
-
* Get all platforms that support the given network
|
|
520
|
-
*/
|
|
521
|
-
getSupportedPlatforms(network: Network): VerificationPlatform[] {
|
|
522
|
-
return this.getAll().filter(platform => platform.supportsNetwork(network))
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Get all configured platforms that support the given network
|
|
527
|
-
*/
|
|
528
|
-
getConfiguredPlatforms(network: Network): VerificationPlatform[] {
|
|
529
|
-
return this.getSupportedPlatforms(network).filter(platform => platform.isConfigured())
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Default verification platform registry instance
|
|
535
|
-
*/
|
|
536
|
-
export function createDefaultVerificationRegistry(etherscanApiKey?: string): VerificationPlatformRegistry {
|
|
537
|
-
const registry = new VerificationPlatformRegistry()
|
|
538
|
-
|
|
539
|
-
// Register Etherscan platform
|
|
540
|
-
registry.register(new EtherscanVerificationPlatform(etherscanApiKey))
|
|
541
|
-
|
|
542
|
-
// Register Sourcify platform
|
|
543
|
-
const { SourcifyVerificationPlatform } = require('./sourcify')
|
|
544
|
-
registry.register(new SourcifyVerificationPlatform())
|
|
545
|
-
|
|
546
|
-
return registry
|
|
547
|
-
}
|