fix(sdk): remove contract verification check; add expectedimplementation; enhance logging (#4165)

### Description

- removes contract verification check
- adds `expectedimplementation` method params for `verifyproxycontract`
- enhances logging and renames variables for clarity

### Drive-by changes

- none

### Related issues

- fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4123

### Backward compatibility

- yes

### Testing

- manual
mo/verify-proxy-contracts
Noah Bayindirli 🥂 3 months ago committed by GitHub
parent 62d71fad35
commit ab827a3fa6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/lazy-oranges-sing.md
  2. 12
      typescript/sdk/src/deploy/EvmModuleDeployer.ts
  3. 13
      typescript/sdk/src/deploy/HyperlaneDeployer.ts
  4. 72
      typescript/sdk/src/deploy/verify/ContractVerifier.ts
  5. 6
      typescript/sdk/src/deploy/verify/types.ts
  6. 31
      typescript/sdk/src/deploy/verify/utils.ts

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': patch
---
Removes innacurate contract verification check, resulting in proxy contracts not being marked as proxies during contract verification.

@ -47,12 +47,14 @@ export class EvmModuleDeployer<Factories extends HyperlaneFactories> {
contractName,
constructorArgs,
initializeArgs,
implementationAddress,
}: {
chain: ChainName;
factory: F;
contractName: string;
constructorArgs: Parameters<F['deploy']>;
initializeArgs?: Parameters<Awaited<ReturnType<F['deploy']>>['initialize']>;
implementationAddress?: Address;
}): Promise<ReturnType<F['deploy']>> {
this.logger.info(
`Deploying ${contractName} on ${chain} with constructor args (${constructorArgs.join(
@ -72,11 +74,12 @@ export class EvmModuleDeployer<Factories extends HyperlaneFactories> {
await this.multiProvider.handleTx(chain, initTx);
}
const verificationInput = getContractVerificationInput(
contractName,
const verificationInput = getContractVerificationInput({
name: contractName,
contract,
factory.bytecode,
);
bytecode: factory.bytecode,
expectedimplementation: implementationAddress,
});
this.addVerificationArtifacts({ chain, artifacts: [verificationInput] });
// try verifying contract
@ -215,6 +218,7 @@ export class EvmModuleDeployer<Factories extends HyperlaneFactories> {
factory: new TransparentUpgradeableProxy__factory(),
contractName: 'TransparentUpgradeableProxy',
constructorArgs,
implementationAddress: implementation.address,
});
return implementation.attach(proxy.address) as C;

@ -370,6 +370,7 @@ export abstract class HyperlaneDeployer<
constructorArgs: Parameters<F['deploy']>,
initializeArgs?: Parameters<Awaited<ReturnType<F['deploy']>>['initialize']>,
shouldRecover = true,
implementationAddress?: Address,
): Promise<ReturnType<F['deploy']>> {
if (shouldRecover) {
const cachedContract = this.readCache(chain, factory, contractName);
@ -422,11 +423,12 @@ export abstract class HyperlaneDeployer<
}
}
const verificationInput = getContractVerificationInput(
contractName,
const verificationInput = getContractVerificationInput({
name: contractName,
contract,
factory.bytecode,
);
bytecode: factory.bytecode,
expectedimplementation: implementationAddress,
});
this.addVerificationArtifacts(chain, [verificationInput]);
// try verifying contract
@ -593,6 +595,9 @@ export abstract class HyperlaneDeployer<
new TransparentUpgradeableProxy__factory(),
'TransparentUpgradeableProxy',
constructorArgs,
undefined,
true,
implementation.address,
);
return implementation.attach(proxy.address) as C;

@ -102,6 +102,10 @@ export class ContractVerifier {
url.searchParams.set('action', action);
}
verificationLogger.trace(
{ apiUrl, chain },
'Sending request to explorer...',
);
let response: Response;
if (isGetRequest) {
response = await fetch(url.toString(), {
@ -118,6 +122,10 @@ export class ContractVerifier {
let responseJson;
try {
const responseTextString = await response.text();
verificationLogger.trace(
{ apiUrl, chain },
'Parsing response from explorer...',
);
responseJson = JSON.parse(responseTextString);
} catch (error) {
verificationLogger.trace(
@ -183,33 +191,13 @@ export class ContractVerifier {
throw new Error(`[${chain}] ${errorMessage}`);
}
verificationLogger.trace(
{ apiUrl, chain, result: responseJson.result },
'Returning result from explorer.',
);
return responseJson.result;
}
private async isAlreadyVerified(
chain: ChainName,
input: ContractVerificationInput,
verificationLogger: Logger,
): Promise<boolean> {
try {
const result = await this.submitForm(
chain,
ExplorerApiActions.GETSOURCECODE,
verificationLogger,
{
address: input.address,
},
);
return !!result[0]?.SourceCode;
} catch (error) {
verificationLogger.debug(
{ error },
'Error checking if contract is already verified',
);
return false;
}
}
private async verifyProxy(
chain: ChainName,
input: ContractVerificationInput,
@ -218,13 +206,9 @@ export class ContractVerifier {
verificationLogger.debug(`📝 Verifying proxy at ${input.address}...`);
try {
const { proxyGuid } = await this.markProxy(
chain,
input,
verificationLogger,
);
if (!proxyGuid) return;
const proxyGuid = await this.markProxy(chain, input, verificationLogger);
verificationLogger.trace({ proxyGuid }, 'Checking proxy status...');
await this.submitForm(
chain,
ExplorerApiActions.CHECK_PROXY_STATUS,
@ -256,14 +240,22 @@ export class ContractVerifier {
chain: ChainName,
input: ContractVerificationInput,
verificationLogger: Logger,
): Promise<{ proxyGuid: any }> {
): Promise<string> {
try {
return this.submitForm(
const proxyGuid = await this.submitForm(
chain,
ExplorerApiActions.MARK_PROXY,
ExplorerApiActions.VERIFY_PROXY,
verificationLogger,
{ address: input.address },
{
address: input.address,
expectedimplementation: input.expectedimplementation,
},
);
verificationLogger.trace(
{ proxyGuid },
'Retrieved guid from verified proxy.',
);
return proxyGuid;
} catch (error) {
verificationLogger.debug(
`Marking of proxy at ${input.address} failed: ${error}`,
@ -358,18 +350,6 @@ export class ContractVerifier {
return;
}
if (await this.isAlreadyVerified(chain, input, verificationLogger)) {
const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl(
chain,
input.address,
);
verificationLogger.debug(
`Contract already verified at ${addressUrl}#code`,
);
await sleep(200); // 5 calls/s (https://info.etherscan.com/api-return-errors/)
return;
}
if (input.isProxy) await this.verifyProxy(chain, input, verificationLogger);
else await this.verifyImplementation(chain, input, verificationLogger);
}

@ -3,6 +3,7 @@ export type ContractVerificationInput = {
address: string;
constructorArguments?: string; // abi-encoded bytes
isProxy?: boolean;
expectedimplementation?: string;
};
export type VerificationInput = ContractVerificationInput[];
@ -47,7 +48,7 @@ export type CompilerOptions = {
export enum ExplorerApiActions {
GETSOURCECODE = 'getsourcecode',
VERIFY_IMPLEMENTATION = 'verifysourcecode',
MARK_PROXY = 'verifyproxycontract',
VERIFY_PROXY = 'verifyproxycontract',
CHECK_STATUS = 'checkverifystatus',
CHECK_PROXY_STATUS = 'checkproxyverification',
}
@ -81,9 +82,10 @@ export type FormOptions<Action extends ExplorerApiActions> =
contractname: string;
constructorArguements?: string; // TYPO IS ENFORCED BY API
}
: Action extends ExplorerApiActions.MARK_PROXY
: Action extends ExplorerApiActions.VERIFY_PROXY
? {
address: string;
expectedimplementation: string;
}
: Action extends ExplorerApiActions.CHECK_STATUS
? {

@ -1,6 +1,6 @@
import { ethers, utils } from 'ethers';
import { eqAddress } from '@hyperlane-xyz/utils';
import { Address, eqAddress } from '@hyperlane-xyz/utils';
import { ChainMap, ChainName } from '../../types.js';
@ -30,23 +30,38 @@ export function buildVerificationInput(
address: string,
constructorArguments: string,
isProxy: boolean = name.endsWith('Proxy'),
expectedimplementation?: string,
): ContractVerificationInput {
return {
name: name.charAt(0).toUpperCase() + name.slice(1),
address,
constructorArguments,
isProxy,
expectedimplementation,
};
}
export function getContractVerificationInput(
name: string,
contract: ethers.Contract,
bytecode: string,
isProxy?: boolean,
): ContractVerificationInput {
export function getContractVerificationInput({
name,
contract,
bytecode,
isProxy,
expectedimplementation,
}: {
name: string;
contract: ethers.Contract;
bytecode: string;
isProxy?: boolean;
expectedimplementation?: Address;
}): ContractVerificationInput {
const args = getConstructorArguments(contract, bytecode);
return buildVerificationInput(name, contract.address, args, isProxy);
return buildVerificationInput(
name,
contract.address,
args,
isProxy,
expectedimplementation,
);
}
/**

Loading…
Cancel
Save