@openzeppelin/hardhat-upgrades
Version:
[](https://docs.openzeppelin.com/upgrades-plugins/hardhat-upgrades) [](https://www.npmjs.org/package/@openzeppelin/h
91 lines • 4.3 kB
JavaScript
import { Interface } from 'ethers';
import { deploy } from './utils/deploy.js';
import { getSigner } from './utils/ethers.js';
import { getRemoteDeployment } from './defender/utils.js';
/**
* Builds the client-neutral engine binding for the ethers API. Deployments still go through the
* ethers `ContractFactory` (preserving the existing behavior, including the Defender path and the
* `proxyFactory`/`deployFunction` escape hatches), while the engine drives the orchestration.
*
* @param runner the contract factory's runner, whose signer signs the plugin's transactions
*/
export function makeEthersBinding(hre, connection, runner, opts) {
const signer = getSigner(runner);
return {
hre,
provider: connection.provider,
encodeConstructorArgs(info, args) {
return new Interface(info.abi).encodeDeploy(args);
},
encodeFunctionData(abi, fn, args) {
return new Interface(abi).encodeFunctionData(fn, args);
},
hasFunction(abi, fn) {
return new Interface(abi).getFunction(fn) !== null;
},
formatManifestAbi(abi) {
return new Interface(abi).format(true);
},
async getSignerAddress() {
return signer === undefined ? undefined : await signer.getAddress();
},
deployUnconfirmed(info, args) {
// ethers' deploy already returns the pending transaction without waiting for the receipt, so
// upgrades-core confirms it the same way; there is nothing different to do for ethers.
return this.deploy(info, args);
},
async deploy(info, args) {
const factory = await connection.ethers.getContractFactory([...info.abi], info.bytecode, signer);
return toDeployedContract(await deploy(hre, opts, factory, ...args));
},
async deployProxy(info, args) {
// Build the proxy factory the way the previous implementation did, falling back to the
// connection's default signer when the implementation factory has no signer.
const factory = opts.proxyFactory ?? (await connection.ethers.getContractFactory([...info.abi], info.bytecode, signer));
const deployFn = opts.deployFunction || deploy;
return toDeployedContract(await deployFn(hre, opts, factory, ...args));
},
async sendTransaction(tx) {
// The signer that signs the plugin's transactions, falling back to the connection's default
// signer to match `connection.ethers.getContractAt(...)` when no factory signer is available.
const txSigner = signer ?? (await connection.ethers.provider.getSigner());
const overrides = opts.txOverrides ?? {};
// Apply overrides first so caller-supplied `to`/`data` cannot replace the plugin's call.
const response = await txSigner.sendTransaction({ ...overrides, to: tx.to, data: tx.data });
return { txHash: response.hash, txResponse: response };
},
getRemoteDeployment: remoteDeploymentId => getRemoteDeployment(hre, remoteDeploymentId),
};
}
/**
* Builds the engine's client-neutral {@link ContractInfo} from an ethers `ContractFactory`.
*/
export function contractInfo(factory) {
return {
abi: JSON.parse(factory.interface.formatJson()),
bytecode: factory.bytecode,
};
}
function toDeployedContract(deployment) {
return {
address: deployment.address,
txHash: deployment.txHash,
deployTransaction: deployment.deployTransaction,
remoteDeploymentId: deployment.remoteDeploymentId,
};
}
/**
* Resolves the ethers `TransactionResponse` of an implementation deployment for the `getTxResponse`
* option: the deployment's own transaction when freshly deployed, or otherwise the transaction
* looked up from the manifest's recorded hash.
*/
export async function txResponseOf(deployment, connection) {
if (deployment.deployTransaction !== undefined) {
return deployment.deployTransaction;
}
if (deployment.txHash !== undefined) {
return (await connection.ethers.provider.getTransaction(deployment.txHash)) ?? undefined;
}
return undefined;
}
//# sourceMappingURL=ethers-binding.js.map