Commit 483f561b authored by Kelvin Fichter's avatar Kelvin Fichter

feat: solidify deployment process

Various fixes to make the deployment process easier
parent bfeb7fba
---
'@eth-optimism/contracts': patch
---
Update and harden the contract deployment process
import { expect } from 'chai'
/* Imports: External */
import {
Contract,
......@@ -86,14 +84,14 @@ export const getAddressManager = (provider: any) => {
export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
const l1BridgeInterface = getContractInterface('L1StandardBridge')
const ProxyBridgeAddress = await AddressManager.getAddress(
'Proxy__L1StandardBridge'
'Proxy__OVM_L1StandardBridge'
)
if (
!utils.isAddress(ProxyBridgeAddress) ||
ProxyBridgeAddress === constants.AddressZero
) {
throw new Error('Proxy__L1StandardBridge not found')
throw new Error('Proxy__OVM_L1StandardBridge not found')
}
const L1StandardBridge = new Contract(
......
......@@ -13,7 +13,7 @@ export const initWatcher = async (
AddressManager: Contract
) => {
const l1MessengerAddress = await AddressManager.getAddress(
'Proxy__L1CrossDomainMessenger'
'Proxy__OVM_L1CrossDomainMessenger'
)
const l2MessengerAddress = await AddressManager.getAddress(
'L2CrossDomainMessenger'
......
......@@ -24,12 +24,12 @@ function envSet() {
}
# set the address to the proxy gateway if possible
envSet L1_STANDARD_BRIDGE_ADDRESS Proxy__L1StandardBridge
envSet L1_STANDARD_BRIDGE_ADDRESS Proxy__OVM_L1StandardBridge
if [ $L1_STANDARD_BRIDGE_ADDRESS == null ]; then
envSet L1_STANDARD_BRIDGE_ADDRESS L1StandardBridge
fi
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS Proxy__L1CrossDomainMessenger
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS Proxy__OVM_L1CrossDomainMessenger
if [ $L1_CROSS_DOMAIN_MESSENGER_ADDRESS == null ]; then
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS L1CrossDomainMessenger
fi
......
// WARNING: DO NOT USE THIS FILE TO DEPLOY CONTRACTS TO PRODUCTION
// WE ARE REMOVING THIS FILE IN A FUTURE RELEASE, IT IS ONLY TO BE USED AS PART OF THE LOCAL
// DEPLOYMENT PROCESS. USE A DEPLOYMENT SCRIPT LOCATED IN scripts/deploy-scripts/ WHEN DEPLOYING
// TO A PRODUCTION ENVIRONMENT.
import { Wallet } from 'ethers'
import path from 'path'
import dirtree from 'directory-tree'
......@@ -43,6 +48,11 @@ const parseEnv = () => {
}
const main = async () => {
// Just be really verbose about this...
console.log(
`WARNING: DO NOT USE THIS FILE IN PRODUCTION! FOR LOCAL DEVELOPMENT ONLY!`
)
const config = parseEnv()
await hre.run('deploy', {
......@@ -55,6 +65,7 @@ const main = async () => {
ovmSequencerAddress: sequencer.address,
ovmProposerAddress: sequencer.address,
ovmAddressManagerOwner: deployer.address,
numDeployConfirmations: 0,
noCompile: process.env.NO_COMPILE ? true : false,
})
......
......@@ -9,6 +9,7 @@ const deployFn: DeployFunction = async (hre) => {
from: deployer,
args: [],
log: true,
waitConfirmations: (hre as any).deployConfig.numDeployConfirmations,
})
}
......
......@@ -17,12 +17,12 @@ const deployFn: DeployFunction = async (hre) => {
await deployAndRegister({
hre,
name: 'Proxy__L1CrossDomainMessenger',
name: 'Proxy__OVM_L1CrossDomainMessenger',
contract: 'Lib_ResolvedDelegateProxy',
iface: 'L1CrossDomainMessenger',
args: [Lib_AddressManager.address, 'OVM_L1CrossDomainMessenger'],
postDeployAction: async (contract) => {
console.log(`Initializing Proxy__L1CrossDomainMessenger...`)
console.log(`Initializing Proxy__OVM_L1CrossDomainMessenger...`)
await contract.initialize(Lib_AddressManager.address)
console.log(`Checking that contract was correctly initialized...`)
......@@ -36,6 +36,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['Proxy__L1CrossDomainMessenger']
deployFn.tags = ['Proxy__OVM_L1CrossDomainMessenger']
export default deployFn
......@@ -9,13 +9,13 @@ const deployFn: DeployFunction = async (hre) => {
await deployAndRegister({
hre,
name: 'Proxy__L1StandardBridge',
name: 'Proxy__OVM_L1StandardBridge',
contract: 'L1ChugSplashProxy',
iface: 'L1StandardBridge',
args: [deployer],
})
}
deployFn.tags = ['Proxy__L1StandardBridge']
deployFn.tags = ['Proxy__OVM_L1StandardBridge']
export default deployFn
......@@ -12,6 +12,8 @@ import {
import {
getDeployedContract,
waitUntilTrue,
getAdvancedContract,
deployAndRegister,
} from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
......@@ -22,21 +24,28 @@ const deployFn: DeployFunction = async (hre) => {
)
// Set up a reference to the proxy as if it were the L1StandardBridge contract.
const contract = await getDeployedContract(hre, 'Proxy__L1StandardBridge', {
iface: 'L1StandardBridge',
signerOrProvider: deployer,
})
const contract = await getDeployedContract(
hre,
'Proxy__OVM_L1StandardBridge',
{
iface: 'L1StandardBridge',
signerOrProvider: deployer,
}
)
// Because of the `iface` parameter supplied to the deployment function above, the `contract`
// variable that we here will have the interface of the L1StandardBridge contract. However,
// we also need to interact with the contract as if it were a L1ChugSplashProxy contract so
// we instantiate a new ethers.Contract object with the same address and signer but with the
// L1ChugSplashProxy interface.
const proxy = new ethers.Contract(
contract.address,
getContractInterface('L1ChugSplashProxy'),
contract.signer
)
const proxy = getAdvancedContract({
hre,
contract: new ethers.Contract(
contract.address,
getContractInterface('L1ChugSplashProxy'),
contract.signer
),
})
// First we need to set the correct implementation code. We'll set the code and then check
// that the code was indeed correctly set.
......@@ -62,10 +71,17 @@ const deployFn: DeployFunction = async (hre) => {
// check that this operation was correctly executed by calling `messenger()` and checking
// that the result matches the value we initialized.
const l1CrossDomainMessengerAddress = await Lib_AddressManager.getAddress(
'Proxy__L1CrossDomainMessenger'
'Proxy__OVM_L1CrossDomainMessenger'
)
console.log(`Setting messenger address...`)
// Critical error, should never happen.
if (
hexStringEquals(l1CrossDomainMessengerAddress, ethers.constants.AddressZero)
) {
throw new Error(`L1CrossDomainMessenger address is set to address(0)`)
}
console.log(`Setting messenger address to ${l1CrossDomainMessengerAddress}...`)
await proxy.setStorage(
ethers.utils.hexZeroPad('0x00', 32),
ethers.utils.hexZeroPad(l1CrossDomainMessengerAddress, 32)
......@@ -80,7 +96,7 @@ const deployFn: DeployFunction = async (hre) => {
})
// Now we set the bridge address in the same manner as the messenger address.
console.log(`Setting l2 bridge address...`)
console.log(`Setting l2 bridge address to ${predeploys.L2StandardBridge}...`)
await proxy.setStorage(
ethers.utils.hexZeroPad('0x01', 32),
ethers.utils.hexZeroPad(predeploys.L2StandardBridge, 32)
......@@ -95,8 +111,8 @@ const deployFn: DeployFunction = async (hre) => {
})
// Finally we transfer ownership of the proxy to the ovmAddressManagerOwner address.
console.log(`Setting owner address...`)
const owner = (hre as any).deployConfig.ovmAddressManagerOwner
console.log(`Setting owner address to ${owner}...`)
await proxy.setOwner(owner)
console.log(`Confirming that owner address was correctly set...`)
......@@ -108,6 +124,15 @@ const deployFn: DeployFunction = async (hre) => {
owner
)
})
// Deploy a copy of the implementation so it can be successfully verified on Etherscan.
console.log(`Deploying a copy of the bridge for Etherscan verification...`)
await deployAndRegister({
hre,
name: 'L1StandardBridge_for_verification_only',
contract: 'L1StandardBridge',
args: [],
})
}
deployFn.tags = ['L1StandardBridge', 'upgrade']
......
......@@ -19,7 +19,7 @@ const deployFn: DeployFunction = async (hre) => {
if (chainId === defaultHardhatNetworkParams.chainId) {
const L1StandardBridge = await getDeployedContract(
hre,
'Proxy__L1StandardBridge',
'Proxy__OVM_L1StandardBridge',
{
iface: 'L1StandardBridge',
}
......
......@@ -86,7 +86,7 @@
"ethers": "^5.4.5",
"glob": "^7.1.6",
"hardhat": "^2.3.0",
"hardhat-deploy": "^0.7.4",
"hardhat-deploy": "^0.9.3",
"hardhat-gas-reporter": "^1.0.4",
"lint-staged": "11.0.0",
"lodash": "^4.17.21",
......
......@@ -23,14 +23,17 @@ npx hardhat deploy \
--ctc-max-transaction-gas-limit 15000000 \
--ctc-l2-gas-discount-divisor 32 \
--ctc-enqueue-gas-cost 60000 \
--scc-fraud-proof-window 604800 \
--scc-fraud-proof-window 10 \
--scc-sequencer-publish-window 12592000 \
--ovm-sequencer-address 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 \
--ovm-proposer-address 0x9A2F243c605e6908D96b18e21Fb82Bf288B19EF3 \
--ovm-address-manager-owner 0x18394B52d3Cb931dfA76F63251919D051953413d \
--gasprice 10000000000 \
--gasprice 1000000000 \
--num-deploy-confirmations 1 \
--tags upgrade \
--network kovan
CONTRACTS_TARGET_NETWORK=kovan \
npx hardhat etherscan-verify --network kovan
npx hardhat etherscan-verify \
--network kovan \
--sleep
......@@ -74,7 +74,7 @@ const networks = {
if (contracts[i] === 'L1CrossDomainMessenger') {
proxiedContracts.push(contracts.splice(i, 1)[0])
}
if (contracts[i] === 'Proxy__L1StandardBridge') {
if (contracts[i] === 'L1StandardBridge') {
proxiedContracts.push(contracts.splice(i, 1)[0])
}
}
......
......@@ -81,6 +81,7 @@ export const deployAndRegister = async ({
from: deployer,
args,
log: true,
waitConfirmations: hre.deployConfig.numDeployConfirmations,
})
await hre.ethers.provider.waitForTransaction(result.transactionHash)
......@@ -93,8 +94,12 @@ export const deployAndRegister = async ({
const factory = await hre.ethers.getContractFactory(iface)
abi = factory.interface
}
const instance = new Contract(result.address, abi, signer)
await postDeployAction(instance)
await postDeployAction(
getAdvancedContract({
hre,
contract: new Contract(result.address, abi, signer),
})
)
}
await registerAddress({
......@@ -105,6 +110,71 @@ export const deployAndRegister = async ({
}
}
// Returns a version of the contract object which modifies all of the input contract's methods to:
// 1. Waits for a confirmed receipt with more than deployConfig.numDeployConfirmations confirmations.
// 2. Include simple resubmission logic, ONLY for Kovan, which appears to drop transactions.
export const getAdvancedContract = (opts: {
hre: any
contract: Contract
}): Contract => {
// Temporarily override Object.defineProperty to bypass ether's object protection.
const def = Object.defineProperty
Object.defineProperty = (obj, propName, prop) => {
prop.writable = true
return def(obj, propName, prop)
}
const contract = new Contract(
opts.contract.address,
opts.contract.interface,
opts.contract.signer || opts.contract.provider
)
// Now reset Object.defineProperty
Object.defineProperty = def
// Override each function call to also `.wait()` so as to simplify the deploy scripts' syntax.
for (const fnName of Object.keys(contract.functions)) {
const fn = contract[fnName].bind(contract)
;(contract as any)[fnName] = async (...args: any) => {
const tx = await fn(...args, {
gasPrice: opts.hre.deployConfig.gasprice || undefined,
})
if (typeof tx !== 'object' || typeof tx.wait !== 'function') {
return tx
}
// Special logic for:
// (1) handling confirmations
// (2) handling an issue on Kovan specifically where transactions get dropped for no
// apparent reason.
const maxTimeout = 120
let timeout = 0
while (true) {
await sleep(1000)
const receipt = await contract.provider.getTransactionReceipt(tx.hash)
if (receipt === null) {
timeout++
if (timeout > maxTimeout && opts.hre.network.name === 'kovan') {
// Special resubmission logic ONLY required on Kovan.
console.log(
`WARNING: Exceeded max timeout on transaction. Attempting to submit transaction again...`
)
return contract[fnName](...args)
}
} else if (
receipt.confirmations >= opts.hre.deployConfig.numDeployConfirmations
) {
return tx
}
}
}
}
return contract
}
export const getDeployedContract = async (
hre: any,
name: string,
......@@ -133,29 +203,8 @@ export const getDeployedContract = async (
}
}
// Temporarily override Object.defineProperty to bypass ether's object protection.
const def = Object.defineProperty
Object.defineProperty = (obj, propName, prop) => {
prop.writable = true
return def(obj, propName, prop)
}
const contract = new Contract(deployed.address, iface, signerOrProvider)
// Now reset Object.defineProperty
Object.defineProperty = def
// Override each function call to also `.wait()` so as to simplify the deploy scripts' syntax.
for (const fnName of Object.keys(contract.functions)) {
const fn = contract[fnName].bind(contract)
;(contract as any)[fnName] = async (...args: any) => {
const result = await fn(...args)
if (typeof result === 'object' && typeof result.wait === 'function') {
await result.wait()
}
return result
}
}
return contract
return getAdvancedContract({
hre,
contract: new Contract(deployed.address, iface, signerOrProvider),
})
}
......@@ -9,6 +9,7 @@ const DEFAULT_CTC_L2_GAS_DISCOUNT_DIVISOR = 32
const DEFAULT_CTC_ENQUEUE_GAS_COST = 60_000
const DEFAULT_SCC_FRAUD_PROOF_WINDOW = 60 * 60 * 24 * 7 // 7 days
const DEFAULT_SCC_SEQUENCER_PUBLISH_WINDOW = 60 * 30 // 30 minutes
const DEFAULT_DEPLOY_CONFIRMATIONS = 12
task('deploy')
.addOptionalParam(
......@@ -65,6 +66,12 @@ task('deploy')
undefined,
types.string
)
.addOptionalParam(
'numDeployConfirmations',
'Number of confirmations to wait for each transaction in the deployment. More is safer.',
DEFAULT_DEPLOY_CONFIRMATIONS,
types.int
)
.setAction(async (args, hre: any, runSuper) => {
// Necessary because hardhat doesn't let us attach non-optional parameters to existing tasks.
const validateAddressArg = (argName: string) => {
......
......@@ -111,7 +111,7 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
this.logger.info('Connecting to L1CrossDomainMessenger...')
this.state.L1CrossDomainMessenger = await loadContractFromManager({
name: 'L1CrossDomainMessenger',
proxy: 'Proxy__L1CrossDomainMessenger',
proxy: 'Proxy__OVM_L1CrossDomainMessenger',
Lib_AddressManager: this.state.Lib_AddressManager,
provider: this.options.l1RpcProvider,
})
......
......@@ -783,6 +783,15 @@
"@ethersproject/logger" "^5.4.0"
bn.js "^4.11.9"
"@ethersproject/bignumber@^5.4.1":
version "5.4.2"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.4.2.tgz#44232e015ae4ce82ac034de549eb3583c71283d8"
integrity sha512-oIBDhsKy5bs7j36JlaTzFgNPaZjiNDOXsdSgSpXRucUl+UA6L/1YLlFeI3cPAoodcenzF4nxNPV13pcy7XbWjA==
dependencies:
"@ethersproject/bytes" "^5.4.0"
"@ethersproject/logger" "^5.4.0"
bn.js "^4.11.9"
"@ethersproject/bytes@5.4.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e"
......@@ -936,7 +945,7 @@
bech32 "1.1.4"
ws "7.4.6"
"@ethersproject/providers@^5.4.5":
"@ethersproject/providers@^5.4.4", "@ethersproject/providers@^5.4.5":
version "5.4.5"
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.5.tgz#eb2ea2a743a8115f79604a8157233a3a2c832928"
integrity sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw==
......@@ -998,7 +1007,7 @@
elliptic "6.5.4"
hash.js "1.1.7"
"@ethersproject/solidity@5.4.0", "@ethersproject/solidity@^5.0.0", "@ethersproject/solidity@^5.0.9":
"@ethersproject/solidity@5.4.0", "@ethersproject/solidity@^5.0.0", "@ethersproject/solidity@^5.0.9", "@ethersproject/solidity@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.4.0.tgz#1305e058ea02dc4891df18b33232b11a14ece9ec"
integrity sha512-XFQTZ7wFSHOhHcV1DpcWj7VXECEiSrBuv7JErJvB9Uo+KfCdc3QtUZV+Vjh/AAaYgezUEKbCtE6Khjm44seevQ==
......@@ -1042,7 +1051,7 @@
"@ethersproject/constants" "^5.4.0"
"@ethersproject/logger" "^5.4.0"
"@ethersproject/wallet@5.4.0", "@ethersproject/wallet@^5.0.0":
"@ethersproject/wallet@5.4.0", "@ethersproject/wallet@^5.0.0", "@ethersproject/wallet@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.4.0.tgz#fa5b59830b42e9be56eadd45a16a2e0933ad9353"
integrity sha512-wU29majLjM6AjCjpat21mPPviG+EpK7wY1+jzKD0fg3ui5fgedf2zEu1RDgpfIMsfn8fJHJuzM4zXZ2+hSHaSQ==
......@@ -2741,7 +2750,7 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3"
integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==
"@types/qs@*", "@types/qs@^6.2.31", "@types/qs@^6.9.4":
"@types/qs@*", "@types/qs@^6.2.31", "@types/qs@^6.9.7":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
......@@ -4694,7 +4703,7 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
......@@ -4759,7 +4768,7 @@ chokidar@3.5.1:
optionalDependencies:
fsevents "~2.3.1"
chokidar@3.5.2, chokidar@^3.4.0, chokidar@^3.4.3:
chokidar@3.5.2, chokidar@^3.4.0, chokidar@^3.4.3, chokidar@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
......@@ -7495,6 +7504,15 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
......@@ -7557,6 +7575,15 @@ fs-extra@^0.30.0:
path-is-absolute "^1.0.0"
rimraf "^2.2.8"
fs-extra@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@^4.0.2, fs-extra@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
......@@ -7584,7 +7611,7 @@ fs-extra@^8.1.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^9.0.0, fs-extra@^9.1.0:
fs-extra@^9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
......@@ -8058,28 +8085,29 @@ hard-rejection@^2.1.0:
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
hardhat-deploy@^0.7.4:
version "0.7.11"
resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.7.11.tgz#93f79dfbb529eeda24ac963e23a19064d536be2f"
integrity sha512-ONLH3NH8Biuhky44KRFyaINVHM8JI4Ihy1TpntIRZUpIFHlz9h3gieq46H7iwdp6z3CqMsOCChF0riUF3CFpmQ==
hardhat-deploy@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.9.3.tgz#cf9a7806c0a86a6f7e9352fc558b26c079983496"
integrity sha512-0sxxQoxcA1+LSVmmLwp9empK4Pz5tjr92JvfcobojBz35DSpp0o3f0f/tXocYWaGbOrms3eQJ8OX5WVsmcnN4Q==
dependencies:
"@ethersproject/abi" "^5.0.0"
"@ethersproject/abstract-signer" "^5.0.0"
"@ethersproject/address" "^5.0.0"
"@ethersproject/bignumber" "^5.0.0"
"@ethersproject/bytes" "^5.0.0"
"@ethersproject/contracts" "^5.0.0"
"@ethersproject/providers" "^5.0.0"
"@ethersproject/solidity" "^5.0.0"
"@ethersproject/transactions" "^5.0.0"
"@ethersproject/wallet" "^5.0.0"
"@types/qs" "^6.9.4"
"@ethersproject/abi" "^5.4.0"
"@ethersproject/abstract-signer" "^5.4.1"
"@ethersproject/address" "^5.4.0"
"@ethersproject/bignumber" "^5.4.1"
"@ethersproject/bytes" "^5.4.0"
"@ethersproject/contracts" "^5.4.1"
"@ethersproject/providers" "^5.4.4"
"@ethersproject/solidity" "^5.4.0"
"@ethersproject/transactions" "^5.4.0"
"@ethersproject/wallet" "^5.4.0"
"@types/qs" "^6.9.7"
axios "^0.21.1"
chalk "^4.1.0"
chokidar "^3.4.0"
debug "^4.1.1"
form-data "^3.0.0"
fs-extra "^9.0.0"
chalk "^4.1.2"
chokidar "^3.5.2"
debug "^4.3.2"
enquirer "^2.3.6"
form-data "^4.0.0"
fs-extra "^10.0.0"
match-all "^1.2.6"
murmur-128 "^0.2.1"
qs "^6.9.4"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment