Add InvariantChecker (#36)
* Refactor checks * comments * Fix build * Get running * cleanup * comments * suggestion * Update typescript/optics-deploy/src/core/checks.ts Co-authored-by: Nam Chu Hoai <nambrot@googlemail.com> * Comments * Assert at the right spot * comments * Fix build Co-authored-by: Nam Chu Hoai <nambrot@googlemail.com>pull/45/head
parent
9961cf0d61
commit
a48e4eab15
@ -1,70 +1,79 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { BridgeDeploy as Deploy } from './BridgeDeploy'; |
||||
import { BridgeDeploy } from './BridgeDeploy'; |
||||
import TestBridgeDeploy from './TestBridgeDeploy'; |
||||
import { assertBeaconProxy, checkVerificationInput } from '../checks'; |
||||
import { VerificationInput, InvariantChecker } from '../checks'; |
||||
import { BeaconProxy } from '../proxyUtils'; |
||||
|
||||
const emptyAddr = '0x' + '00'.repeat(32); |
||||
|
||||
export async function checkBridgeDeploy( |
||||
deploy: Deploy | TestBridgeDeploy, |
||||
remotes: number[], |
||||
) { |
||||
assertBeaconProxy(deploy.contracts.bridgeToken!); |
||||
assertBeaconProxy(deploy.contracts.bridgeRouter!); |
||||
type AnyBridgeDeploy = BridgeDeploy | TestBridgeDeploy; |
||||
|
||||
if (deploy.config.weth) { |
||||
expect(deploy.contracts.ethHelper).to.not.be.undefined; |
||||
} else { |
||||
expect(deploy.contracts.ethHelper).to.be.undefined; |
||||
|
||||
export class BridgeInvariantChecker extends InvariantChecker<AnyBridgeDeploy> { |
||||
constructor(deploys: AnyBridgeDeploy[]) { |
||||
super(deploys) |
||||
} |
||||
|
||||
const bridgeRouter = deploy.contracts.bridgeRouter?.proxy!; |
||||
await Promise.all( |
||||
remotes.map(async (remoteDomain) => { |
||||
const registeredRouter = await bridgeRouter.remotes(remoteDomain); |
||||
expect(registeredRouter).to.not.equal(emptyAddr); |
||||
}), |
||||
); |
||||
async checkDeploy(deploy: AnyBridgeDeploy): Promise<void> { |
||||
await this.checkBeaconProxies(deploy) |
||||
await this.checkBridgeRouter(deploy) |
||||
this.checkEthHelper(deploy) |
||||
this.checkVerificationInputs(deploy) |
||||
} |
||||
|
||||
expect(await bridgeRouter.owner()).to.equal( |
||||
deploy.coreContractAddresses.governance.proxy, |
||||
); |
||||
async checkBeaconProxies(deploy: AnyBridgeDeploy): Promise<void> { |
||||
await this.checkBeaconProxyImplementation( |
||||
deploy.chain.domain, |
||||
'BridgeToken', |
||||
deploy.contracts.bridgeToken!, |
||||
); |
||||
await this.checkBeaconProxyImplementation( |
||||
deploy.chain.domain, |
||||
'BridgeRouter', |
||||
deploy.contracts.bridgeRouter!, |
||||
); |
||||
} |
||||
|
||||
// check verification addresses
|
||||
checkVerificationInput( |
||||
deploy, |
||||
'BridgeToken Implementation', |
||||
deploy.contracts.bridgeToken?.implementation.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'BridgeToken UpgradeBeacon', |
||||
deploy.contracts.bridgeToken?.beacon.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'BridgeToken Proxy', |
||||
deploy.contracts.bridgeToken?.proxy.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'BridgeRouter Implementation', |
||||
deploy.contracts.bridgeRouter?.implementation.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'BridgeRouter UpgradeBeacon', |
||||
deploy.contracts.bridgeRouter?.beacon.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'BridgeRouter Proxy', |
||||
deploy.contracts.bridgeRouter?.proxy.address!, |
||||
); |
||||
if (deploy.config.weth) { |
||||
expect(deploy.verificationInput[6].address).to.equal( |
||||
deploy.contracts.ethHelper?.address, |
||||
async checkBridgeRouter(deploy: AnyBridgeDeploy): Promise<void> { |
||||
const bridgeRouter = deploy.contracts.bridgeRouter?.proxy!; |
||||
const domains = this._deploys.map((d: AnyBridgeDeploy) => d.chain.domain) |
||||
const remoteDomains = domains.filter((d: number) => d !== deploy.chain.domain) |
||||
await Promise.all( |
||||
remoteDomains.map(async (remoteDomain) => { |
||||
const registeredRouter = await bridgeRouter.remotes(remoteDomain); |
||||
expect(registeredRouter).to.not.equal(emptyAddr); |
||||
}), |
||||
); |
||||
|
||||
expect(await bridgeRouter.owner()).to.equal( |
||||
deploy.coreContractAddresses.governance.proxy, |
||||
); |
||||
} |
||||
|
||||
checkEthHelper(deploy: AnyBridgeDeploy): void { |
||||
if (deploy.config.weth) { |
||||
expect(deploy.contracts.ethHelper).to.not.be.undefined; |
||||
} else { |
||||
expect(deploy.contracts.ethHelper).to.be.undefined; |
||||
} |
||||
} |
||||
|
||||
getVerificationInputs(deploy: AnyBridgeDeploy): VerificationInput[] { |
||||
const inputs: VerificationInput[] = [] |
||||
const addInputsForUpgradableContract = (contract: BeaconProxy<any>, name: string) => { |
||||
inputs.push([`${name} Implementation`, contract.implementation]) |
||||
inputs.push([`${name} UpgradeBeacon`, contract.beacon]) |
||||
inputs.push([`${name} Proxy`, contract.proxy]) |
||||
} |
||||
expect(deploy.contracts.bridgeToken).to.not.be.undefined; |
||||
expect(deploy.contracts.bridgeRouter).to.not.be.undefined; |
||||
addInputsForUpgradableContract(deploy.contracts.bridgeToken!, 'BridgeToken') |
||||
addInputsForUpgradableContract(deploy.contracts.bridgeRouter!, 'BridgeRouter') |
||||
if (deploy.config.weth) { |
||||
expect(deploy.contracts.ethHelper).to.not.be.undefined; |
||||
inputs.push(['EthHelper', deploy.contracts.ethHelper!]) |
||||
} |
||||
return inputs |
||||
} |
||||
} |
||||
|
@ -1,223 +1,202 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { BeaconProxy } from '../proxyUtils'; |
||||
import { CoreDeploy } from './CoreDeploy'; |
||||
import { |
||||
assertInvariantViolation, |
||||
checkBeaconProxyImplementation, |
||||
checkVerificationInput, |
||||
InvariantViolationHandler, |
||||
} from '../checks'; |
||||
import { VerificationInput, ViolationType, HomeUpdaterViolation, ReplicaUpdaterViolation, UpdaterManagerViolation, InvariantChecker } from '../checks'; |
||||
|
||||
const emptyAddr = '0x' + '00'.repeat(20); |
||||
|
||||
export async function checkCoreDeploys( |
||||
deploys: CoreDeploy[], |
||||
governorDomain: number, |
||||
invariantViolationHandler: InvariantViolationHandler |
||||
) { |
||||
const checkDeploy = async (deploy: CoreDeploy) => { |
||||
const remoteDomains = deploys.filter(_ => _.chain.domain !== deploy.chain.domain).map(_ => _.chain.domain) |
||||
export class CoreInvariantChecker extends InvariantChecker<CoreDeploy> { |
||||
|
||||
console.info(`Checking core deploy on ${deploy.chain.name}`) |
||||
return checkCoreDeploy(deploy, remoteDomains, governorDomain, invariantViolationHandler) |
||||
constructor(deploys: CoreDeploy[]) { |
||||
super(deploys) |
||||
} |
||||
|
||||
await Promise.all(deploys.map(checkDeploy)) |
||||
} |
||||
async checkDeploy(deploy: CoreDeploy): Promise<void> { |
||||
this.checkContractsDefined(deploy) |
||||
await this.checkBeaconProxies(deploy) |
||||
await this.checkHome(deploy) |
||||
await this.checkReplicas(deploy) |
||||
await this.checkGovernance(deploy) |
||||
await this.checkXAppConnectionManager(deploy) |
||||
this.checkVerificationInputs(deploy) |
||||
} |
||||
|
||||
export async function checkCoreDeploy( |
||||
deploy: CoreDeploy, |
||||
remoteDomains: number[], |
||||
governorDomain: number, |
||||
invariantViolationHandler: InvariantViolationHandler = assertInvariantViolation, |
||||
) { |
||||
// Home upgrade setup contracts are defined
|
||||
await checkBeaconProxyImplementation( |
||||
deploy.chain.domain, |
||||
'Home', |
||||
deploy.contracts.upgradeBeaconController!, |
||||
deploy.contracts.home!, |
||||
invariantViolationHandler, |
||||
); |
||||
|
||||
// updaterManager is set on Home
|
||||
const updaterManager = await deploy.contracts.home?.proxy.updaterManager(); |
||||
expect(updaterManager).to.equal(deploy.contracts.updaterManager?.address); |
||||
|
||||
// GovernanceRouter upgrade setup contracts are defined
|
||||
await checkBeaconProxyImplementation( |
||||
deploy.chain.domain, |
||||
'Governance', |
||||
deploy.contracts.upgradeBeaconController!, |
||||
deploy.contracts.governance!, |
||||
invariantViolationHandler, |
||||
); |
||||
|
||||
for (const domain of remoteDomains) { |
||||
// Replica upgrade setup contracts are defined
|
||||
await checkBeaconProxyImplementation( |
||||
deploy.chain.domain, |
||||
'Replica', |
||||
deploy.contracts.upgradeBeaconController!, |
||||
deploy.contracts.replicas[domain]!, |
||||
invariantViolationHandler, |
||||
); |
||||
// governanceRouter for remote domain is registered
|
||||
const registeredRouter = await deploy.contracts.governance?.proxy.routers( |
||||
domain, |
||||
); |
||||
expect(registeredRouter).to.not.equal(emptyAddr); |
||||
// replica is enrolled in xAppConnectionManager
|
||||
const enrolledReplica = |
||||
await deploy.contracts.xAppConnectionManager?.domainToReplica(domain); |
||||
expect(enrolledReplica).to.not.equal(emptyAddr); |
||||
//watchers have permission in xAppConnectionManager
|
||||
/* |
||||
await Promise.all( |
||||
deploy.config.watchers.map(async (watcher) => { |
||||
const watcherPermissions = |
||||
await deploy.contracts.xAppConnectionManager?.watcherPermission( |
||||
watcher, |
||||
domain, |
||||
); |
||||
expect(watcherPermissions).to.be.true; |
||||
}), |
||||
); |
||||
*/ |
||||
checkContractsDefined(deploy: CoreDeploy): void { |
||||
const contracts = deploy.contracts; |
||||
expect(contracts.home).to.not.be.undefined; |
||||
expect(contracts.governance).to.not.be.undefined; |
||||
expect(contracts.upgradeBeaconController).to.not.be.undefined; |
||||
expect(contracts.xAppConnectionManager).to.not.be.undefined; |
||||
expect(contracts.updaterManager).to.not.be.undefined; |
||||
for (const domain in contracts.replicas) { |
||||
expect(contracts.replicas[domain]).to.not.be.undefined; |
||||
} |
||||
} |
||||
|
||||
if (remoteDomains.length > 0) { |
||||
// expect all replicas to have to same implementation and upgradeBeacon
|
||||
const firstReplica = deploy.contracts.replicas[remoteDomains[0]]!; |
||||
const replicaImpl = firstReplica.implementation.address; |
||||
const replicaBeacon = firstReplica.beacon.address; |
||||
// check every other implementation/beacon matches the first
|
||||
remoteDomains.slice(1).forEach((remoteDomain) => { |
||||
const replica = deploy.contracts.replicas[remoteDomain]!; |
||||
const implementation = replica.implementation.address; |
||||
const beacon = replica.beacon.address; |
||||
expect(implementation).to.equal(replicaImpl); |
||||
expect(beacon).to.equal(replicaBeacon); |
||||
}); |
||||
async checkHome(deploy: CoreDeploy): Promise<void> { |
||||
// contracts are defined
|
||||
const home = deploy.contracts.home!.proxy; |
||||
// updaterManager is set on Home
|
||||
const actualManager = await home.updaterManager(); |
||||
const expectedManager = deploy.contracts.updaterManager!.address; |
||||
if (actualManager !== expectedManager) { |
||||
const violation: UpdaterManagerViolation = { |
||||
domain: deploy.chain.domain, |
||||
type: ViolationType.UpdaterManager, |
||||
actual: actualManager, |
||||
expected: expectedManager, |
||||
} |
||||
this.addViolation(violation) |
||||
} |
||||
|
||||
const actual = await home?.updater()!; |
||||
expect(actual).to.not.be.undefined; |
||||
const expected = deploy.config.updater; |
||||
if (actual !== expected) { |
||||
const violation: HomeUpdaterViolation = { |
||||
domain: deploy.chain.domain, |
||||
type: ViolationType.HomeUpdater, |
||||
actual, |
||||
expected, |
||||
} |
||||
this.addViolation(violation) |
||||
} |
||||
} |
||||
|
||||
// contracts are defined
|
||||
expect(deploy.contracts.updaterManager).to.not.be.undefined; |
||||
expect(deploy.contracts.upgradeBeaconController).to.not.be.undefined; |
||||
expect(deploy.contracts.xAppConnectionManager).to.not.be.undefined; |
||||
|
||||
// governor is set on governor chain, empty on others
|
||||
const gov = await deploy.contracts.governance?.proxy.governor(); |
||||
const localDomain = await deploy.contracts.home?.proxy.localDomain(); |
||||
if (governorDomain == localDomain) { |
||||
expect(gov).to.not.equal(emptyAddr); |
||||
} else { |
||||
expect(gov).to.equal(emptyAddr); |
||||
async checkReplicas(deploy: CoreDeploy): Promise<void> { |
||||
// Check if the Replicas on *remote* domains are set to the updater
|
||||
// configured on our domain.
|
||||
const domain = deploy.chain.domain |
||||
const addReplicaUpdaterViolations = async (remoteDeploy: CoreDeploy) => { |
||||
const replica = remoteDeploy.contracts.replicas[domain]; |
||||
// Sanity check remote domain.
|
||||
const actualRemoteDomain = await replica.proxy.remoteDomain(); |
||||
expect(actualRemoteDomain).to.be.equal(remoteDeploy.chain.domain); |
||||
const actual = await replica.proxy.updater(); |
||||
const expected = deploy.config.updater; |
||||
if (actual !== expected) { |
||||
const violation: ReplicaUpdaterViolation = { |
||||
domain: remoteDeploy.chain.domain, |
||||
remoteDomain: domain, |
||||
type: ViolationType.ReplicaUpdater, |
||||
actual, |
||||
expected, |
||||
} |
||||
this.addViolation(violation) |
||||
} |
||||
} |
||||
const remoteDeploys = this._deploys.filter((d) => d.chain.domain !== domain) |
||||
await Promise.all(remoteDeploys.map(addReplicaUpdaterViolations)) |
||||
// Check that all replicas on this domain share the same implementation and
|
||||
// UpgradeBeacon.
|
||||
const replicas = Object.values(deploy.contracts.replicas) |
||||
const implementations = replicas.map((r) => r.implementation.address); |
||||
const identical = (a: any, b: any) => (a === b) ? a : false; |
||||
const upgradeBeacons = replicas.map((r) => r.beacon.address); |
||||
expect(implementations.reduce(identical)).to.not.be.false; |
||||
expect(upgradeBeacons.reduce(identical)).to.not.be.false; |
||||
} |
||||
// governor domain is correct
|
||||
expect(await deploy.contracts.governance?.proxy.governorDomain()).to.equal( |
||||
governorDomain, |
||||
); |
||||
|
||||
// Home is set on xAppConnectionManager
|
||||
const xAppManagerHome = await deploy.contracts.xAppConnectionManager?.home(); |
||||
const homeAddress = deploy.contracts.home?.proxy.address; |
||||
expect(xAppManagerHome).to.equal(homeAddress); |
||||
|
||||
// governor has ownership over following contracts
|
||||
const updaterManagerOwner = await deploy.contracts.updaterManager?.owner(); |
||||
const xAppManagerOwner = |
||||
await deploy.contracts.xAppConnectionManager?.owner(); |
||||
const beaconOwner = await deploy.contracts.upgradeBeaconController?.owner(); |
||||
const homeOwner = await deploy.contracts.home?.proxy.owner(); |
||||
const governorAddr = deploy.contracts.governance?.proxy.address; |
||||
expect(updaterManagerOwner).to.equal(governorAddr); |
||||
expect(xAppManagerOwner).to.equal(governorAddr); |
||||
expect(beaconOwner).to.equal(governorAddr); |
||||
expect(homeOwner).to.equal(governorAddr); |
||||
// This bit fails when the replicas don't yet have the owner() function.
|
||||
/* |
||||
Object.entries(deploy.contracts.replicas).forEach(async ([domain, replica]) => { |
||||
const replicaOwner = await replica.proxy.owner() |
||||
expect(replicaOwner).to.equal(governorAddr) |
||||
}) |
||||
*/ |
||||
|
||||
checkCoreVerificationInput(deploy, remoteDomains) |
||||
} |
||||
|
||||
function checkCoreVerificationInput( |
||||
deploy: CoreDeploy, |
||||
remoteDomains: number[], |
||||
) { |
||||
// Checks that verification input is consistent with deployed contracts.
|
||||
checkVerificationInput( |
||||
deploy, |
||||
'UpgradeBeaconController', |
||||
deploy.contracts.upgradeBeaconController?.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'XAppConnectionManager', |
||||
deploy.contracts.xAppConnectionManager?.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'UpdaterManager', |
||||
deploy.contracts.updaterManager?.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Home Implementation', |
||||
deploy.contracts.home?.implementation.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Home UpgradeBeacon', |
||||
deploy.contracts.home?.beacon.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Home Proxy', |
||||
deploy.contracts.home?.proxy.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Governance Implementation', |
||||
deploy.contracts.governance?.implementation.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Governance UpgradeBeacon', |
||||
deploy.contracts.governance?.beacon.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Governance Proxy', |
||||
deploy.contracts.governance?.proxy.address!, |
||||
); |
||||
|
||||
if (remoteDomains.length > 0) { |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Replica Implementation', |
||||
deploy.contracts.replicas[remoteDomains[0]]?.implementation.address!, |
||||
); |
||||
checkVerificationInput( |
||||
deploy, |
||||
'Replica UpgradeBeacon', |
||||
deploy.contracts.replicas[remoteDomains[0]]?.beacon.address!, |
||||
async checkGovernance(deploy: CoreDeploy): Promise<void> { |
||||
expect(deploy.contracts.governance).to.not.be.undefined; |
||||
|
||||
// governanceRouter for each remote domain is registered
|
||||
const registeredRouters = await Promise.all( |
||||
Object.keys(deploy.contracts.replicas) |
||||
.map(_ => deploy.contracts.governance?.proxy.routers(_)) |
||||
) |
||||
registeredRouters.map(_ => expect(_).to.not.equal(emptyAddr)) |
||||
|
||||
// governor is set on governor chain, empty on others
|
||||
// TODO: assert all governance routers have the same governor domain
|
||||
const governorDomain = await deploy.contracts.governance?.proxy.governorDomain() |
||||
const gov = await deploy.contracts.governance?.proxy.governor(); |
||||
const localDomain = await deploy.contracts.home?.proxy.localDomain(); |
||||
if (governorDomain == localDomain) { |
||||
expect(gov).to.not.equal(emptyAddr); |
||||
} else { |
||||
expect(gov).to.equal(emptyAddr); |
||||
} |
||||
|
||||
const owners = [ |
||||
deploy.contracts.updaterManager?.owner()!, |
||||
deploy.contracts.xAppConnectionManager?.owner()!, |
||||
deploy.contracts.upgradeBeaconController?.owner()!, |
||||
deploy.contracts.home?.proxy.owner()!, |
||||
] |
||||
Object.values(deploy.contracts.replicas).map(_ => owners.push(_.proxy.owner())) |
||||
|
||||
const expectedOwner = deploy.contracts.governance?.proxy.address; |
||||
const actualOwners = await Promise.all(owners) |
||||
actualOwners.map(_ => expect(_).to.equal(expectedOwner)) |
||||
} |
||||
|
||||
async checkXAppConnectionManager(deploy: CoreDeploy): Promise<void> { |
||||
expect(deploy.contracts.xAppConnectionManager).to.not.be.undefined; |
||||
for (const domain in deploy.contracts.replicas) { |
||||
// replica is enrolled in xAppConnectionManager
|
||||
const enrolledReplica = |
||||
await deploy.contracts.xAppConnectionManager?.domainToReplica(domain); |
||||
expect(enrolledReplica).to.not.equal(emptyAddr); |
||||
//watchers have permission in xAppConnectionManager
|
||||
await Promise.all( |
||||
deploy.config.watchers.map(async (watcher) => { |
||||
const watcherPermissions = |
||||
await deploy.contracts.xAppConnectionManager?.watcherPermission( |
||||
watcher, |
||||
domain, |
||||
); |
||||
expect(watcherPermissions).to.be.true; |
||||
}), |
||||
); |
||||
} |
||||
// Home is set on xAppConnectionManager
|
||||
const xAppManagerHome = await deploy.contracts.xAppConnectionManager?.home(); |
||||
const homeAddress = deploy.contracts.home?.proxy.address; |
||||
expect(xAppManagerHome).to.equal(homeAddress); |
||||
} |
||||
|
||||
getVerificationInputs(deploy: CoreDeploy): VerificationInput[] { |
||||
const inputs: VerificationInput[] = []; |
||||
const contracts = deploy.contracts; |
||||
inputs.push(['UpgradeBeaconController', contracts.upgradeBeaconController!]) |
||||
inputs.push(['XAppConnectionManager', contracts.xAppConnectionManager!]) |
||||
inputs.push(['UpdaterManager', contracts.updaterManager!]) |
||||
const addInputsForUpgradableContract = (contract: BeaconProxy<any>, name: string) => { |
||||
inputs.push([`${name} Implementation`, contract.implementation]) |
||||
inputs.push([`${name} UpgradeBeacon`, contract.beacon]) |
||||
inputs.push([`${name} Proxy`, contract.proxy]) |
||||
} |
||||
addInputsForUpgradableContract(contracts.home!, 'Home') |
||||
addInputsForUpgradableContract(contracts.governance!, 'Governance') |
||||
for (const domain in contracts.replicas) { |
||||
addInputsForUpgradableContract(contracts.replicas[domain], 'Replica') |
||||
} |
||||
return inputs |
||||
} |
||||
|
||||
async checkBeaconProxies(deploy: CoreDeploy): Promise<void> { |
||||
const domain = deploy.chain.domain; |
||||
const contracts = deploy.contracts; |
||||
// Home upgrade setup contracts are defined
|
||||
await this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'Home', |
||||
contracts.home! |
||||
); |
||||
|
||||
const replicaProxies = deploy.verificationInput.filter( |
||||
(contract) => contract.name == 'Replica Proxy', |
||||
// GovernanceRouter upgrade setup contracts are defined
|
||||
await this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'Governance', |
||||
contracts.governance! |
||||
); |
||||
remoteDomains.forEach((domain) => { |
||||
const replicaProxy = replicaProxies.find((proxy) => { |
||||
return (proxy.address = |
||||
deploy.contracts.replicas[domain]?.proxy.address); |
||||
}); |
||||
expect(replicaProxy).to.not.be.undefined; |
||||
}); |
||||
|
||||
await Promise.all( |
||||
Object.values(contracts.replicas).map( |
||||
_ => this.checkBeaconProxyImplementation(domain, 'Replica', _) |
||||
) |
||||
) |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue