@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
117 lines • 6.04 kB
JavaScript
import { buildArtifact as zksyncBuildArtifact } from '@hyperlane-xyz/core/buildArtifact-zksync.js';
import { assert, retryAsync, rootLogger, strip0x, } from '@hyperlane-xyz/utils';
import { checkContractVerificationStatus, getContractSourceCode, verifyContractSourceCodeViaStandardJsonInput, verifyProxyContract, } from '../../block-explorer/etherscan.js';
import { ContractVerificationStatus } from '../../token/types.js';
import { BaseContractVerifier } from './BaseContractVerifier.js';
export class ContractVerifier extends BaseContractVerifier {
multiProvider;
apiKeys;
logger = rootLogger.child({ module: 'ContractVerifier' });
compilerOptions;
constructor(multiProvider, apiKeys, buildArtifact, licenseType) {
super(multiProvider, buildArtifact);
this.multiProvider = multiProvider;
this.apiKeys = apiKeys;
const compilerversion = `v${buildArtifact.solcLongVersion}`;
const versionRegex = /v(\d.\d.\d+)\+commit.\w+/;
const matches = versionRegex.exec(compilerversion);
if (!matches) {
throw new Error(`Invalid compiler version ${compilerversion}`);
}
this.compilerOptions = {
codeformat: 'solidity-standard-json-input',
compilerversion,
licenseType,
};
if (zksyncBuildArtifact?.zk_version)
this.compilerOptions.zksolcversion = `v${zksyncBuildArtifact.zk_version}`;
}
async verify(chain, input, verificationLogger) {
const contractType = input.isProxy ? 'proxy' : 'implementation';
try {
const verificationStatus = await this.getContractVerificationStatus(chain, input.address, verificationLogger);
if (verificationStatus === ContractVerificationStatus.Verified ||
verificationStatus === ContractVerificationStatus.Skipped) {
verificationLogger.debug(`Contract ${contractType} at address "${input.address}" on chain "${chain}" is already verified. Skipping...`);
return;
}
let verificationId;
if (input.isProxy) {
verificationId = await this.verifyProxy(chain, input);
}
else {
verificationId = await this.verifyImplementation(chain, input, verificationLogger);
}
verificationLogger.debug(`Verification request for ${contractType} contract at address "${input.address}" on chain "${chain}" sent. GUID ${verificationId}`);
await this.checkStatus(chain, verificationId, !!input.isProxy, verificationLogger);
verificationLogger.debug(`Contract ${contractType} at address "${input.address}" on chain "${chain}" successfully verified`);
}
catch (err) {
verificationLogger.error(`Failed to verify ${contractType} contract at address "${input.address}" on chain "${chain}"`, err);
}
}
async verifyImplementation(chain, input, verificationLogger) {
const { apiUrl, apiKey } = this.multiProvider.getEvmExplorerMetadata(chain);
const data = this.getImplementationData(chain, input, verificationLogger);
return verifyContractSourceCodeViaStandardJsonInput({
apiUrl,
apiKey,
}, {
compilerVersion: this.compilerOptions.compilerversion,
constructorArguments: input.constructorArguments,
contractAddress: input.address,
contractName: data.contractname,
sourceCode: data.sourceCode,
licenseType: this.compilerOptions.licenseType,
zkCompilerVersion: this.compilerOptions.zksolcversion,
});
}
async verifyProxy(chain, input) {
assert(input.expectedimplementation, `Implementation address not provided for proxied contract at address "${input.address}" on chain "${chain}". Skipping verification`);
const { apiUrl, apiKey } = this.multiProvider.getEvmExplorerMetadata(chain);
return verifyProxyContract({
apiUrl,
apiKey,
}, {
implementationAddress: input.expectedimplementation,
contractAddress: input.address,
});
}
async checkStatus(chain, verificationId, isProxy, verificationLogger) {
const contractType = isProxy ? 'proxy' : 'implementation';
verificationLogger.trace({ verificationId }, `Checking ${contractType} verification status on chain "${chain}"...`);
const { apiUrl, apiKey } = this.multiProvider.getEvmExplorerMetadata(chain);
await retryAsync(() => checkContractVerificationStatus({
apiUrl,
apiKey,
}, { isProxy, verificationId }), undefined, 1000);
}
prepareImplementationData(sourceName, input, filteredStandardInputJson) {
return {
sourceCode: filteredStandardInputJson,
contractname: `${sourceName}:${input.name}`,
contractaddress: input.address,
constructorArguements: strip0x(input.constructorArguments ?? ''),
...this.compilerOptions,
};
}
async getContractVerificationStatus(chain, address, verificationLogger = this.logger) {
try {
const { apiUrl, apiKey } = this.multiProvider.getEvmExplorerMetadata(chain);
verificationLogger.trace(`Fetching contract ABI for ${chain} address ${address}`);
const sourceCodeResults = await getContractSourceCode({
apiUrl,
apiKey,
}, { contractAddress: address });
// Explorer won't return ContractName if unverified
return sourceCodeResults.ContractName
? ContractVerificationStatus.Verified
: ContractVerificationStatus.Unverified;
}
catch (e) {
this.logger.error(`Error fetching contract verification status for ${address} on chain ${chain}: ${e}`);
return ContractVerificationStatus.Error;
}
}
}
//# sourceMappingURL=ContractVerifier.js.map