UNPKG

@openzeppelin/hardhat-upgrades

Version:

[![Docs](https://img.shields.io/badge/docs-%F0%9F%93%84-blue)](https://docs.openzeppelin.com/upgrades-plugins/hardhat-upgrades) [![NPM Package](https://img.shields.io/npm/v/@openzeppelin/hardhat-upgrades.svg)](https://www.npmjs.org/package/@openzeppelin/h

130 lines • 6.4 kB
import { getAddress, getContract } from 'viem'; import { resolveLinkedBytecode } from '@nomicfoundation/hardhat-utils/bytecode'; import { UpgradesError } from '@openzeppelin/upgrades-core'; import { makeViemBinding } from './viem-binding.js'; /** * Asserts that the plugins required by the viem-based API are in use for the given connection: * @nomicfoundation/hardhat-viem, which creates the contract instances that the API returns and * signs and broadcasts the plugin's transactions. */ export function assertRequiredPlugins(connection) { if (connection === undefined || connection === null) { throw new UpgradesError('A network connection is required.', () => 'Create a connection with `await hre.network.create()` and pass it to this function.'); } if (!('viem' in connection)) { throw new UpgradesError('The viem-based API requires the @nomicfoundation/hardhat-viem plugin.', () => 'Install the @nomicfoundation/hardhat-viem and viem packages, and register @nomicfoundation/hardhat-viem in the `plugins` array of your Hardhat config.'); } } export function getContractAddress(addressOrInstance) { if (typeof addressOrInstance === 'string') { return asAddress(addressOrInstance); } else { return asAddress(addressOrInstance.address); } } export function isAddress(value) { return /^0x[0-9a-fA-F]{40}$/.test(value); } /** * Returns the given address as a checksummed viem address, so that the addresses returned by * the viem-based API are consistently checksummed regardless of the caller's input case. * Throws if the value is not an address or has an invalid checksum. */ export function asAddress(value) { const checksummed = getAddress(value); if (!isAddress(checksummed)) { throw new Error(`Broken invariant: ${value} is not an address`); } return checksummed; } /** * Resolves the wallet client whose account signs the transactions sent by the plugin: the given * wallet client, or by default the first wallet client of the connection. Unlike the previous * implementation, accounts that sign client-side (viem local accounts) are supported, since the * plugin now signs through viem itself. */ export async function resolveWalletClient(connection, walletClient) { const resolved = walletClient ?? (await connection.viem.getWalletClients())[0]; if (resolved === undefined) { throw new UpgradesError('No wallet client is available.', () => 'Provide a wallet client with the `client` option, or configure accounts for the network connection.'); } if (resolved.account === undefined || resolved.account === null) { throw new UpgradesError('The wallet client must have an account.', () => 'Use a wallet client from `connection.viem.getWalletClients()` or `connection.viem.getWalletClient(address)`.'); } return resolved; } /** * Builds the engine binding for the viem-based API from the options' wallet client and transaction * parameters. */ export async function makeBinding(hre, connection, opts = {}) { const walletClient = await resolveWalletClient(connection, opts.client?.wallet); return makeViemBinding(hre, connection, walletClient, execOptions(opts)); } /** * Builds the engine binding for read-only operations (validations, force-import) that never sign a * transaction, so they work even when the connection has no accounts. Uses the options' wallet * client if one is given, otherwise the connection's first wallet client if available. */ export async function makeReadBinding(hre, connection, opts = {}) { const candidate = opts.client?.wallet ?? (await connection.viem.getWalletClients())[0]; const walletClient = candidate?.account ? candidate : undefined; return makeViemBinding(hre, connection, walletClient, {}); } /** * Extracts the transaction parameters from the viem-based options. */ export function execOptions(opts) { return { value: opts.value, gas: opts.gas, gasPrice: opts.gasPrice, maxFeePerGas: opts.maxFeePerGas, maxPriorityFeePerGas: opts.maxPriorityFeePerGas, timeout: opts.timeout, pollingInterval: opts.pollingInterval, }; } /** * Reads the contract's ABI and library-linked creation bytecode from the project artifacts, * for use as the engine's client-neutral contract identity. */ export async function getContractInfo(hre, contractName, libraries = {}) { const artifact = await hre.artifacts.readArtifact(contractName); const bytecode = resolveLinkedBytecode(artifact, libraries); return { abi: artifact.abi, bytecode }; } /** * Reads only a contract's ABI from the project artifacts, for functions that use the ABI to encode * an initializer call but never deploy the contract's bytecode. */ export async function getAbi(hre, contractName) { const artifact = await hre.artifacts.readArtifact(contractName); return artifact.abi; } /** * Gets a viem contract instance for the named contract at the given address, using * `connection.viem.getContractAt` so that the result follows hardhat-viem conventions. */ export async function getViemContractAt(connection, contractName, address, client) { return connection.viem.getContractAt(contractName, address, client !== undefined ? { client } : undefined); } /** * Builds a viem contract instance at `address` for the given ABI, usable for reads even when the * connection has no accounts. A wallet client (the provided one, or the connection's first account) * enables writes; without one the instance is read-only — so attaching to an existing contract does * not require an account, mirroring the ethers-based API. Used by `forceImport`, which records the * deployment without needing an account and so must be able to return an instance without one. */ export async function attachViemContract(connection, abi, address, client) { const [publicClient, walletClient] = await Promise.all([ client?.public ?? connection.viem.getPublicClient(), client?.wallet ?? connection.viem.getWalletClients().then(clients => clients[0]), ]); const contract = walletClient !== undefined ? getContract({ address, abi, client: { public: publicClient, wallet: walletClient } }) : getContract({ address, abi, client: { public: publicClient } }); return contract; } //# sourceMappingURL=utils.js.map