Skip to content

Commit c1adc9c

Browse files
authored
fix/verify-contract: - fall back to source-based extraction if there's a mismatch in ABI (#1878)
1 parent f447c85 commit c1adc9c

5 files changed

Lines changed: 50 additions & 11 deletions

File tree

.changeset/purple-items-argue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@layerzerolabs/verify-contract": minor
3+
---
4+
5+
Verify contract checks deployment ABI first, but fall back to source-based extraction if there's a mismatch

packages/verify-contract/src/common/abi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { TypeName, type FunctionDefinition } from '@solidity-parser/parser/dist/
1212
* @param args Constructor arguments
1313
* @returns
1414
*/
15-
export const encodeContructorArguments = (abi: JsonFragment[], args: unknown[] | undefined): string | undefined => {
15+
export const encodeConstructorArguments = (abi: JsonFragment[], args: unknown[] | undefined): string | undefined => {
1616
if (args == null || args.length === 0) {
1717
return undefined
1818
}
@@ -24,7 +24,7 @@ export const encodeContructorArguments = (abi: JsonFragment[], args: unknown[] |
2424
return encodedConstructorArguments.slice(2)
2525
}
2626

27-
export const getContructorABIFromSource = (source: string): MinimalAbi => {
27+
export const getConstructorABIFromSource = (source: string): MinimalAbi => {
2828
try {
2929
// First we'll parse the source code and get the AST
3030
const ast = parser.parse(source)

packages/verify-contract/src/common/networks.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,12 @@ export const networks: Record<string, NetworkDefinition> = {
417417
aliases: ['stable-mainnet'],
418418
},
419419

420+
'stable-testnet': {
421+
chainId: 2201,
422+
apiUrl: ETHERSCAN_V2_URL,
423+
aliases: ['stable-testnet'],
424+
},
425+
420426
// Swellchain
421427
swellchain: {
422428
chainId: 1923,
@@ -484,6 +490,12 @@ export const networks: Record<string, NetworkDefinition> = {
484490
// Non-Etherscan Networks (custom explorers)
485491
// chainId is not used for these networks
486492

493+
// Codex
494+
codex: {
495+
chainId: 81224,
496+
apiUrl: 'https://explorer.codex.xyz/',
497+
},
498+
487499
// Astar
488500
astar: {
489501
chainId: 0,

packages/verify-contract/src/hardhat-deploy/verify.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { COLORS, RecordLogger, anonymizeValue, createRecordLogger } from '../com
77
import { tryCreateScanContractUrl } from '../common/url'
88
import { DeploymentSchema } from '../common/schema'
99
import { extractSolcInputFromMetadata } from './schema'
10-
import { encodeContructorArguments, getContructorABIFromSource } from '../common/abi'
10+
import { encodeConstructorArguments, getConstructorABIFromSource } from '../common/abi'
1111
import type {
1212
VerificationArtifact,
1313
VerificationResult,
@@ -100,7 +100,10 @@ export const verifyNonTarget = async (
100100
typeof contract.constructorArguments === 'string'
101101
? contract.constructorArguments
102102
: // For decoded constructor arguments we'll need to try and encoded them using the contract source
103-
encodeContructorArguments(getContructorABIFromSource(source.content), contract.constructorArguments)
103+
encodeConstructorArguments(
104+
getConstructorABIFromSource(source.content),
105+
contract.constructorArguments
106+
)
104107

105108
// Deployment metadata contains solcInput, just a bit rearranged
106109
const solcInput = extractSolcInputFromMetadata(deployment.metadata)
@@ -254,7 +257,26 @@ export const verifyTarget = async (
254257
const licenseType = findLicenseType(source.content)
255258

256259
// Constructor arguments need to come ABI-encoded but without the 0x
257-
const constructorArguments = encodeContructorArguments(deployment.abi, deployment.args)
260+
// Try using deployment ABI first, but fall back to source-based extraction if there's a mismatch
261+
let constructorArguments: string | undefined
262+
try {
263+
constructorArguments = encodeConstructorArguments(deployment.abi, deployment.args)
264+
} catch (error) {
265+
// If encoding fails due to argument mismatch, try extracting constructor from source
266+
// This handles cases where the ABI is incomplete but source code has the full signature
267+
try {
268+
const sourceBasedAbi = getConstructorABIFromSource(source.content)
269+
constructorArguments = encodeConstructorArguments(sourceBasedAbi, deployment.args)
270+
} catch (sourceError) {
271+
// If both fail, log a warning and skip this contract
272+
logger.warn(
273+
`Skipping contract ${contractName} in ${fileName} on network ${networkName} due to constructor encoding error: ${error}. ` +
274+
`Tried fallback to source-based extraction but that also failed: ${sourceError}`
275+
)
276+
277+
return []
278+
}
279+
}
258280

259281
// Deployment metadata contains solcInput, just a bit rearranged
260282
const solcInput = extractSolcInputFromMetadata(deployment.metadata)

packages/verify-contract/test/abi.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { encodeContructorArguments } from '@/common/abi'
1+
import { encodeConstructorArguments } from '@/common/abi'
22

33
describe('abi', () => {
4-
describe('encodeContructorArguments', () => {
4+
describe('encodeConstructorArguments', () => {
55
it('should return undefined if args are nullish', () => {
6-
expect(encodeContructorArguments([], undefined)).toBeUndefined()
6+
expect(encodeConstructorArguments([], undefined)).toBeUndefined()
77
})
88

99
it('should return undefined if args are empty', () => {
10-
expect(encodeContructorArguments([], [])).toBeUndefined()
10+
expect(encodeConstructorArguments([], [])).toBeUndefined()
1111
})
1212

1313
it('should throw an error if there is no constructor fragment', () => {
14-
expect(() => encodeContructorArguments([{}], [1])).toThrow('invalid fragment object')
14+
expect(() => encodeConstructorArguments([{}], [1])).toThrow('invalid fragment object')
1515
})
1616

1717
it.each([
@@ -50,7 +50,7 @@ describe('abi', () => {
5050
],
5151
],
5252
])('should return %s for arguments %j and ABI %j', (encoded, args, abi) => {
53-
expect(encodeContructorArguments(abi, args)).toBe(encoded)
53+
expect(encodeConstructorArguments(abi, args)).toBe(encoded)
5454
})
5555
})
5656
})

0 commit comments

Comments
 (0)