UNPKG

@solarity/hardhat-migrate

Version:
163 lines (122 loc) 5.67 kB
import { ethers, Interface, Overrides } from "ethers"; import { isFullyQualifiedName } from "hardhat/utils/contract-names"; import { Linker } from "./Linker.js"; import { CatchClassError, fillParameters, getChainId, getInterfaceOnlyWithConstructor } from "../utils/index.js"; import { MigrateError } from "../utils/index.js"; import type { MigrationMetadata } from "../../types/tools.js"; import type { ContractDeployTxWithName, OverridesAndLibs } from "../../types/deployer.js"; import { Stats } from "../tools/Stats.js"; import { Reporter } from "../tools/reporters/Reporter.js"; import { networkManager } from "../tools/network/NetworkManager.js"; import { TransactionRunner } from "../tools/runners/TransactionRunner.js"; import { ArtifactProcessor } from "../tools/storage/ArtifactProcessor.js"; import { TransactionProcessor } from "../tools/storage/TransactionProcessor.js"; import { VerificationProcessor } from "../tools/storage/VerificationProcessor.js"; import { migratorConfig } from "../tools/network/EthersProvider.js"; @CatchClassError export class MinimalContract { private readonly _rawBytecode: string; private readonly _interfaceOnlyWithConstructor; constructor( private _bytecode: string, private readonly _interface: Interface, private readonly _contractName: string = "", ) { this._interfaceOnlyWithConstructor = getInterfaceOnlyWithConstructor(this._interface.fragments); this._rawBytecode = this._bytecode; if (_contractName === "") { try { this._contractName = ArtifactProcessor.tryGetContractName(_bytecode); } catch { throw new MigrateError("Contract name is not provided and cannot be extracted from bytecode."); } } } public async deploy(args: any[] = [], parameters: OverridesAndLibs = {}): Promise<string> { await fillParameters(parameters); await this._tryLinkLibraries(parameters); const tx = await this._createDeployTransaction(args, parameters); if (migratorConfig!.execution.continue) { return this._recoverContractAddress(tx, args); } else { return this._processContractDeploymentTransaction(tx, args); } } private async _tryLinkLibraries(parameters: OverridesAndLibs): Promise<void> { try { if (!Linker.isBytecodeNeedsLinking(this._bytecode)) { return; } this._bytecode = (await Linker.tryLinkBytecode(this._contractName, this._bytecode, parameters.libraries || {}))!; } catch (e: any) { throw new MigrateError( `Unable to link libraries for ${this._contractName}! Try manually deploy the libraries and link them.\n Error: ${e.message}`, ); } } private async _createDeployTransaction(args: any[], txOverrides: Overrides): Promise<ContractDeployTxWithName> { const factory = new ethers.ContractFactory(this._interfaceOnlyWithConstructor, this._bytecode); return { contractName: this._contractName, chainId: await getChainId(), from: await (await networkManager!.getSigner(txOverrides.from)).getAddress(), ...(await factory.getDeployTransaction(...args, txOverrides)), }; } private async _recoverContractAddress(tx: ContractDeployTxWithName, args: any[]): Promise<string> { try { const contractAddress = await TransactionProcessor?.tryRestoreContractAddressByKeyFields(tx); Reporter!.notifyContractRecovery(tx.contractName, contractAddress!); await this._saveContractForVerification(contractAddress!, tx, args); return contractAddress!; } catch { /* empty */ } Reporter!.notifyDeploymentInsteadOfRecovery(tx.contractName); return this._processContractDeploymentTransaction(tx, args); } private async _processContractDeploymentTransaction(tx: ContractDeployTxWithName, args: any[]): Promise<string> { const signer = await networkManager!.getSigner(tx.from); const txResponse = await signer.sendTransaction(tx); await TransactionRunner!.reportTransactionResponse(txResponse, tx.contractName); const contractAddress = (await txResponse.wait(0))!.contractAddress; if (typeof contractAddress !== "string") { throw new MigrateError("Contract deployment failed. Invalid contract address conversion."); } await this._saveContractForVerification(contractAddress, tx, args); const saveMetadata: MigrationMetadata = { migrationNumber: Stats.currentMigration, contractName: tx.contractName, fullyQualifiedContractName: this._getFullyQualifiedName(tx) || undefined, }; TransactionProcessor?.saveDeploymentTransaction(tx, tx.contractName, contractAddress, saveMetadata); return contractAddress; } private async _saveContractForVerification(contractAddress: string, tx: ContractDeployTxWithName, args: any[]) { if (VerificationProcessor.isVerificationDataSaved(contractAddress)) { return; } const contractName = this._getFullyQualifiedName(tx); if (contractName === null) { Reporter!.reportVerificationFailedToSave(tx.contractName); return; } VerificationProcessor.saveVerificationFunction({ contractAddress, contractName: contractName, constructorArguments: args, chainId: Number(await getChainId()), }); await ArtifactProcessor.saveArtifactIfNotExist(contractName, this._rawBytecode); } private _getFullyQualifiedName(tx: ContractDeployTxWithName): string | null { try { if (!isFullyQualifiedName(tx.contractName)) { return ArtifactProcessor.tryGetContractName(this._rawBytecode); } return tx.contractName; } catch { return null; } } }