@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
131 lines • 6.11 kB
JavaScript
import { ProxyAdmin__factory, TransparentUpgradeableProxy__factory, } from '@hyperlane-xyz/core';
import { assert, eqAddress } from '@hyperlane-xyz/utils';
import { tryGetContractDeploymentTransaction } from '../../block-explorer/etherscan.js';
import { ExplorerFamily } from '../../metadata/chainMetadataTypes.js';
import { proxyAdmin, proxyImplementation } from '../proxy.js';
export function formatFunctionArguments(fragment, args) {
const params = Object.fromEntries(fragment.inputs.map((input, index) => [input.name, args[index]]));
return JSON.stringify(params, null, 2);
}
export function getConstructorArguments(contract, bytecode) {
const tx = contract.deployTransaction;
if (tx === undefined)
throw new Error('deploy transaction not found');
return tx.data.replace(bytecode, '');
}
export function buildVerificationInput(name, address, constructorArguments, isProxy = name.endsWith('Proxy'), expectedimplementation) {
return {
name: name.charAt(0).toUpperCase() + name.slice(1),
address,
constructorArguments,
isProxy,
expectedimplementation,
};
}
export function getContractVerificationInput({ name, contract, bytecode, isProxy, expectedimplementation, }) {
const args = getConstructorArguments(contract, bytecode);
return buildVerificationInput(name, contract.address, args, isProxy, expectedimplementation);
}
/**
* Check if the artifact should be added to the verification inputs.
* @param verificationInputs - The verification inputs for the chain.
* @param chain - The chain to check.
* @param artifact - The artifact to check.
* @returns
*/
export function shouldAddVerificationInput(verificationInputs, chain, artifact) {
return !verificationInputs[chain].some((existingArtifact) => existingArtifact.name === artifact.name &&
eqAddress(existingArtifact.address, artifact.address) &&
existingArtifact.constructorArguments === artifact.constructorArguments &&
existingArtifact.isProxy === artifact.isProxy);
}
/**
* Retrieves the constructor args using their respective Explorer and/or RPC (eth_getTransactionByHash)
*/
export async function getConstructorArgumentsApi({ chainName, contractAddress, bytecode, multiProvider, }) {
const { family } = multiProvider.getExplorerApi(chainName);
let constructorArgs;
switch (family) {
case ExplorerFamily.Routescan:
case ExplorerFamily.Etherscan:
constructorArgs = await getEtherscanConstructorArgs({
chainName,
contractAddress,
bytecode,
multiProvider,
});
break;
case ExplorerFamily.Blockscout:
constructorArgs = await getBlockScoutConstructorArgs({
chainName,
contractAddress,
multiProvider,
});
break;
default:
throw new Error(`Explorer Family ${family} unsupported`);
}
return constructorArgs;
}
export async function getEtherscanConstructorArgs({ bytecode, chainName, contractAddress, multiProvider, }) {
const { apiUrl: blockExplorerApiUrl, apiKey: blockExplorerApiKey } = multiProvider.getExplorerApi(chainName);
const creationTx = await tryGetContractDeploymentTransaction({ apiUrl: blockExplorerApiUrl, apiKey: blockExplorerApiKey }, { contractAddress });
// Fetch deployment bytecode (includes constructor args)
assert(creationTx, 'Contract creation transaction not found!');
const metadata = multiProvider.getChainMetadata(chainName);
const rpcUrl = metadata.rpcUrls[0].http;
const creationTxResp = await fetch(rpcUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
method: 'eth_getTransactionByHash',
params: [creationTx.txHash],
id: 1,
jsonrpc: '2.0',
}),
});
// Truncate the deployment bytecode
const creationInput = (await creationTxResp.json()).result.input;
return creationInput.substring(bytecode.length);
}
export async function getBlockScoutConstructorArgs({ chainName, contractAddress, multiProvider, }) {
const { apiUrl: blockExplorerApiUrl } = multiProvider.getExplorerApi(chainName);
const url = new URL(`/api/v2/smart-contracts/${contractAddress}`, blockExplorerApiUrl);
const smartContractResp = await fetch(url, {
headers: {
'Content-Type': 'application/json',
},
});
return (await smartContractResp.json()).constructor_args;
}
export async function getProxyAndAdminInput({ chainName, multiProvider, proxyAddress, }) {
const provider = multiProvider.getProvider(chainName);
const proxyAdminAddress = await proxyAdmin(provider, proxyAddress);
const proxyAdminConstructorArgs = await getConstructorArgumentsApi({
chainName,
multiProvider,
bytecode: ProxyAdmin__factory.bytecode,
contractAddress: proxyAdminAddress,
});
const proxyAdminInput = buildVerificationInput('ProxyAdmin', proxyAdminAddress, proxyAdminConstructorArgs);
const proxyConstructorArgs = await getConstructorArgumentsApi({
chainName,
multiProvider,
contractAddress: proxyAddress,
bytecode: TransparentUpgradeableProxy__factory.bytecode,
});
const transparentUpgradeableProxyInput = buildVerificationInput('TransparentUpgradeableProxy', proxyAddress, proxyConstructorArgs, true, await proxyImplementation(provider, proxyAddress));
return { proxyAdminInput, transparentUpgradeableProxyInput };
}
export async function getImplementationInput({ bytecode, chainName, contractName, implementationAddress, multiProvider, }) {
const implementationConstructorArgs = await getConstructorArgumentsApi({
bytecode,
chainName,
multiProvider,
contractAddress: implementationAddress,
});
return buildVerificationInput(contractName, implementationAddress, implementationConstructorArgs);
}
//# sourceMappingURL=utils.js.map