|
|
|
@ -34,9 +34,18 @@ import { MultiProvider } from '../providers/MultiProvider'; |
|
|
|
|
import { MailboxClientConfig } from '../router/types'; |
|
|
|
|
import { ChainMap, ChainName } from '../types'; |
|
|
|
|
|
|
|
|
|
import { UpgradeConfig, proxyAdmin } from './proxy'; |
|
|
|
|
import { |
|
|
|
|
UpgradeConfig, |
|
|
|
|
isProxy, |
|
|
|
|
proxyAdmin, |
|
|
|
|
proxyConstructorArgs, |
|
|
|
|
proxyImplementation, |
|
|
|
|
} from './proxy'; |
|
|
|
|
import { ContractVerificationInput } from './verify/types'; |
|
|
|
|
import { getContractVerificationInput } from './verify/utils'; |
|
|
|
|
import { |
|
|
|
|
buildVerificationInput, |
|
|
|
|
getContractVerificationInput, |
|
|
|
|
} from './verify/utils'; |
|
|
|
|
|
|
|
|
|
export interface DeployerOptions { |
|
|
|
|
logger?: Debugger; |
|
|
|
@ -60,6 +69,7 @@ export abstract class HyperlaneDeployer< |
|
|
|
|
protected readonly multiProvider: MultiProvider, |
|
|
|
|
protected readonly factories: Factories, |
|
|
|
|
protected readonly options?: DeployerOptions, |
|
|
|
|
protected readonly recoverVerificationInputs = false, |
|
|
|
|
) { |
|
|
|
|
this.logger = options?.logger ?? debug('hyperlane:deployer'); |
|
|
|
|
this.chainTimeoutMs = options?.chainTimeoutMs ?? 5 * 60 * 1000; // 5 minute timeout per chain
|
|
|
|
@ -266,21 +276,29 @@ export abstract class HyperlaneDeployer< |
|
|
|
|
): Promise<ReturnType<F['deploy']>> { |
|
|
|
|
const cachedContract = this.readCache(chain, factory, contractName); |
|
|
|
|
if (cachedContract) { |
|
|
|
|
if (this.recoverVerificationInputs) { |
|
|
|
|
const recoveredInputs = await this.recoverVerificationArtifacts( |
|
|
|
|
chain, |
|
|
|
|
contractName, |
|
|
|
|
cachedContract, |
|
|
|
|
constructorArgs, |
|
|
|
|
initializeArgs, |
|
|
|
|
); |
|
|
|
|
this.addVerificationArtifacts(chain, recoveredInputs); |
|
|
|
|
} |
|
|
|
|
return cachedContract; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const signer = this.multiProvider.getSigner(chain); |
|
|
|
|
const overrides = this.multiProvider.getTransactionOverrides(chain); |
|
|
|
|
|
|
|
|
|
this.logger(`Deploy ${contractName} on ${chain}`); |
|
|
|
|
const contract = await (factory |
|
|
|
|
.connect(signer) |
|
|
|
|
.deploy(...constructorArgs, overrides) as ReturnType<F['deploy']>); |
|
|
|
|
|
|
|
|
|
await this.multiProvider.handleTx(chain, contract.deployTransaction); |
|
|
|
|
const contract = await this.multiProvider.handleDeploy( |
|
|
|
|
chain, |
|
|
|
|
factory, |
|
|
|
|
constructorArgs, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (initializeArgs) { |
|
|
|
|
this.logger(`Initialize ${contractName} on ${chain}`); |
|
|
|
|
const overrides = this.multiProvider.getTransactionOverrides(chain); |
|
|
|
|
const initTx = await contract.initialize(...initializeArgs, overrides); |
|
|
|
|
await this.multiProvider.handleTx(chain, initTx); |
|
|
|
|
} |
|
|
|
@ -391,19 +409,21 @@ export abstract class HyperlaneDeployer< |
|
|
|
|
proxyAdmin: string, |
|
|
|
|
initializeArgs?: Parameters<C['initialize']>, |
|
|
|
|
): Promise<C> { |
|
|
|
|
const initData = initializeArgs |
|
|
|
|
? implementation.interface.encodeFunctionData( |
|
|
|
|
'initialize', |
|
|
|
|
initializeArgs, |
|
|
|
|
) |
|
|
|
|
: '0x'; |
|
|
|
|
const isProxied = await isProxy( |
|
|
|
|
this.multiProvider.getProvider(chain), |
|
|
|
|
implementation.address, |
|
|
|
|
); |
|
|
|
|
if (isProxied) { |
|
|
|
|
// if the implementation is already a proxy, do not deploy a new proxy
|
|
|
|
|
return implementation; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.logger(`Deploying transparent upgradable proxy`); |
|
|
|
|
const constructorArgs: [string, string, string] = [ |
|
|
|
|
implementation.address, |
|
|
|
|
const constructorArgs = proxyConstructorArgs( |
|
|
|
|
implementation, |
|
|
|
|
proxyAdmin, |
|
|
|
|
initData, |
|
|
|
|
]; |
|
|
|
|
initializeArgs, |
|
|
|
|
); |
|
|
|
|
const proxy = await this.deployContractFromFactory( |
|
|
|
|
chain, |
|
|
|
|
new TransparentUpgradeableProxy__factory(), |
|
|
|
@ -465,6 +485,52 @@ export abstract class HyperlaneDeployer< |
|
|
|
|
return undefined; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async recoverVerificationArtifacts<C extends ethers.Contract>( |
|
|
|
|
chain: ChainName, |
|
|
|
|
contractName: string, |
|
|
|
|
cachedContract: C, |
|
|
|
|
constructorArgs: Parameters<C['deploy']>, |
|
|
|
|
initializeArgs?: Parameters<C['initialize']>, |
|
|
|
|
): Promise<ContractVerificationInput[]> { |
|
|
|
|
const provider = this.multiProvider.getProvider(chain); |
|
|
|
|
const isProxied = await isProxy(provider, cachedContract.address); |
|
|
|
|
|
|
|
|
|
let implementation: string; |
|
|
|
|
if (isProxied) { |
|
|
|
|
implementation = await proxyImplementation( |
|
|
|
|
provider, |
|
|
|
|
cachedContract.address, |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
implementation = cachedContract.address; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const implementationInput = buildVerificationInput( |
|
|
|
|
contractName, |
|
|
|
|
implementation, |
|
|
|
|
cachedContract.interface.encodeDeploy(constructorArgs), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!isProxied) { |
|
|
|
|
return [implementationInput]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const admin = await proxyAdmin(provider, cachedContract.address); |
|
|
|
|
const proxyArgs = proxyConstructorArgs( |
|
|
|
|
cachedContract.attach(implementation), |
|
|
|
|
admin, |
|
|
|
|
initializeArgs, |
|
|
|
|
); |
|
|
|
|
const proxyInput = buildVerificationInput( |
|
|
|
|
'TransparentUpgradeableProxy', |
|
|
|
|
cachedContract.address, |
|
|
|
|
TransparentUpgradeableProxy__factory.createInterface().encodeDeploy( |
|
|
|
|
proxyArgs, |
|
|
|
|
), |
|
|
|
|
); |
|
|
|
|
return [implementationInput, proxyInput]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Deploys the Implementation and Proxy for a given contract |
|
|
|
|
* |
|
|
|
@ -476,15 +542,6 @@ export abstract class HyperlaneDeployer< |
|
|
|
|
constructorArgs: Parameters<Factories[K]['deploy']>, |
|
|
|
|
initializeArgs?: Parameters<HyperlaneContracts<Factories>[K]['initialize']>, |
|
|
|
|
): Promise<HyperlaneContracts<Factories>[K]> { |
|
|
|
|
const cachedContract = this.readCache( |
|
|
|
|
chain, |
|
|
|
|
this.factories[contractName], |
|
|
|
|
contractName.toString(), |
|
|
|
|
); |
|
|
|
|
if (cachedContract) { |
|
|
|
|
return cachedContract; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to initialize the implementation even though it may not be necessary
|
|
|
|
|
const implementation = await this.deployContract( |
|
|
|
|
chain, |
|
|
|
|