From 04dffe2333c42287a1a32d8384417b49a559d352 Mon Sep 17 00:00:00 2001 From: Jadi Date: Mon, 1 Dec 2025 11:50:34 -0800 Subject: [PATCH 1/4] feat/verify: using deployment ABI first, fall back to source-based extraction If encoding fails due to argument mismatch, try extracting constructor from source This handles cases where the ABI is incomplete but source code has the full signature --- .../src/hardhat-deploy/verify.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/verify-contract/src/hardhat-deploy/verify.ts b/packages/verify-contract/src/hardhat-deploy/verify.ts index 266eaf761..548d13031 100644 --- a/packages/verify-contract/src/hardhat-deploy/verify.ts +++ b/packages/verify-contract/src/hardhat-deploy/verify.ts @@ -254,7 +254,26 @@ export const verifyTarget = async ( const licenseType = findLicenseType(source.content) // Constructor arguments need to come ABI-encoded but without the 0x - const constructorArguments = encodeContructorArguments(deployment.abi, deployment.args) + // Try using deployment ABI first, but fall back to source-based extraction if there's a mismatch + let constructorArguments: string | undefined + try { + constructorArguments = encodeContructorArguments(deployment.abi, deployment.args) + } catch (error) { + // If encoding fails due to argument mismatch, try extracting constructor from source + // This handles cases where the ABI is incomplete but source code has the full signature + try { + const sourceBasedAbi = getContructorABIFromSource(source.content) + constructorArguments = encodeContructorArguments(sourceBasedAbi, deployment.args) + } catch (sourceError) { + // If both fail, log a warning and skip this contract + logger.warn( + `Skipping contract ${contractName} in ${fileName} on network ${networkName} due to constructor encoding error: ${error}. ` + + `Tried fallback to source-based extraction but that also failed: ${sourceError}` + ) + + return [] + } + } // Deployment metadata contains solcInput, just a bit rearranged const solcInput = extractSolcInputFromMetadata(deployment.metadata) From 00a61fe08178202b48f8fe86c13fa42848c1bb6d Mon Sep 17 00:00:00 2001 From: Jadi Date: Fri, 12 Dec 2025 14:44:02 -0800 Subject: [PATCH 2/4] feat/verify: adding stable-testnet to verify-contract networks --- packages/verify-contract/src/common/networks.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/verify-contract/src/common/networks.ts b/packages/verify-contract/src/common/networks.ts index 7c7777872..db35c95b8 100644 --- a/packages/verify-contract/src/common/networks.ts +++ b/packages/verify-contract/src/common/networks.ts @@ -417,6 +417,12 @@ export const networks: Record = { aliases: ['stable-mainnet'], }, + 'stable-testnet': { + chainId: 2201, + apiUrl: ETHERSCAN_V2_URL, + aliases: ['stable-testnet'], + }, + // Swellchain swellchain: { chainId: 1923, @@ -484,6 +490,12 @@ export const networks: Record = { // Non-Etherscan Networks (custom explorers) // chainId is not used for these networks + // Codex + codex: { + chainId: 81224, + apiUrl: 'https://explorer.codex.xyz/', + }, + // Astar astar: { chainId: 0, From 47fb806f0577142c5d6de667f8c292da54a92597 Mon Sep 17 00:00:00 2001 From: Jadi Date: Fri, 12 Dec 2025 14:48:54 -0800 Subject: [PATCH 3/4] changeset --- .changeset/purple-items-argue.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/purple-items-argue.md diff --git a/.changeset/purple-items-argue.md b/.changeset/purple-items-argue.md new file mode 100644 index 000000000..09799ba31 --- /dev/null +++ b/.changeset/purple-items-argue.md @@ -0,0 +1,5 @@ +--- +"@layerzerolabs/verify-contract": minor +--- + +Verify contract checks deployment ABI first, but fall back to source-based extraction if there's a mismatch From 0f177552405a87b3ce95790b65fa59be093ee197 Mon Sep 17 00:00:00 2001 From: Jadi Date: Tue, 16 Dec 2025 11:25:54 -0800 Subject: [PATCH 4/4] Fixing a legacy typo in function names --- packages/verify-contract/src/common/abi.ts | 4 ++-- .../verify-contract/src/hardhat-deploy/verify.ts | 13 ++++++++----- packages/verify-contract/test/abi.test.ts | 12 ++++++------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/verify-contract/src/common/abi.ts b/packages/verify-contract/src/common/abi.ts index c69b1f182..3b457ca61 100644 --- a/packages/verify-contract/src/common/abi.ts +++ b/packages/verify-contract/src/common/abi.ts @@ -12,7 +12,7 @@ import { TypeName, type FunctionDefinition } from '@solidity-parser/parser/dist/ * @param args Constructor arguments * @returns */ -export const encodeContructorArguments = (abi: JsonFragment[], args: unknown[] | undefined): string | undefined => { +export const encodeConstructorArguments = (abi: JsonFragment[], args: unknown[] | undefined): string | undefined => { if (args == null || args.length === 0) { return undefined } @@ -24,7 +24,7 @@ export const encodeContructorArguments = (abi: JsonFragment[], args: unknown[] | return encodedConstructorArguments.slice(2) } -export const getContructorABIFromSource = (source: string): MinimalAbi => { +export const getConstructorABIFromSource = (source: string): MinimalAbi => { try { // First we'll parse the source code and get the AST const ast = parser.parse(source) diff --git a/packages/verify-contract/src/hardhat-deploy/verify.ts b/packages/verify-contract/src/hardhat-deploy/verify.ts index 548d13031..f0e008b14 100644 --- a/packages/verify-contract/src/hardhat-deploy/verify.ts +++ b/packages/verify-contract/src/hardhat-deploy/verify.ts @@ -7,7 +7,7 @@ import { COLORS, RecordLogger, anonymizeValue, createRecordLogger } from '../com import { tryCreateScanContractUrl } from '../common/url' import { DeploymentSchema } from '../common/schema' import { extractSolcInputFromMetadata } from './schema' -import { encodeContructorArguments, getContructorABIFromSource } from '../common/abi' +import { encodeConstructorArguments, getConstructorABIFromSource } from '../common/abi' import type { VerificationArtifact, VerificationResult, @@ -100,7 +100,10 @@ export const verifyNonTarget = async ( typeof contract.constructorArguments === 'string' ? contract.constructorArguments : // For decoded constructor arguments we'll need to try and encoded them using the contract source - encodeContructorArguments(getContructorABIFromSource(source.content), contract.constructorArguments) + encodeConstructorArguments( + getConstructorABIFromSource(source.content), + contract.constructorArguments + ) // Deployment metadata contains solcInput, just a bit rearranged const solcInput = extractSolcInputFromMetadata(deployment.metadata) @@ -257,13 +260,13 @@ export const verifyTarget = async ( // Try using deployment ABI first, but fall back to source-based extraction if there's a mismatch let constructorArguments: string | undefined try { - constructorArguments = encodeContructorArguments(deployment.abi, deployment.args) + constructorArguments = encodeConstructorArguments(deployment.abi, deployment.args) } catch (error) { // If encoding fails due to argument mismatch, try extracting constructor from source // This handles cases where the ABI is incomplete but source code has the full signature try { - const sourceBasedAbi = getContructorABIFromSource(source.content) - constructorArguments = encodeContructorArguments(sourceBasedAbi, deployment.args) + const sourceBasedAbi = getConstructorABIFromSource(source.content) + constructorArguments = encodeConstructorArguments(sourceBasedAbi, deployment.args) } catch (sourceError) { // If both fail, log a warning and skip this contract logger.warn( diff --git a/packages/verify-contract/test/abi.test.ts b/packages/verify-contract/test/abi.test.ts index fdb1a6c3b..e3ae92221 100644 --- a/packages/verify-contract/test/abi.test.ts +++ b/packages/verify-contract/test/abi.test.ts @@ -1,17 +1,17 @@ -import { encodeContructorArguments } from '@/common/abi' +import { encodeConstructorArguments } from '@/common/abi' describe('abi', () => { - describe('encodeContructorArguments', () => { + describe('encodeConstructorArguments', () => { it('should return undefined if args are nullish', () => { - expect(encodeContructorArguments([], undefined)).toBeUndefined() + expect(encodeConstructorArguments([], undefined)).toBeUndefined() }) it('should return undefined if args are empty', () => { - expect(encodeContructorArguments([], [])).toBeUndefined() + expect(encodeConstructorArguments([], [])).toBeUndefined() }) it('should throw an error if there is no constructor fragment', () => { - expect(() => encodeContructorArguments([{}], [1])).toThrow('invalid fragment object') + expect(() => encodeConstructorArguments([{}], [1])).toThrow('invalid fragment object') }) it.each([ @@ -50,7 +50,7 @@ describe('abi', () => { ], ], ])('should return %s for arguments %j and ABI %j', (encoded, args, abi) => { - expect(encodeContructorArguments(abi, args)).toBe(encoded) + expect(encodeConstructorArguments(abi, args)).toBe(encoded) }) }) })