Use MultiProvider and the SDK in abacus-deploy (#277)
parent
f6168cbe47
commit
d40b1e4dc4
@ -0,0 +1,8 @@ |
||||
import { BridgeConfigWithoutCore } from '../../../src/bridge'; |
||||
|
||||
export const bridge: BridgeConfigWithoutCore = { |
||||
weth: { |
||||
// Deployment calls weth.approve()
|
||||
// celo: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e',
|
||||
}, |
||||
}; |
@ -0,0 +1,37 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { NonceManager } from '@ethersproject/experimental'; |
||||
import { ChainName, MultiProvider } from '@abacus-network/sdk'; |
||||
import { registerDomains } from '../../../src/config'; |
||||
import { configs } from '../../networks/testnets'; |
||||
|
||||
export const domainNames: ChainName[] = [ |
||||
'alfajores', |
||||
'kovan', |
||||
'mumbai', |
||||
'fuji', |
||||
]; |
||||
|
||||
export const registerMultiProvider = (multiProvider: MultiProvider) => { |
||||
// Hardhat account 0
|
||||
const key = |
||||
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; |
||||
const provider = new ethers.providers.JsonRpcProvider( |
||||
'http://localhost:8545', |
||||
); |
||||
|
||||
const wallet = new ethers.Wallet(key, provider); |
||||
const signer = new NonceManager(wallet); |
||||
registerMultiProviderTest(multiProvider, signer); |
||||
}; |
||||
|
||||
export const registerMultiProviderTest = ( |
||||
multiProvider: MultiProvider, |
||||
signer: ethers.Signer, |
||||
) => { |
||||
registerDomains(domainNames, configs, multiProvider); |
||||
domainNames.forEach((name) => { |
||||
multiProvider.registerSigner(name, signer); |
||||
// Hardhat mines blocks lazily so anything > 0 will cause the test to stall.
|
||||
multiProvider.registerConfirmations(name, 0); |
||||
}); |
||||
}; |
@ -0,0 +1,20 @@ |
||||
import { GovernanceConfigWithoutCore } from '../../../src/governance'; |
||||
|
||||
export const governance: GovernanceConfigWithoutCore = { |
||||
recoveryTimelock: 180, |
||||
addresses: { |
||||
alfajores: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
governor: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
kovan: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
mumbai: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
fuji: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
}, |
||||
}; |
@ -0,0 +1,9 @@ |
||||
export { core } from './core'; |
||||
export { governance } from './governance'; |
||||
export { bridge } from './bridge'; |
||||
export { agent } from './agent'; |
||||
export { |
||||
domainNames, |
||||
registerMultiProvider, |
||||
registerMultiProviderTest, |
||||
} from './domains'; |
@ -1,67 +1,63 @@ |
||||
import { BigNumber } from 'ethers'; |
||||
import { ChainConfigWithoutSigner, ChainName } from '../../src/config/chain'; |
||||
import { ChainName } from '@abacus-network/sdk'; |
||||
import { TransactionConfig } from '../../src/config/chain'; |
||||
|
||||
export const alfajores: ChainConfigWithoutSigner = { |
||||
name: ChainName.ALFAJORES, |
||||
domain: 1000, |
||||
export const alfajores: TransactionConfig = { |
||||
confirmations: 1, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
export const fuji: ChainConfigWithoutSigner = { |
||||
name: ChainName.FUJI, |
||||
domain: 43113, |
||||
export const fuji: TransactionConfig = { |
||||
confirmations: 1, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
export const goerli: ChainConfigWithoutSigner = { |
||||
name: ChainName.GOERLI, |
||||
domain: 5, |
||||
export const goerli: TransactionConfig = { |
||||
confirmations: 3, |
||||
overrides: { |
||||
gasPrice: BigNumber.from(10_000_000_000), |
||||
}, |
||||
}; |
||||
|
||||
export const kovan: ChainConfigWithoutSigner = { |
||||
name: ChainName.KOVAN, |
||||
domain: 3000, |
||||
export const kovan: TransactionConfig = { |
||||
confirmations: 3, |
||||
overrides: { |
||||
gasPrice: BigNumber.from(10_000_000_000), |
||||
}, |
||||
confirmations: 3, |
||||
}; |
||||
|
||||
export const mumbai: ChainConfigWithoutSigner = { |
||||
name: ChainName.MUMBAI, |
||||
domain: 80001, |
||||
export const mumbai: TransactionConfig = { |
||||
confirmations: 3, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
export const rinkarby: ChainConfigWithoutSigner = { |
||||
name: ChainName.RINKARBY, |
||||
domain: 4000, |
||||
export const rinkarby: TransactionConfig = { |
||||
confirmations: 2, |
||||
overrides: { |
||||
gasPrice: 0, |
||||
gasLimit: 600_000_000, |
||||
}, |
||||
confirmations: 2, |
||||
}; |
||||
|
||||
export const rinkeby: ChainConfigWithoutSigner = { |
||||
name: ChainName.RINKEBY, |
||||
domain: 2000, |
||||
export const rinkeby: TransactionConfig = { |
||||
confirmations: 3, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
export const ropsten: ChainConfigWithoutSigner = { |
||||
name: ChainName.ROPSTEN, |
||||
domain: 3, |
||||
export const ropsten: TransactionConfig = { |
||||
confirmations: 3, |
||||
overrides: { |
||||
gasPrice: BigNumber.from(10_000_000_000), |
||||
}, |
||||
}; |
||||
|
||||
export const configs: Partial<Record<ChainName, TransactionConfig>> = { |
||||
alfajores, |
||||
fuji, |
||||
goerli, |
||||
kovan, |
||||
mumbai, |
||||
rinkarby, |
||||
rinkeby, |
||||
ropsten, |
||||
}; |
||||
|
@ -1,18 +1,24 @@ |
||||
import { AbacusCore, coreAddresses } from '@abacus-network/sdk'; |
||||
import { |
||||
getEnvironment, |
||||
getBridgeConfig, |
||||
getBridgeDirectory, |
||||
getChainConfigsRecord, |
||||
getBridgeContractsSdkFilepath, |
||||
getBridgeVerificationDirectory, |
||||
registerMultiProvider, |
||||
} from './utils'; |
||||
import { BridgeDeploy } from '../src/bridge'; |
||||
import { AbacusBridgeDeployer } from '../src/bridge'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigsRecord(environment); |
||||
const config = await getBridgeConfig(environment); |
||||
const deploy = new BridgeDeploy(); |
||||
await deploy.deploy(chains, config); |
||||
deploy.writeOutput(getBridgeDirectory(environment)); |
||||
const config = await getBridgeConfig( |
||||
environment, |
||||
new AbacusCore(coreAddresses[environment]), |
||||
); |
||||
const deployer = new AbacusBridgeDeployer(); |
||||
await registerMultiProvider(deployer, environment); |
||||
await deployer.deploy(config); |
||||
deployer.writeContracts(getBridgeContractsSdkFilepath(environment)); |
||||
deployer.writeVerification(getBridgeVerificationDirectory(environment)); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,20 +1,24 @@ |
||||
import { |
||||
getEnvironment, |
||||
getCoreConfig, |
||||
getCoreDirectory, |
||||
getChainConfigsRecord, |
||||
getCoreContractsSdkFilepath, |
||||
getCoreRustDirectory, |
||||
getCoreVerificationDirectory, |
||||
registerMultiProvider, |
||||
} from './utils'; |
||||
import { CoreDeploy } from '../src/core'; |
||||
import { AbacusCoreDeployer } from '../src/core'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigsRecord(environment); |
||||
const deployer = new AbacusCoreDeployer(); |
||||
await registerMultiProvider(deployer, environment); |
||||
|
||||
const config = await getCoreConfig(environment); |
||||
const deploy = new CoreDeploy(); |
||||
await deploy.deploy(chains, config); |
||||
const outputDir = getCoreDirectory(environment); |
||||
deploy.writeOutput(outputDir); |
||||
deploy.writeRustConfigs(environment, outputDir); |
||||
await deployer.deploy(config); |
||||
|
||||
deployer.writeContracts(getCoreContractsSdkFilepath(environment)); |
||||
deployer.writeVerification(getCoreVerificationDirectory(environment)); |
||||
deployer.writeRustConfigs(environment, getCoreRustDirectory(environment)); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,14 +1,11 @@ |
||||
import { createAgentGCPKeys } from '../src/agents/gcp'; |
||||
import { getEnvironment, getChainConfigs } from './utils'; |
||||
import { getEnvironment, getDomainNames } from './utils'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigs(environment); |
||||
const domainNames = await getDomainNames(environment); |
||||
|
||||
return createAgentGCPKeys( |
||||
environment, |
||||
Object.values(chains).map((c) => c.name), |
||||
); |
||||
return createAgentGCPKeys(environment, domainNames); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,14 +1,11 @@ |
||||
import { deleteAgentGCPKeys } from '../src/agents/gcp'; |
||||
import { getEnvironment, getChainConfigsRecord } from './utils'; |
||||
import { getEnvironment, getDomainNames } from './utils'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigsRecord(environment); |
||||
const domainNames = await getDomainNames(environment); |
||||
|
||||
return deleteAgentGCPKeys( |
||||
environment, |
||||
Object.values(chains).map((c) => c.name), |
||||
); |
||||
return deleteAgentGCPKeys(environment, domainNames); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,11 +0,0 @@ |
||||
import { getCoreDeploy, getCoreDirectory, getEnvironment } from './utils'; |
||||
import { ImplementationDeployer } from '../src/core/implementation'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const coreDeploy = await getCoreDeploy(environment); |
||||
const deployer = new ImplementationDeployer(coreDeploy); |
||||
await deployer.deployInboxImplementations(); |
||||
coreDeploy.writeOutput(getCoreDirectory(environment)); |
||||
} |
||||
main().then(console.log).catch(console.error); |
@ -1,12 +1,12 @@ |
||||
import { runKeymasterHelmCommand } from '../src/agents'; |
||||
import { HelmCommand } from '../src/utils/helm'; |
||||
import { getAgentConfig, getChainConfigs, getEnvironment } from './utils'; |
||||
import { getAgentConfig, getDomainNames, getEnvironment } from './utils'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigs(environment); |
||||
const domainNames = await getDomainNames(environment); |
||||
const agentConfig = await getAgentConfig(environment); |
||||
return runKeymasterHelmCommand(HelmCommand.Install, agentConfig, chains); |
||||
return runKeymasterHelmCommand(HelmCommand.Install, agentConfig, domainNames); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,26 +1,37 @@ |
||||
import { |
||||
getBridgeDeploy, |
||||
getChainConfigsRecord, |
||||
getCoreDeploy, |
||||
AbacusCore, |
||||
AbacusBridge, |
||||
coreAddresses, |
||||
bridgeAddresses, |
||||
} from '@abacus-network/sdk'; |
||||
import { |
||||
getEnvironment, |
||||
getGovernanceConfig, |
||||
getGovernanceDirectory, |
||||
getGovernanceContractsSdkFilepath, |
||||
getGovernanceVerificationDirectory, |
||||
registerMultiProvider, |
||||
} from './utils'; |
||||
import { GovernanceDeploy } from '../src/governance'; |
||||
import { AbacusCoreDeployer } from '../src/core'; |
||||
import { AbacusBridgeDeployer } from '../src/bridge'; |
||||
import { AbacusGovernanceDeployer } from '../src/governance'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigsRecord(environment); |
||||
const config = await getGovernanceConfig(environment); |
||||
const deploy = new GovernanceDeploy(); |
||||
await deploy.deploy(chains, config); |
||||
deploy.writeOutput(getGovernanceDirectory(environment)); |
||||
const core = new AbacusCore(coreAddresses[environment]); |
||||
const bridge = new AbacusBridge(bridgeAddresses[environment]); |
||||
registerMultiProvider(core, environment); |
||||
registerMultiProvider(bridge, environment); |
||||
|
||||
const core = await getCoreDeploy(environment); |
||||
await core.transferOwnership(deploy.routerAddresses()); |
||||
const config = await getGovernanceConfig(environment, core); |
||||
const deployer = new AbacusGovernanceDeployer(); |
||||
await registerMultiProvider(deployer, environment); |
||||
await deployer.deploy(config); |
||||
deployer.writeContracts(getGovernanceContractsSdkFilepath(environment)); |
||||
deployer.writeVerification(getGovernanceVerificationDirectory(environment)); |
||||
|
||||
const bridge = await getBridgeDeploy(environment); |
||||
await bridge.transferOwnership(deploy.routerAddresses()); |
||||
const owners = deployer.routerAddresses; |
||||
await AbacusCoreDeployer.transferOwnership(core, owners); |
||||
await AbacusBridgeDeployer.transferOwnership(bridge, owners); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,17 +0,0 @@ |
||||
import { |
||||
getCoreDeploy, |
||||
getBridgeDeploy, |
||||
getGovernanceDeploy, |
||||
getEnvironment, |
||||
} from './utils'; |
||||
import { updateSdkDomain } from '../src/sdk'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const coreDeploy = await getCoreDeploy(environment); |
||||
const bridgeDeploy = await getBridgeDeploy(environment); |
||||
const governanceDeploy = await getGovernanceDeploy(environment); |
||||
updateSdkDomain(environment, coreDeploy, governanceDeploy, bridgeDeploy); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
@ -1,12 +1,12 @@ |
||||
import { runKeymasterHelmCommand } from '../src/agents'; |
||||
import { HelmCommand } from '../src/utils/helm'; |
||||
import { getAgentConfig, getChainConfigs, getEnvironment } from './utils'; |
||||
import { getAgentConfig, getDomainNames, getEnvironment } from './utils'; |
||||
|
||||
async function main() { |
||||
const environment = await getEnvironment(); |
||||
const chains = await getChainConfigs(environment); |
||||
const domainNames = await getDomainNames(environment); |
||||
const agentConfig = await getAgentConfig(environment); |
||||
return runKeymasterHelmCommand(HelmCommand.Upgrade, agentConfig, chains); |
||||
return runKeymasterHelmCommand(HelmCommand.Upgrade, agentConfig, domainNames); |
||||
} |
||||
|
||||
main().then(console.log).catch(console.error); |
||||
|
@ -1,59 +0,0 @@ |
||||
import fs from 'fs'; |
||||
import { |
||||
ETHHelper, |
||||
ETHHelper__factory, |
||||
BridgeRouter, |
||||
BridgeRouter__factory, |
||||
BridgeToken, |
||||
BridgeToken__factory, |
||||
} from '@abacus-network/apps'; |
||||
import { ethers } from 'ethers'; |
||||
import { CommonContracts, BeaconProxy } from '../common'; |
||||
import { BridgeContractAddresses } from './types'; |
||||
|
||||
export class BridgeContracts extends CommonContracts<BridgeContractAddresses> { |
||||
constructor( |
||||
public readonly router: BeaconProxy<BridgeRouter>, |
||||
public readonly token: BeaconProxy<BridgeToken>, |
||||
public readonly helper?: ETHHelper, |
||||
) { |
||||
super(); |
||||
} |
||||
|
||||
toObject(): BridgeContractAddresses { |
||||
return { |
||||
router: this.router.toObject(), |
||||
token: this.token.toObject(), |
||||
helper: this.helper?.address, |
||||
}; |
||||
} |
||||
|
||||
static readJson(filepath: string, signer: ethers.Signer): BridgeContracts { |
||||
const contents = fs.readFileSync(filepath, 'utf8'); |
||||
const addresses: BridgeContractAddresses = JSON.parse(contents); |
||||
return BridgeContracts.fromObject(addresses, signer); |
||||
} |
||||
|
||||
static fromObject( |
||||
addresses: BridgeContractAddresses, |
||||
signer: ethers.Signer, |
||||
): BridgeContracts { |
||||
const router: BeaconProxy<BridgeRouter> = BeaconProxy.fromObject( |
||||
addresses.router, |
||||
BridgeRouter__factory.abi, |
||||
signer, |
||||
); |
||||
|
||||
const token: BeaconProxy<BridgeToken> = BeaconProxy.fromObject( |
||||
addresses.token, |
||||
BridgeToken__factory.abi, |
||||
signer, |
||||
); |
||||
|
||||
if (addresses.helper) { |
||||
const helper = ETHHelper__factory.connect(addresses.helper, signer); |
||||
return new BridgeContracts(router, token, helper); |
||||
} |
||||
return new BridgeContracts(router, token); |
||||
} |
||||
} |
@ -1,44 +0,0 @@ |
||||
import { BridgeToken, BridgeRouter, ETHHelper } from '@abacus-network/apps'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { BridgeConfig } from './types'; |
||||
import { BridgeInstance } from './BridgeInstance'; |
||||
import { BridgeContracts } from './BridgeContracts'; |
||||
import { CommonDeploy, DeployType } from '../common'; |
||||
import { ChainConfig } from '../config'; |
||||
import { RouterDeploy } from '../router'; |
||||
|
||||
export class BridgeDeploy extends RouterDeploy<BridgeInstance, BridgeConfig> { |
||||
deployType = DeployType.BRIDGE; |
||||
|
||||
async deployInstance( |
||||
domain: types.Domain, |
||||
config: BridgeConfig, |
||||
): Promise<BridgeInstance> { |
||||
return BridgeInstance.deploy(domain, this.chains, config); |
||||
} |
||||
|
||||
static readContracts( |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
directory: string, |
||||
): BridgeDeploy { |
||||
return CommonDeploy.readContractsHelper( |
||||
BridgeDeploy, |
||||
BridgeInstance, |
||||
BridgeContracts.readJson, |
||||
chains, |
||||
directory, |
||||
); |
||||
} |
||||
|
||||
token(domain: types.Domain): BridgeToken { |
||||
return this.instances[domain].token; |
||||
} |
||||
|
||||
router(domain: types.Domain): BridgeRouter { |
||||
return this.instances[domain].router; |
||||
} |
||||
|
||||
helper(domain: types.Domain): ETHHelper | undefined { |
||||
return this.instances[domain].helper; |
||||
} |
||||
} |
@ -1,106 +0,0 @@ |
||||
import { |
||||
ETHHelper, |
||||
ETHHelper__factory, |
||||
BridgeRouter, |
||||
BridgeRouter__factory, |
||||
BridgeToken, |
||||
BridgeToken__factory, |
||||
} from '@abacus-network/apps'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { ChainConfig } from '../config'; |
||||
import { ContractDeployer, BeaconProxy } from '../common'; |
||||
import { BridgeContracts } from './BridgeContracts'; |
||||
import { BridgeConfig } from './types'; |
||||
import { RouterInstance } from '../router'; |
||||
import { |
||||
getContractVerificationInput, |
||||
getBeaconProxyVerificationInput, |
||||
VerificationInput, |
||||
} from '../verification'; |
||||
|
||||
export class BridgeInstance extends RouterInstance<BridgeContracts> { |
||||
async transferOwnership(owner: types.Address) { |
||||
const tx = await this.router.transferOwnership(owner, this.chain.overrides); |
||||
await tx.wait(this.chain.confirmations); |
||||
} |
||||
|
||||
static async deploy( |
||||
domain: types.Domain, |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
config: BridgeConfig, |
||||
): Promise<BridgeInstance> { |
||||
const chain = chains[domain]; |
||||
const core = config.core[chain.name]; |
||||
if (core === undefined) throw new Error('could not find core'); |
||||
|
||||
const token: BeaconProxy<BridgeToken> = await BeaconProxy.deploy( |
||||
chain, |
||||
new BridgeToken__factory(chain.signer), |
||||
core.upgradeBeaconController, |
||||
[], |
||||
[], |
||||
); |
||||
|
||||
const router: BeaconProxy<BridgeRouter> = await BeaconProxy.deploy( |
||||
chain, |
||||
new BridgeRouter__factory(chain.signer), |
||||
core.upgradeBeaconController, |
||||
[], |
||||
[token.beacon.address, core.xAppConnectionManager], |
||||
); |
||||
|
||||
const weth = config.weth[chain.name]; |
||||
if (weth) { |
||||
const deployer = new ContractDeployer(chain); |
||||
const helper: ETHHelper = await deployer.deploy( |
||||
new ETHHelper__factory(chain.signer), |
||||
weth, |
||||
router.address, |
||||
); |
||||
const contracts = new BridgeContracts(router, token, helper); |
||||
return new BridgeInstance(chain, contracts); |
||||
} |
||||
const contracts = new BridgeContracts(router, token); |
||||
return new BridgeInstance(chain, contracts); |
||||
} |
||||
|
||||
get token(): BridgeToken { |
||||
return this.contracts.token.contract; |
||||
} |
||||
|
||||
get router(): BridgeRouter { |
||||
return this.contracts.router.contract; |
||||
} |
||||
|
||||
get helper(): ETHHelper | undefined { |
||||
return this.contracts.helper; |
||||
} |
||||
|
||||
get verificationInput(): VerificationInput { |
||||
let input: VerificationInput = []; |
||||
input = input.concat( |
||||
getBeaconProxyVerificationInput( |
||||
'BridgeToken', |
||||
this.contracts.token, |
||||
BridgeToken__factory.bytecode, |
||||
), |
||||
); |
||||
input = input.concat( |
||||
getBeaconProxyVerificationInput( |
||||
'BridgeRouter', |
||||
this.contracts.router, |
||||
BridgeRouter__factory.bytecode, |
||||
), |
||||
); |
||||
if (this.helper) { |
||||
input.push( |
||||
getContractVerificationInput( |
||||
'ETH Helper', |
||||
this.helper, |
||||
ETHHelper__factory.bytecode, |
||||
), |
||||
); |
||||
} |
||||
return input; |
||||
} |
||||
} |
@ -1,39 +0,0 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { types } from '@abacus-network/utils'; |
||||
import { BridgeConfig } from './types'; |
||||
import { BridgeDeploy } from './BridgeDeploy'; |
||||
import { RouterInvariantChecker } from '../router'; |
||||
|
||||
export class BridgeInvariantChecker extends RouterInvariantChecker< |
||||
BridgeDeploy, |
||||
BridgeConfig |
||||
> { |
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
await this.checkBeaconProxies(domain); |
||||
await this.checkEnrolledRouters(domain); |
||||
await this.checkOwnership(domain); |
||||
this.checkEthHelper(domain); |
||||
} |
||||
|
||||
async checkBeaconProxies(domain: types.Domain): Promise<void> { |
||||
await this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'BridgeToken', |
||||
this.deploy.instances[domain].contracts.token, |
||||
); |
||||
await this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'BridgeRouter', |
||||
this.deploy.instances[domain].contracts.router, |
||||
); |
||||
} |
||||
|
||||
checkEthHelper(domain: types.Domain): void { |
||||
if (this.config.weth[this.deploy.name(domain)]) { |
||||
expect(this.deploy.helper(domain)).to.not.be.undefined; |
||||
} else { |
||||
expect(this.deploy.helper(domain)).to.be.undefined; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,37 @@ |
||||
import { expect } from 'chai'; |
||||
|
||||
import { types } from '@abacus-network/utils'; |
||||
import { AbacusBridge } from '@abacus-network/sdk'; |
||||
import { BridgeRouter } from '@abacus-network/apps'; |
||||
import { BridgeConfig } from './types'; |
||||
import { AbacusRouterChecker } from '../router'; |
||||
|
||||
export class AbacusBridgeChecker extends AbacusRouterChecker< |
||||
AbacusBridge, |
||||
BridgeConfig |
||||
> { |
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
await super.checkDomain(domain); |
||||
await this.checkProxiedContracts(domain); |
||||
this.checkEthHelper(domain); |
||||
} |
||||
|
||||
async checkProxiedContracts(domain: types.Domain): Promise<void> { |
||||
const addresses = this.app.mustGetContracts(domain).addresses; |
||||
await this.checkProxiedContract(domain, 'BridgeToken', addresses.token); |
||||
await this.checkProxiedContract(domain, 'BridgeRouter', addresses.router); |
||||
} |
||||
|
||||
checkEthHelper(domain: types.Domain): void { |
||||
const helper = this.app.mustGetContracts(domain).helper; |
||||
if (this.config.weth[this.app.mustResolveDomainName(domain)]) { |
||||
expect(helper).to.not.be.undefined; |
||||
} else { |
||||
expect(helper).to.be.undefined; |
||||
} |
||||
} |
||||
|
||||
mustGetRouter(domain: types.Domain): BridgeRouter { |
||||
return this.app.mustGetContracts(domain).router; |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
BridgeRouter, |
||||
BridgeToken__factory, |
||||
BridgeRouter__factory, |
||||
ETHHelper__factory, |
||||
} from '@abacus-network/apps'; |
||||
import { AbacusBridge, BridgeContractAddresses } from '@abacus-network/sdk'; |
||||
import { AbacusRouterDeployer } from '../router'; |
||||
import { BridgeConfig } from './types'; |
||||
|
||||
export class AbacusBridgeDeployer extends AbacusRouterDeployer< |
||||
BridgeContractAddresses, |
||||
BridgeConfig |
||||
> { |
||||
async deployContracts( |
||||
domain: types.Domain, |
||||
config: BridgeConfig, |
||||
): Promise<BridgeContractAddresses> { |
||||
const signer = this.mustGetSigner(domain); |
||||
const name = this.mustResolveDomainName(domain); |
||||
const core = config.core[name]; |
||||
if (!core) throw new Error('could not find core'); |
||||
|
||||
const token = await this.deployProxiedContract( |
||||
domain, |
||||
'BridgeToken', |
||||
new BridgeToken__factory(signer), |
||||
core.upgradeBeaconController, |
||||
[], |
||||
[], |
||||
); |
||||
|
||||
const router = await this.deployProxiedContract( |
||||
domain, |
||||
'BridgeRouter', |
||||
new BridgeRouter__factory(signer), |
||||
core.upgradeBeaconController, |
||||
[], |
||||
[token.addresses.beacon, core.xAppConnectionManager], |
||||
); |
||||
|
||||
const addresses: BridgeContractAddresses = { |
||||
router: router.addresses, |
||||
token: token.addresses, |
||||
}; |
||||
|
||||
const weth = config.weth[name]; |
||||
if (weth) { |
||||
const helper = await this.deployContract( |
||||
domain, |
||||
'ETH Helper', |
||||
new ETHHelper__factory(signer), |
||||
weth, |
||||
router.address, |
||||
); |
||||
addresses.helper = helper.address; |
||||
} |
||||
return addresses; |
||||
} |
||||
|
||||
mustGetRouter(domain: number): BridgeRouter { |
||||
return BridgeRouter__factory.connect( |
||||
this.mustGetAddresses(domain).router.proxy, |
||||
this.mustGetSigner(domain), |
||||
); |
||||
} |
||||
|
||||
static async transferOwnership( |
||||
bridge: AbacusBridge, |
||||
owners: Record<types.Domain, types.Address>, |
||||
) { |
||||
for (const domain of bridge.domainNumbers) { |
||||
const owner = owners[domain]; |
||||
if (!owner) throw new Error(`Missing owner for ${domain}`); |
||||
await AbacusBridgeDeployer.transferOwnershipOfDomain( |
||||
bridge, |
||||
domain, |
||||
owner, |
||||
); |
||||
} |
||||
} |
||||
|
||||
static async transferOwnershipOfDomain( |
||||
bridge: AbacusBridge, |
||||
domain: types.Domain, |
||||
owner: types.Address, |
||||
): Promise<ethers.ContractReceipt> { |
||||
const contracts = bridge.mustGetContracts(domain); |
||||
const overrides = bridge.getOverrides(domain); |
||||
const tx = await contracts.router.transferOwnership(owner, overrides); |
||||
return tx.wait(bridge.getConfirmations(domain)); |
||||
} |
||||
} |
@ -1,9 +1,3 @@ |
||||
export { BridgeDeploy } from './BridgeDeploy'; |
||||
export { BridgeInstance } from './BridgeInstance'; |
||||
export { BridgeContracts } from './BridgeContracts'; |
||||
export { BridgeInvariantChecker } from './BridgeInvariantChecker'; |
||||
export { |
||||
BridgeContractAddresses, |
||||
BridgeConfig, |
||||
BridgeConfigWithoutCore, |
||||
} from './types'; |
||||
export { AbacusBridgeDeployer } from './deploy'; |
||||
export { AbacusBridgeChecker } from './check'; |
||||
export { BridgeConfig, BridgeConfigWithoutCore } from './types'; |
||||
|
@ -1,122 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { |
||||
UpgradeBeacon, |
||||
UpgradeBeacon__factory, |
||||
UpgradeBeaconProxy, |
||||
UpgradeBeaconProxy__factory, |
||||
} from '@abacus-network/core'; |
||||
import { types } from '@abacus-network/utils'; |
||||
|
||||
import { ChainConfig } from '../config'; |
||||
import { ContractDeployer } from './ContractDeployer'; |
||||
|
||||
export type ProxiedAddress = { |
||||
proxy: types.Address; |
||||
implementation: types.Address; |
||||
beacon: types.Address; |
||||
}; |
||||
|
||||
export class BeaconProxy<T extends ethers.Contract> { |
||||
constructor( |
||||
public readonly implementation: T, |
||||
public readonly proxy: UpgradeBeaconProxy, |
||||
public readonly beacon: UpgradeBeacon, |
||||
public readonly contract: T, |
||||
) {} |
||||
|
||||
/** |
||||
* Deploys the UpgradeBeacon, Implementation and Proxy for a given contract |
||||
* |
||||
* @param T - The contract |
||||
*/ |
||||
static async deploy<T extends ethers.Contract>( |
||||
chain: ChainConfig, |
||||
factory: ethers.ContractFactory, |
||||
ubcAddress: types.Address, |
||||
deployArgs: any[], |
||||
initArgs: any[], |
||||
): Promise<BeaconProxy<T>> { |
||||
const deployer = new ContractDeployer(chain, false); |
||||
const implementation: T = await deployer.deploy(factory, ...deployArgs); |
||||
const beacon: UpgradeBeacon = await deployer.deploy( |
||||
new UpgradeBeacon__factory(chain.signer), |
||||
implementation.address, |
||||
ubcAddress, |
||||
); |
||||
|
||||
const initData = implementation.interface.encodeFunctionData( |
||||
'initialize', |
||||
initArgs, |
||||
); |
||||
const proxy: UpgradeBeaconProxy = await deployer.deploy( |
||||
new UpgradeBeaconProxy__factory(chain.signer), |
||||
beacon.address, |
||||
initData, |
||||
); |
||||
// proxy wait(x) implies implementation and beacon wait(>=x)
|
||||
// due to nonce ordering
|
||||
await proxy.deployTransaction.wait(chain.confirmations); |
||||
return new BeaconProxy( |
||||
implementation as T, |
||||
proxy, |
||||
beacon, |
||||
factory.attach(proxy.address) as T, |
||||
); |
||||
} |
||||
|
||||
static fromObject<T extends ethers.Contract>( |
||||
addresses: ProxiedAddress, |
||||
abi: any, |
||||
signer: ethers.Signer, |
||||
): BeaconProxy<T> { |
||||
const implementation = new ethers.Contract( |
||||
addresses.implementation, |
||||
abi, |
||||
signer, |
||||
) as T; |
||||
const proxy = UpgradeBeaconProxy__factory.connect(addresses.proxy, signer); |
||||
const beacon = UpgradeBeacon__factory.connect(addresses.beacon, signer); |
||||
const contract = new ethers.Contract(addresses.proxy, abi, signer) as T; |
||||
return new BeaconProxy<T>(implementation, proxy, beacon, contract); |
||||
} |
||||
|
||||
/** |
||||
* Sets up a new proxy with the same beacon and implementation |
||||
* |
||||
* @param T - The contract |
||||
*/ |
||||
async duplicate( |
||||
chain: ChainConfig, |
||||
initArgs: any[], |
||||
): Promise<BeaconProxy<T>> { |
||||
const deployer = new ContractDeployer(chain); |
||||
const initData = this.implementation.interface.encodeFunctionData( |
||||
'initialize', |
||||
initArgs, |
||||
); |
||||
const proxy: UpgradeBeaconProxy = await deployer.deploy( |
||||
new UpgradeBeaconProxy__factory(chain.signer), |
||||
this.beacon.address, |
||||
initData, |
||||
); |
||||
|
||||
return new BeaconProxy( |
||||
this.implementation, |
||||
proxy, |
||||
this.beacon, |
||||
this.contract.attach(proxy.address) as T, |
||||
); |
||||
} |
||||
|
||||
get address(): types.Address { |
||||
return this.contract.address; |
||||
} |
||||
|
||||
toObject(): ProxiedAddress { |
||||
return { |
||||
proxy: this.proxy.address, |
||||
implementation: this.implementation.address, |
||||
beacon: this.beacon.address, |
||||
}; |
||||
} |
||||
} |
@ -1,22 +0,0 @@ |
||||
import fs from 'fs'; |
||||
import path from 'path'; |
||||
|
||||
export abstract class CommonContracts<T> { |
||||
constructor() {} |
||||
|
||||
abstract toObject(): T; |
||||
|
||||
toJson(): string { |
||||
return JSON.stringify(this.toObject()); |
||||
} |
||||
|
||||
toJsonPretty(): string { |
||||
return JSON.stringify(this.toObject(), null, 2); |
||||
} |
||||
|
||||
writeJson(filepath: string) { |
||||
const dir = path.dirname(filepath); |
||||
fs.mkdirSync(dir, { recursive: true }); |
||||
fs.writeFileSync(filepath, this.toJsonPretty()); |
||||
} |
||||
} |
@ -1,166 +0,0 @@ |
||||
import path from 'path'; |
||||
import fs from 'fs'; |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { ChainConfig, ChainName } from '../config'; |
||||
import { CommonInstance } from './CommonInstance'; |
||||
import { CommonContracts } from './CommonContracts'; |
||||
import { DeployType } from './types'; |
||||
|
||||
export abstract class CommonDeploy< |
||||
T extends CommonInstance<CommonContracts<any>>, |
||||
V, |
||||
> { |
||||
public readonly instances: Record<types.Domain, T>; |
||||
public readonly chains: Record<types.Domain, ChainConfig>; |
||||
|
||||
constructor() { |
||||
this.instances = {}; |
||||
this.chains = {}; |
||||
} |
||||
|
||||
abstract deployInstance(domain: types.Domain, config: V): Promise<T>; |
||||
abstract deployType: DeployType; |
||||
|
||||
configDirectory(directory: string) { |
||||
return path.join(directory, this.deployType); |
||||
} |
||||
|
||||
contractsDirectory(directory: string) { |
||||
return path.join(this.configDirectory(directory), 'contracts'); |
||||
} |
||||
|
||||
contractsFilepath(directory: string, chain: ChainName) { |
||||
return path.join(this.contractsDirectory(directory), `${chain}.json`); |
||||
} |
||||
|
||||
verificationDirectory(directory: string) { |
||||
return path.join(this.configDirectory(directory), 'verification'); |
||||
} |
||||
|
||||
async deploy(chains: Record<types.Domain, ChainConfig>, config: V) { |
||||
await this.ready(); |
||||
if (this.domains.length > 0) throw new Error('cannot deploy twice'); |
||||
const domains = Object.keys(chains).map((domain) => parseInt(domain)); |
||||
for (const domain of domains) { |
||||
this.chains[domain] = CommonDeploy.fixOverrides(chains[domain]); |
||||
} |
||||
for (const domain of domains) { |
||||
this.instances[domain] = await this.deployInstance(domain, config); |
||||
} |
||||
} |
||||
|
||||
async ready(): Promise<void> { |
||||
await Promise.all( |
||||
this.domains.map( |
||||
(d) => |
||||
(this.chains[d].signer.provider! as ethers.providers.JsonRpcProvider) |
||||
.ready, |
||||
), |
||||
); |
||||
} |
||||
|
||||
async transferOwnership(owners: Record<types.Domain, types.Address>) { |
||||
await Promise.all( |
||||
this.domains.map((d) => this.instances[d].transferOwnership(owners[d])), |
||||
); |
||||
} |
||||
|
||||
static readContractsHelper< |
||||
L extends CommonDeploy<M, any>, |
||||
M extends CommonInstance<O>, |
||||
O extends CommonContracts<any>, |
||||
>( |
||||
deployConstructor: { new (): L }, |
||||
instanceConstructor: { new (chain: ChainConfig, contracts: O): M }, |
||||
contractsReader: (directory: string, signer: ethers.Signer) => O, |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
directory: string, |
||||
): L { |
||||
const deploy = new deployConstructor(); |
||||
const domains = Object.keys(chains).map((d) => parseInt(d)); |
||||
for (const domain of domains) { |
||||
const chain = chains[domain]; |
||||
const contracts = contractsReader( |
||||
deploy.contractsFilepath(directory, chain.name), |
||||
chain.signer, |
||||
); |
||||
deploy.chains[domain] = chain; |
||||
deploy.instances[domain] = new instanceConstructor(chain, contracts); |
||||
} |
||||
return deploy; |
||||
} |
||||
|
||||
writeOutput(directory: string) { |
||||
this.writeContracts(directory); |
||||
this.writeVerificationInput(directory); |
||||
} |
||||
|
||||
writeContracts(directory: string) { |
||||
for (const domain of this.domains) { |
||||
this.instances[domain].contracts.writeJson( |
||||
path.join( |
||||
this.contractsDirectory(directory), |
||||
`${this.name(domain)}.json`, |
||||
), |
||||
); |
||||
} |
||||
} |
||||
|
||||
writeJson(filepath: string, obj: Object) { |
||||
const dir = path.dirname(filepath); |
||||
fs.mkdirSync(dir, { recursive: true }); |
||||
const contents = JSON.stringify(obj, null, 2); |
||||
fs.writeFileSync(filepath, contents); |
||||
} |
||||
|
||||
writeVerificationInput(directory: string) { |
||||
for (const domain of this.domains) { |
||||
const verificationInput = this.instances[domain].verificationInput; |
||||
const filepath = path.join( |
||||
this.verificationDirectory(directory), |
||||
`${this.name(domain)}.json`, |
||||
); |
||||
this.writeJson(filepath, verificationInput); |
||||
} |
||||
} |
||||
|
||||
signer(domain: types.Domain) { |
||||
return this.chains[domain].signer; |
||||
} |
||||
|
||||
name(domain: types.Domain) { |
||||
return this.chains[domain].name; |
||||
} |
||||
|
||||
overrides(domain: types.Domain) { |
||||
return this.chains[domain].overrides; |
||||
} |
||||
|
||||
get domains(): types.Domain[] { |
||||
return Object.keys(this.instances).map((d) => parseInt(d)); |
||||
} |
||||
|
||||
remotes(domain: types.Domain): types.Domain[] { |
||||
return this.domains.filter((d) => d !== domain); |
||||
} |
||||
|
||||
// this is currently a kludge to account for ethers issues
|
||||
static fixOverrides(chain: ChainConfig): ChainConfig { |
||||
let overrides: ethers.Overrides = {}; |
||||
if (chain.supports1559) { |
||||
overrides = { |
||||
maxFeePerGas: chain.overrides.maxFeePerGas, |
||||
maxPriorityFeePerGas: chain.overrides.maxPriorityFeePerGas, |
||||
gasLimit: chain.overrides.gasLimit, |
||||
}; |
||||
} else { |
||||
overrides = { |
||||
type: 0, |
||||
gasPrice: chain.overrides.gasPrice, |
||||
gasLimit: chain.overrides.gasLimit, |
||||
}; |
||||
} |
||||
return { ...chain, overrides }; |
||||
} |
||||
} |
@ -1,14 +0,0 @@ |
||||
import { types } from '@abacus-network/utils'; |
||||
import { ChainConfig } from '../config'; |
||||
import { VerificationInput } from '../verification'; |
||||
import { CommonContracts } from './CommonContracts'; |
||||
|
||||
export abstract class CommonInstance<T extends CommonContracts<any>> { |
||||
constructor( |
||||
public readonly chain: ChainConfig, |
||||
public readonly contracts: T, |
||||
) {} |
||||
|
||||
abstract transferOwnership(owner: types.Address): Promise<void>; |
||||
abstract verificationInput: VerificationInput; |
||||
} |
@ -1,20 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { ChainConfig } from '../config'; |
||||
|
||||
export class ContractDeployer { |
||||
constructor( |
||||
public readonly chain: ChainConfig, |
||||
public readonly wait = true, |
||||
) {} |
||||
|
||||
async deploy<T extends ethers.Contract>( |
||||
factory: ethers.ContractFactory, |
||||
...args: any[] |
||||
): Promise<T> { |
||||
const contract = (await factory.deploy(...args, this.chain.overrides)) as T; |
||||
if (this.wait) { |
||||
await contract.deployTransaction.wait(this.chain.confirmations); |
||||
} |
||||
return contract; |
||||
} |
||||
} |
@ -1,14 +0,0 @@ |
||||
export { CommonDeploy } from './CommonDeploy'; |
||||
export { CommonInstance } from './CommonInstance'; |
||||
export { CommonContracts } from './CommonContracts'; |
||||
export { |
||||
CommonInvariantChecker, |
||||
ViolationType, |
||||
ValidatorViolation, |
||||
ValidatorManagerViolation, |
||||
UpgradeBeaconViolation, |
||||
Violation, |
||||
} from './CommonInvariantChecker'; |
||||
export { ContractDeployer } from './ContractDeployer'; |
||||
export { BeaconProxy, ProxiedAddress } from './BeaconProxy'; |
||||
export { DeployType } from './types'; |
@ -1,5 +0,0 @@ |
||||
export enum DeployType { |
||||
CORE = 'core', |
||||
GOVERNANCE = 'governance', |
||||
BRIDGE = 'bridge', |
||||
} |
@ -1,31 +0,0 @@ |
||||
type Address = string; |
||||
|
||||
export type ProxiedAddress = { |
||||
proxy: Address; |
||||
implementation: Address; |
||||
beacon: Address; |
||||
}; |
||||
|
||||
export type CoreContractAddresses = { |
||||
upgradeBeaconController: Address; |
||||
xAppConnectionManager: Address; |
||||
validatorManager: Address; |
||||
governanceRouter: ProxiedAddress; |
||||
outbox: ProxiedAddress; |
||||
// TODO: Put chain name in here
|
||||
inboxes?: Record<number, ProxiedAddress>; |
||||
}; |
||||
|
||||
export type BridgeContractAddresses = { |
||||
bridgeRouter: ProxiedAddress; |
||||
bridgeToken: ProxiedAddress; |
||||
ethHelper?: Address; |
||||
}; |
||||
|
||||
export type CoreConfigAddresses = { |
||||
validator: Address; |
||||
recoveryManager: Address; |
||||
governor?: Address; |
||||
}; |
||||
|
||||
export type CoreDeployAddresses = CoreContractAddresses & CoreConfigAddresses; |
@ -1,63 +1,56 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { NonceManager } from '@ethersproject/experimental'; |
||||
import { ChainName, domains, MultiProvider } from '@abacus-network/sdk'; |
||||
import { getSecretDeployerKey, getSecretRpcEndpoint } from '../agents'; |
||||
|
||||
export enum ChainName { |
||||
// Mainnets
|
||||
CELO = 'celo', |
||||
ETHEREUM = 'ethereum', |
||||
AVALANCHE = 'avalanche', |
||||
POLYGON = 'polygon', |
||||
|
||||
// Testnets
|
||||
ALFAJORES = 'alfajores', |
||||
MUMBAI = 'mumbai', |
||||
KOVAN = 'kovan', |
||||
GOERLI = 'goerli', |
||||
FUJI = 'fuji', |
||||
RINKARBY = 'rinkarby', |
||||
RINKEBY = 'rinkeby', |
||||
ROPSTEN = 'ropsten', |
||||
|
||||
// Local
|
||||
LOCAL = 'local', |
||||
} |
||||
|
||||
export type ChainConfig = { |
||||
name: ChainName; |
||||
domain: number; |
||||
signer: ethers.Signer; |
||||
export type TransactionConfig = { |
||||
overrides: ethers.Overrides; |
||||
supports1559?: boolean; |
||||
// The number of confirmations considered reorg safe
|
||||
confirmations?: number; |
||||
poll_interval?: number; |
||||
}; |
||||
|
||||
export type ChainConfigWithoutSigner = Omit<ChainConfig, 'signer'>; |
||||
// this is currently a kludge to account for ethers issues
|
||||
export function fixOverrides(config: TransactionConfig): ethers.Overrides { |
||||
if (config.supports1559) { |
||||
return { |
||||
maxFeePerGas: config.overrides.maxFeePerGas, |
||||
maxPriorityFeePerGas: config.overrides.maxPriorityFeePerGas, |
||||
gasLimit: config.overrides.gasLimit, |
||||
}; |
||||
} else { |
||||
return { |
||||
type: 0, |
||||
gasPrice: config.overrides.gasPrice, |
||||
gasLimit: config.overrides.gasLimit, |
||||
}; |
||||
} |
||||
} |
||||
|
||||
export async function fetchSigner( |
||||
partial: ChainConfigWithoutSigner, |
||||
environment: string, |
||||
name: ChainName, |
||||
deployerKeySecretName: string, |
||||
): Promise<ChainConfig> { |
||||
const rpc = await getSecretRpcEndpoint(environment, partial.name); |
||||
): Promise<ethers.Signer> { |
||||
const rpc = await getSecretRpcEndpoint(environment, name); |
||||
const key = await getSecretDeployerKey(deployerKeySecretName); |
||||
const provider = new ethers.providers.JsonRpcProvider(rpc); |
||||
const wallet = new ethers.Wallet(key, provider); |
||||
const signer = new NonceManager(wallet); |
||||
return { ...partial, signer }; |
||||
return new NonceManager(wallet); |
||||
} |
||||
|
||||
export function getChainsForEnvironment( |
||||
partials: ChainConfigWithoutSigner[], |
||||
environment: string, |
||||
deployerKeySecretName: string, |
||||
) { |
||||
return () => |
||||
Promise.all( |
||||
partials.map((partial) => |
||||
fetchSigner(partial, environment, deployerKeySecretName), |
||||
), |
||||
); |
||||
} |
||||
export const registerDomains = ( |
||||
domainNames: ChainName[], |
||||
configs: Partial<Record<ChainName, TransactionConfig>>, |
||||
multiProvider: MultiProvider, |
||||
) => { |
||||
domainNames.forEach((name) => { |
||||
multiProvider.registerDomain(domains[name]); |
||||
const config = configs[name]; |
||||
if (!config) throw new Error(`Missing TransactionConfig for ${name}`); |
||||
multiProvider.registerOverrides(name, fixOverrides(config)); |
||||
if (config.confirmations) { |
||||
multiProvider.registerConfirmations(name, config.confirmations); |
||||
} |
||||
}); |
||||
}; |
||||
|
@ -1,9 +0,0 @@ |
||||
import { CoreConfigAddresses } from './addresses'; |
||||
import { ChainName } from './chain'; |
||||
import { DeployEnvironment } from '../config'; |
||||
|
||||
export interface CoreConfig { |
||||
environment: DeployEnvironment; |
||||
recoveryTimelock: number; |
||||
addresses: Partial<Record<ChainName, CoreConfigAddresses>>; |
||||
} |
@ -1,4 +1,3 @@ |
||||
export enum DeployEnvironment { |
||||
test = 'test', |
||||
local = 'local', |
||||
} |
||||
export const ALL_ENVIRONMENTS = ['test', 'local'] as const; |
||||
type DeployEnvironmentTuple = typeof ALL_ENVIRONMENTS; |
||||
export type DeployEnvironment = DeployEnvironmentTuple[number]; |
||||
|
@ -1,5 +1,5 @@ |
||||
export { AgentConfig, RustConfig } from './agent'; |
||||
export { ChainConfigWithoutSigner, ChainConfig, ChainName } from './chain'; |
||||
export { fetchSigner, TransactionConfig, registerDomains } from './chain'; |
||||
export { ContractMetricsConfig } from './contract-metrics'; |
||||
export { DeployEnvironment } from './environment'; |
||||
export { ALL_ENVIRONMENTS, DeployEnvironment } from './environment'; |
||||
export { InfrastructureConfig } from './infrastructure'; |
||||
|
@ -1,96 +0,0 @@ |
||||
import fs from 'fs'; |
||||
import { ethers } from 'ethers'; |
||||
import { |
||||
UpgradeBeaconController, |
||||
UpgradeBeaconController__factory, |
||||
XAppConnectionManager, |
||||
XAppConnectionManager__factory, |
||||
ValidatorManager, |
||||
ValidatorManager__factory, |
||||
Outbox, |
||||
Outbox__factory, |
||||
Inbox, |
||||
Inbox__factory, |
||||
} from '@abacus-network/core'; |
||||
import { types } from '@abacus-network/utils'; |
||||
|
||||
import { BeaconProxy, CommonContracts, ProxiedAddress } from '../common'; |
||||
import { CoreContractAddresses } from './types'; |
||||
|
||||
export class CoreContracts extends CommonContracts<CoreContractAddresses> { |
||||
constructor( |
||||
public readonly upgradeBeaconController: UpgradeBeaconController, |
||||
public readonly xAppConnectionManager: XAppConnectionManager, |
||||
public readonly validatorManager: ValidatorManager, |
||||
public readonly outbox: BeaconProxy<Outbox>, |
||||
public readonly inboxes: Record<types.Domain, BeaconProxy<Inbox>>, |
||||
) { |
||||
super(); |
||||
} |
||||
|
||||
toObject(): CoreContractAddresses { |
||||
const inboxes: Record<types.Domain, ProxiedAddress> = {}; |
||||
Object.keys(this.inboxes!) |
||||
.map((d) => parseInt(d)) |
||||
.map((domain: types.Domain) => { |
||||
inboxes[domain] = this.inboxes[domain].toObject(); |
||||
}); |
||||
|
||||
return { |
||||
upgradeBeaconController: this.upgradeBeaconController.address, |
||||
xAppConnectionManager: this.xAppConnectionManager.address, |
||||
validatorManager: this.validatorManager.address, |
||||
outbox: this.outbox.toObject(), |
||||
inboxes, |
||||
}; |
||||
} |
||||
|
||||
static readJson(filepath: string, signer: ethers.Signer): CoreContracts { |
||||
const contents = fs.readFileSync(filepath, 'utf8'); |
||||
const addresses: CoreContractAddresses = JSON.parse(contents); |
||||
return CoreContracts.fromObject(addresses, signer); |
||||
} |
||||
|
||||
static fromObject( |
||||
addresses: CoreContractAddresses, |
||||
signer: ethers.Signer, |
||||
): CoreContracts { |
||||
const upgradeBeaconController = UpgradeBeaconController__factory.connect( |
||||
addresses.upgradeBeaconController, |
||||
signer, |
||||
); |
||||
const xAppConnectionManager = XAppConnectionManager__factory.connect( |
||||
addresses.xAppConnectionManager, |
||||
signer, |
||||
); |
||||
const validatorManager = ValidatorManager__factory.connect( |
||||
addresses.validatorManager, |
||||
signer, |
||||
); |
||||
|
||||
const outbox: BeaconProxy<Outbox> = BeaconProxy.fromObject( |
||||
addresses.outbox, |
||||
Outbox__factory.abi, |
||||
signer, |
||||
); |
||||
|
||||
const inboxes: Record<types.Domain, BeaconProxy<Inbox>> = {}; |
||||
Object.keys(addresses.inboxes) |
||||
.map((d) => parseInt(d)) |
||||
.map((domain: types.Domain) => { |
||||
inboxes[domain] = BeaconProxy.fromObject( |
||||
addresses.inboxes[domain], |
||||
Inbox__factory.abi, |
||||
signer, |
||||
); |
||||
}); |
||||
|
||||
return new CoreContracts( |
||||
upgradeBeaconController, |
||||
xAppConnectionManager, |
||||
validatorManager, |
||||
outbox, |
||||
inboxes, |
||||
); |
||||
} |
||||
} |
@ -1,108 +0,0 @@ |
||||
import path from 'path'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
UpgradeBeaconController, |
||||
XAppConnectionManager, |
||||
ValidatorManager, |
||||
Outbox, |
||||
Inbox, |
||||
} from '@abacus-network/core'; |
||||
import { CoreInstance } from './CoreInstance'; |
||||
import { CoreContracts } from './CoreContracts'; |
||||
import { CoreConfig } from './types'; |
||||
import { ChainConfig, DeployEnvironment, RustConfig } from '../config'; |
||||
import { CommonDeploy, DeployType } from '../common'; |
||||
|
||||
export class CoreDeploy extends CommonDeploy<CoreInstance, CoreConfig> { |
||||
deployType = DeployType.CORE; |
||||
|
||||
deployInstance( |
||||
domain: types.Domain, |
||||
config: CoreConfig, |
||||
): Promise<CoreInstance> { |
||||
return CoreInstance.deploy(domain, this.chains, config); |
||||
} |
||||
|
||||
upgradeBeaconController(domain: types.Domain): UpgradeBeaconController { |
||||
return this.instances[domain].upgradeBeaconController; |
||||
} |
||||
|
||||
validatorManager(domain: types.Domain): ValidatorManager { |
||||
return this.instances[domain].validatorManager; |
||||
} |
||||
|
||||
outbox(domain: types.Domain): Outbox { |
||||
return this.instances[domain].outbox; |
||||
} |
||||
|
||||
inbox(local: types.Domain, remote: types.Domain): Inbox { |
||||
return this.instances[local].inbox(remote); |
||||
} |
||||
|
||||
xAppConnectionManager(domain: types.Domain): XAppConnectionManager { |
||||
return this.instances[domain].xAppConnectionManager; |
||||
} |
||||
|
||||
static readContracts( |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
directory: string, |
||||
): CoreDeploy { |
||||
return CommonDeploy.readContractsHelper( |
||||
CoreDeploy, |
||||
CoreInstance, |
||||
CoreContracts.readJson, |
||||
chains, |
||||
directory, |
||||
); |
||||
} |
||||
|
||||
writeRustConfigs(environment: DeployEnvironment, directory: string) { |
||||
for (const domain of this.domains) { |
||||
const filepath = path.join( |
||||
this.configDirectory(directory), |
||||
'rust', |
||||
`${this.name(domain)}_config.json`, |
||||
); |
||||
|
||||
const outbox = { |
||||
address: this.outbox(domain).address, |
||||
domain: domain.toString(), |
||||
name: this.name(domain), |
||||
rpcStyle: 'ethereum', |
||||
connection: { |
||||
type: 'http', |
||||
url: '', |
||||
}, |
||||
}; |
||||
|
||||
const rustConfig: RustConfig = { |
||||
environment, |
||||
signers: {}, |
||||
replicas: {}, |
||||
home: outbox, |
||||
tracing: { |
||||
level: 'debug', |
||||
fmt: 'json', |
||||
}, |
||||
db: 'db_path', |
||||
}; |
||||
|
||||
for (const remote of this.remotes(domain)) { |
||||
const inbox = { |
||||
address: this.inbox(remote, domain).address, |
||||
domain: remote.toString(), |
||||
name: this.name(remote), |
||||
rpcStyle: 'ethereum', |
||||
connection: { |
||||
type: 'http', |
||||
url: '', |
||||
}, |
||||
}; |
||||
|
||||
rustConfig.signers[this.name(remote)] = { key: '', type: 'hexKey' }; |
||||
rustConfig.replicas[this.name(remote)] = inbox; |
||||
} |
||||
this.writeJson(filepath, rustConfig); |
||||
} |
||||
} |
||||
} |
@ -1,198 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
UpgradeBeaconController, |
||||
UpgradeBeaconController__factory, |
||||
XAppConnectionManager, |
||||
XAppConnectionManager__factory, |
||||
ValidatorManager, |
||||
ValidatorManager__factory, |
||||
Outbox, |
||||
Outbox__factory, |
||||
Inbox, |
||||
Inbox__factory, |
||||
UpgradeBeaconProxy__factory, |
||||
} from '@abacus-network/core'; |
||||
import { ChainConfig } from '../config'; |
||||
import { BeaconProxy, ContractDeployer, CommonInstance } from '../common'; |
||||
import { |
||||
getContractVerificationInput, |
||||
getBeaconProxyVerificationInput, |
||||
VerificationInput, |
||||
} from '../verification'; |
||||
import { CoreConfig } from './types'; |
||||
import { CoreContracts } from './CoreContracts'; |
||||
|
||||
export class CoreInstance extends CommonInstance<CoreContracts> { |
||||
static async deploy( |
||||
domain: types.Domain, |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
config: CoreConfig, |
||||
): Promise<CoreInstance> { |
||||
const chain = chains[domain]; |
||||
const deployer = new ContractDeployer(chain); |
||||
|
||||
const upgradeBeaconController: UpgradeBeaconController = |
||||
await deployer.deploy(new UpgradeBeaconController__factory(chain.signer)); |
||||
|
||||
const validatorManager: ValidatorManager = await deployer.deploy( |
||||
new ValidatorManager__factory(chain.signer), |
||||
); |
||||
await validatorManager.enrollValidator( |
||||
domain, |
||||
config.validators[chain.name], |
||||
chain.overrides, |
||||
); |
||||
|
||||
const outbox: BeaconProxy<Outbox> = await BeaconProxy.deploy( |
||||
chain, |
||||
new Outbox__factory(chain.signer), |
||||
upgradeBeaconController.address, |
||||
[domain], |
||||
[validatorManager.address], |
||||
); |
||||
|
||||
const xAppConnectionManager: XAppConnectionManager = await deployer.deploy( |
||||
new XAppConnectionManager__factory(chain.signer), |
||||
); |
||||
await xAppConnectionManager.setOutbox(outbox.address, chain.overrides); |
||||
|
||||
const inboxes: Record<types.Domain, BeaconProxy<Inbox>> = {}; |
||||
const domains = Object.keys(chains).map((d) => parseInt(d)); |
||||
const remotes = domains.filter((d) => d !== domain); |
||||
for (let i = 0; i < remotes.length; i++) { |
||||
const remote = remotes[i]; |
||||
const initArgs = [ |
||||
remote, |
||||
validatorManager.address, |
||||
ethers.constants.HashZero, |
||||
0, |
||||
]; |
||||
if (i === 0) { |
||||
inboxes[remote] = await BeaconProxy.deploy( |
||||
chain, |
||||
new Inbox__factory(chain.signer), |
||||
upgradeBeaconController.address, |
||||
[domain], |
||||
initArgs, |
||||
); |
||||
} else { |
||||
const inbox = inboxes[remotes[0]]; |
||||
inboxes[remote] = await inbox.duplicate(chain, initArgs); |
||||
} |
||||
|
||||
await xAppConnectionManager.enrollInbox( |
||||
remote, |
||||
inboxes[remote].address, |
||||
chain.overrides, |
||||
); |
||||
await validatorManager.enrollValidator( |
||||
remote, |
||||
config.validators[chains[remote].name], |
||||
chain.overrides, |
||||
); |
||||
} |
||||
const contracts = new CoreContracts( |
||||
upgradeBeaconController, |
||||
xAppConnectionManager, |
||||
validatorManager, |
||||
outbox, |
||||
inboxes, |
||||
); |
||||
return new CoreInstance(chain, contracts); |
||||
} |
||||
async transferOwnership(owner: types.Address): Promise<void> { |
||||
const overrides = this.chain.overrides; |
||||
await this.validatorManager.transferOwnership(owner, overrides); |
||||
|
||||
await this.xAppConnectionManager.transferOwnership(owner, overrides); |
||||
|
||||
await this.upgradeBeaconController.transferOwnership(owner, overrides); |
||||
|
||||
const remotes = Object.keys(this.contracts.inboxes).map((d) => parseInt(d)); |
||||
for (const remote of remotes) { |
||||
await this.inbox(remote).transferOwnership(owner, overrides); |
||||
} |
||||
|
||||
const tx = await this.outbox.transferOwnership(owner, overrides); |
||||
await tx.wait(this.chain.confirmations); |
||||
} |
||||
|
||||
get remotes(): types.Domain[] { |
||||
return Object.keys(this.contracts.inboxes).map((d) => parseInt(d)); |
||||
} |
||||
|
||||
get upgradeBeaconController(): UpgradeBeaconController { |
||||
return this.contracts.upgradeBeaconController; |
||||
} |
||||
|
||||
get validatorManager(): ValidatorManager { |
||||
return this.contracts.validatorManager; |
||||
} |
||||
|
||||
get outbox(): Outbox { |
||||
return this.contracts.outbox.contract; |
||||
} |
||||
|
||||
inbox(domain: types.Domain): Inbox { |
||||
return this.contracts.inboxes[domain].contract; |
||||
} |
||||
|
||||
get xAppConnectionManager(): XAppConnectionManager { |
||||
return this.contracts.xAppConnectionManager; |
||||
} |
||||
get verificationInput(): VerificationInput { |
||||
let input: VerificationInput = []; |
||||
input.push( |
||||
getContractVerificationInput( |
||||
'XAppConnectionManager', |
||||
this.xAppConnectionManager, |
||||
XAppConnectionManager__factory.bytecode, |
||||
), |
||||
); |
||||
input.push( |
||||
getContractVerificationInput( |
||||
'ValidatorManager', |
||||
this.validatorManager, |
||||
ValidatorManager__factory.bytecode, |
||||
), |
||||
); |
||||
input.push( |
||||
getContractVerificationInput( |
||||
'UpgradeBeaconController', |
||||
this.upgradeBeaconController, |
||||
UpgradeBeaconController__factory.bytecode, |
||||
), |
||||
); |
||||
input = input.concat( |
||||
getBeaconProxyVerificationInput( |
||||
'Outbox', |
||||
this.contracts.outbox, |
||||
Outbox__factory.bytecode, |
||||
), |
||||
); |
||||
// All Inboxes share the same implementation and upgrade beacon.
|
||||
for (let i = 0; i < this.remotes.length; i++) { |
||||
const inbox = this.contracts.inboxes[this.remotes[i]]; |
||||
if (i == 0) { |
||||
input = input.concat( |
||||
getBeaconProxyVerificationInput( |
||||
'Inbox', |
||||
inbox, |
||||
Inbox__factory.bytecode, |
||||
), |
||||
); |
||||
} else { |
||||
input.push( |
||||
getContractVerificationInput( |
||||
'Inbox Proxy', |
||||
inbox.proxy, |
||||
UpgradeBeaconProxy__factory.bytecode, |
||||
true, |
||||
), |
||||
); |
||||
} |
||||
} |
||||
return input; |
||||
} |
||||
} |
@ -1,144 +0,0 @@ |
||||
import { expect } from 'chai'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { CoreDeploy } from './CoreDeploy'; |
||||
import { CoreConfig } from './types'; |
||||
import { |
||||
ViolationType, |
||||
ValidatorViolation, |
||||
ValidatorManagerViolation, |
||||
CommonInvariantChecker, |
||||
} from '../common'; |
||||
|
||||
export class CoreInvariantChecker extends CommonInvariantChecker< |
||||
CoreDeploy, |
||||
CoreConfig |
||||
> { |
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
this.checkContractsDefined(domain); |
||||
await this.checkOwnership(domain); |
||||
await this.checkBeaconProxies(domain); |
||||
await this.checkOutbox(domain); |
||||
await this.checkInboxes(domain); |
||||
await this.checkXAppConnectionManager(domain); |
||||
await this.checkValidatorManager(domain); |
||||
} |
||||
|
||||
checkContractsDefined(domain: types.Domain): void { |
||||
expect(this.deploy.outbox(domain)).to.not.be.undefined; |
||||
expect(this.deploy.upgradeBeaconController(domain)).to.not.be.undefined; |
||||
expect(this.deploy.xAppConnectionManager(domain)).to.not.be.undefined; |
||||
expect(this.deploy.validatorManager(domain)).to.not.be.undefined; |
||||
for (const remote of this.deploy.remotes(domain)) { |
||||
expect(this.deploy.inbox(domain, remote)).to.not.be.undefined; |
||||
} |
||||
} |
||||
|
||||
async checkOwnership(domain: types.Domain): Promise<void> { |
||||
const owners = [ |
||||
this.deploy.validatorManager(domain).owner(), |
||||
this.deploy.xAppConnectionManager(domain).owner(), |
||||
this.deploy.upgradeBeaconController(domain).owner(), |
||||
this.deploy.outbox(domain).owner(), |
||||
]; |
||||
this.deploy.remotes(domain).map((remote) => { |
||||
owners.push(this.deploy.inbox(domain, remote).owner()); |
||||
}); |
||||
const actual = await Promise.all(owners); |
||||
const expected = this.owners[domain]; |
||||
actual.map((_) => expect(_).to.equal(expected)); |
||||
} |
||||
|
||||
async checkOutbox(domain: types.Domain): Promise<void> { |
||||
const outbox = this.deploy.outbox(domain); |
||||
// validatorManager is set on Outbox
|
||||
const actualManager = await outbox.validatorManager(); |
||||
const expectedManager = this.deploy.validatorManager(domain).address; |
||||
if (actualManager !== expectedManager) { |
||||
const violation: ValidatorManagerViolation = { |
||||
domain: domain, |
||||
type: ViolationType.ValidatorManager, |
||||
actual: actualManager, |
||||
expected: expectedManager, |
||||
}; |
||||
this.addViolation(violation); |
||||
} |
||||
} |
||||
|
||||
async checkValidatorManager(domain: types.Domain): Promise<void> { |
||||
const manager = this.deploy.validatorManager(domain); |
||||
|
||||
for (const d of this.deploy.domains) { |
||||
const expected = this.config.validators[this.deploy.chains[d].name]; |
||||
const actual = await manager.validators(d); |
||||
expect(actual).to.not.be.undefined; |
||||
if (actual !== expected) { |
||||
const violation: ValidatorViolation = { |
||||
local: domain, |
||||
remote: d, |
||||
type: ViolationType.Validator, |
||||
actual, |
||||
expected, |
||||
}; |
||||
this.addViolation(violation); |
||||
} |
||||
} |
||||
} |
||||
|
||||
async checkInboxes(domain: types.Domain): Promise<void> { |
||||
const remotes = this.deploy.remotes(domain); |
||||
// Check that all inboxes on this domain are pointed to the right validator
|
||||
// manager.
|
||||
for (const remote of remotes) { |
||||
expect( |
||||
await this.deploy.inbox(domain, remote).validatorManager(), |
||||
).to.equal(this.deploy.validatorManager(domain).address); |
||||
} |
||||
if (remotes.length > 0) { |
||||
// Check that all inboxes on this domain share the same implementation and
|
||||
// UpgradeBeacon.
|
||||
const inboxes = Object.values( |
||||
this.deploy.instances[domain].contracts.inboxes, |
||||
); |
||||
const implementations = inboxes.map((r) => r.implementation.address); |
||||
const identical = (a: any, b: any) => (a === b ? a : false); |
||||
const upgradeBeacons = inboxes.map((r) => r.beacon.address); |
||||
expect(implementations.reduce(identical)).to.not.be.false; |
||||
expect(upgradeBeacons.reduce(identical)).to.not.be.false; |
||||
} |
||||
} |
||||
|
||||
async checkXAppConnectionManager(domain: types.Domain): Promise<void> { |
||||
expect(this.deploy.xAppConnectionManager(domain)).to.not.be.undefined; |
||||
for (const remote of this.deploy.remotes(domain)) { |
||||
// inbox is enrolled in xAppConnectionManager
|
||||
const enrolledInbox = await this.deploy |
||||
.xAppConnectionManager(domain) |
||||
.domainToInbox(remote); |
||||
expect(enrolledInbox).to.equal(this.deploy.inbox(domain, remote).address); |
||||
} |
||||
// Outbox is set on xAppConnectionManager
|
||||
const outbox = await this.deploy.xAppConnectionManager(domain).outbox(); |
||||
expect(outbox).to.equal(this.deploy.outbox(domain).address); |
||||
} |
||||
|
||||
async checkBeaconProxies(domain: types.Domain): Promise<void> { |
||||
// Outbox upgrade setup contracts are defined
|
||||
await this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'Outbox', |
||||
this.deploy.instances[domain].contracts.outbox, |
||||
); |
||||
|
||||
await Promise.all( |
||||
this.deploy |
||||
.remotes(domain) |
||||
.map((remote) => |
||||
this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'Inbox', |
||||
this.deploy.instances[domain].contracts.inboxes[remote], |
||||
), |
||||
), |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,131 @@ |
||||
import { expect } from 'chai'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { AbacusCore } from '@abacus-network/sdk'; |
||||
import { CoreConfig } from './types'; |
||||
import { |
||||
ViolationType, |
||||
ValidatorViolation, |
||||
ValidatorManagerViolation, |
||||
AbacusAppChecker, |
||||
} from '../check'; |
||||
|
||||
export class AbacusCoreChecker extends AbacusAppChecker< |
||||
AbacusCore, |
||||
CoreConfig |
||||
> { |
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
await this.checkOwnership(domain); |
||||
await this.checkProxiedContracts(domain); |
||||
await this.checkOutbox(domain); |
||||
await this.checkInboxes(domain); |
||||
await this.checkXAppConnectionManager(domain); |
||||
await this.checkValidatorManager(domain); |
||||
} |
||||
|
||||
async checkOwnership(domain: types.Domain): Promise<void> { |
||||
const contracts = this.app.mustGetContracts(domain); |
||||
const owners = [ |
||||
contracts.validatorManager.owner(), |
||||
contracts.xAppConnectionManager.owner(), |
||||
contracts.upgradeBeaconController.owner(), |
||||
contracts.outbox.owner(), |
||||
]; |
||||
this.app.remoteDomainNumbers(domain).map((remote) => { |
||||
owners.push(this.app.mustGetInbox(remote, domain).owner()); |
||||
}); |
||||
const actual = await Promise.all(owners); |
||||
const expected = this.owners[domain]; |
||||
actual.map((_) => expect(_).to.equal(expected)); |
||||
} |
||||
|
||||
async checkOutbox(domain: types.Domain): Promise<void> { |
||||
const contracts = this.app.mustGetContracts(domain); |
||||
const outbox = contracts.outbox; |
||||
// validatorManager is set on Outbox
|
||||
const actualManager = await outbox.validatorManager(); |
||||
const expectedManager = contracts.validatorManager.address; |
||||
if (actualManager !== expectedManager) { |
||||
const violation: ValidatorManagerViolation = { |
||||
domain: domain, |
||||
type: ViolationType.ValidatorManager, |
||||
actual: actualManager, |
||||
expected: expectedManager, |
||||
}; |
||||
this.addViolation(violation); |
||||
} |
||||
} |
||||
|
||||
async checkValidatorManager(domain: types.Domain): Promise<void> { |
||||
const manager = this.app.mustGetContracts(domain).validatorManager; |
||||
|
||||
for (const d of this.app.domainNumbers) { |
||||
const name = this.app.mustResolveDomainName(d); |
||||
const expected = this.config.validators[name]; |
||||
expect(expected).to.not.be.undefined; |
||||
const actual = await manager.validators(d); |
||||
expect(actual).to.not.be.undefined; |
||||
if (actual !== expected && expected !== undefined) { |
||||
const violation: ValidatorViolation = { |
||||
local: domain, |
||||
remote: d, |
||||
type: ViolationType.Validator, |
||||
actual, |
||||
expected, |
||||
}; |
||||
this.addViolation(violation); |
||||
} |
||||
} |
||||
} |
||||
|
||||
async checkInboxes(domain: types.Domain): Promise<void> { |
||||
// Check that all inboxes on this domain are pointed to the right validator
|
||||
// manager.
|
||||
const contracts = this.app.mustGetContracts(domain); |
||||
const validatorManager = contracts.validatorManager; |
||||
await Promise.all( |
||||
this.app.remoteDomainNumbers(domain).map(async (remote) => { |
||||
expect( |
||||
await this.app.mustGetInbox(remote, domain).validatorManager(), |
||||
).to.equal(validatorManager.address); |
||||
}), |
||||
); |
||||
|
||||
// Check that all inboxes on this domain share the same implementation and
|
||||
// UpgradeBeacon.
|
||||
const inboxes = Object.values(contracts.addresses.inboxes); |
||||
const implementations = inboxes.map((r) => r.implementation); |
||||
const identical = (a: any, b: any) => (a === b ? a : false); |
||||
const upgradeBeacons = inboxes.map((r) => r.beacon); |
||||
expect(implementations.reduce(identical)).to.not.be.false; |
||||
expect(upgradeBeacons.reduce(identical)).to.not.be.false; |
||||
} |
||||
|
||||
async checkXAppConnectionManager(domain: types.Domain): Promise<void> { |
||||
const contracts = this.app.mustGetContracts(domain); |
||||
for (const remote of this.app.remoteDomainNumbers(domain)) { |
||||
// inbox is enrolled in xAppConnectionManager
|
||||
const enrolledInbox = await contracts.xAppConnectionManager.domainToInbox( |
||||
remote, |
||||
); |
||||
expect(enrolledInbox).to.equal( |
||||
this.app.mustGetInbox(remote, domain).address, |
||||
); |
||||
} |
||||
// Outbox is set on xAppConnectionManager
|
||||
const outbox = await contracts.xAppConnectionManager.outbox(); |
||||
expect(outbox).to.equal(contracts.outbox.address); |
||||
} |
||||
|
||||
async checkProxiedContracts(domain: types.Domain): Promise<void> { |
||||
const addresses = this.app.mustGetContracts(domain).addresses; |
||||
// Outbox upgrade setup contracts are defined
|
||||
await this.checkProxiedContract(domain, 'Outbox', addresses.outbox); |
||||
|
||||
const inboxes = Object.values(addresses.inboxes); |
||||
await Promise.all( |
||||
inboxes.map((inbox) => { |
||||
return this.checkProxiedContract(domain, 'Inbox', inbox); |
||||
}), |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,203 @@ |
||||
import path from 'path'; |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
AbacusCore, |
||||
ChainName, |
||||
CoreContractAddresses, |
||||
ProxiedAddress, |
||||
} from '@abacus-network/sdk'; |
||||
import { |
||||
UpgradeBeaconController, |
||||
XAppConnectionManager, |
||||
ValidatorManager, |
||||
Inbox, |
||||
UpgradeBeaconController__factory, |
||||
XAppConnectionManager__factory, |
||||
ValidatorManager__factory, |
||||
Outbox__factory, |
||||
Inbox__factory, |
||||
} from '@abacus-network/core'; |
||||
import { DeployEnvironment, RustConfig } from '../config'; |
||||
import { AbacusAppDeployer, ProxiedContract } from '../deploy'; |
||||
import { CoreConfig } from './types'; |
||||
|
||||
export class AbacusCoreDeployer extends AbacusAppDeployer< |
||||
CoreContractAddresses, |
||||
CoreConfig |
||||
> { |
||||
async deployContracts( |
||||
domain: types.Domain, |
||||
config: CoreConfig, |
||||
): Promise<CoreContractAddresses> { |
||||
const overrides = this.getOverrides(domain); |
||||
const signer = this.mustGetSigner(domain); |
||||
const upgradeBeaconController: UpgradeBeaconController = |
||||
await this.deployContract( |
||||
domain, |
||||
'UpgradeBeaconController', |
||||
new UpgradeBeaconController__factory(signer), |
||||
); |
||||
|
||||
const validatorManager: ValidatorManager = await this.deployContract( |
||||
domain, |
||||
'ValidatorManager', |
||||
new ValidatorManager__factory(signer), |
||||
); |
||||
|
||||
for (const name of this.domainNames) { |
||||
const validator = config.validators[name]; |
||||
if (!validator) throw new Error(`No validator for ${name}`); |
||||
await validatorManager.enrollValidator( |
||||
this.resolveDomain(name), |
||||
validator, |
||||
overrides, |
||||
); |
||||
} |
||||
|
||||
const outbox = await this.deployProxiedContract( |
||||
domain, |
||||
'Outbox', |
||||
new Outbox__factory(signer), |
||||
upgradeBeaconController.address, |
||||
[domain], |
||||
[validatorManager.address], |
||||
); |
||||
|
||||
const xAppConnectionManager: XAppConnectionManager = |
||||
await this.deployContract( |
||||
domain, |
||||
'XAppConnectionManager', |
||||
new XAppConnectionManager__factory(signer), |
||||
); |
||||
await xAppConnectionManager.setOutbox(outbox.address, overrides); |
||||
|
||||
const inboxes: Record<types.Domain, ProxiedContract<Inbox>> = {}; |
||||
const inboxAddresses: Partial<Record<ChainName, ProxiedAddress>> = {}; |
||||
const remotes = this.remoteDomainNumbers(domain); |
||||
for (let i = 0; i < remotes.length; i++) { |
||||
const remote = remotes[i]; |
||||
const initArgs = [ |
||||
remote, |
||||
validatorManager.address, |
||||
ethers.constants.HashZero, |
||||
0, |
||||
]; |
||||
if (i === 0) { |
||||
inboxes[remote] = await this.deployProxiedContract( |
||||
domain, |
||||
'Inbox', |
||||
new Inbox__factory(signer), |
||||
upgradeBeaconController.address, |
||||
[domain], |
||||
initArgs, |
||||
); |
||||
} else { |
||||
inboxes[remote] = await this.duplicateProxiedContract( |
||||
domain, |
||||
'Inbox', |
||||
inboxes[remotes[0]], |
||||
initArgs, |
||||
); |
||||
} |
||||
inboxAddresses[this.mustResolveDomainName(remote)] = |
||||
inboxes[remote].addresses; |
||||
|
||||
await xAppConnectionManager.enrollInbox( |
||||
remote, |
||||
inboxes[remote].address, |
||||
overrides, |
||||
); |
||||
} |
||||
|
||||
const addresses = { |
||||
upgradeBeaconController: upgradeBeaconController.address, |
||||
xAppConnectionManager: xAppConnectionManager.address, |
||||
validatorManager: validatorManager.address, |
||||
outbox: outbox.addresses, |
||||
inboxes: inboxAddresses, |
||||
}; |
||||
return addresses; |
||||
} |
||||
|
||||
writeRustConfigs(environment: DeployEnvironment, directory: string) { |
||||
for (const domain of this.domainNumbers) { |
||||
const name = this.mustResolveDomainName(domain); |
||||
const filepath = path.join(directory, `${name}_config.json`); |
||||
const addresses = this.mustGetAddresses(domain); |
||||
|
||||
const outbox = { |
||||
address: addresses.outbox.proxy, |
||||
domain: domain.toString(), |
||||
name, |
||||
rpcStyle: 'ethereum', |
||||
connection: { |
||||
type: 'http', |
||||
url: '', |
||||
}, |
||||
}; |
||||
|
||||
const rustConfig: RustConfig = { |
||||
environment, |
||||
signers: {}, |
||||
replicas: {}, |
||||
home: outbox, |
||||
tracing: { |
||||
level: 'debug', |
||||
fmt: 'json', |
||||
}, |
||||
db: 'db_path', |
||||
}; |
||||
|
||||
for (const remote of this.remoteDomainNumbers(domain)) { |
||||
const remoteName = this.mustResolveDomainName(remote); |
||||
const inboxAddress = addresses.inboxes[remoteName]; |
||||
if (!inboxAddress) throw new Error(`No inbox for ${remoteName}`); |
||||
const inbox = { |
||||
address: inboxAddress.proxy, |
||||
domain: remote.toString(), |
||||
name: remoteName, |
||||
rpcStyle: 'ethereum', |
||||
connection: { |
||||
type: 'http', |
||||
url: '', |
||||
}, |
||||
}; |
||||
|
||||
rustConfig.signers[remoteName] = { key: '', type: 'hexKey' }; |
||||
rustConfig.replicas[remoteName] = inbox; |
||||
} |
||||
AbacusAppDeployer.writeJson(filepath, rustConfig); |
||||
} |
||||
} |
||||
|
||||
static async transferOwnership( |
||||
core: AbacusCore, |
||||
owners: Record<types.Domain, types.Address>, |
||||
) { |
||||
for (const domain of core.domainNumbers) { |
||||
const owner = owners[domain]; |
||||
if (!owner) throw new Error(`Missing owner for ${domain}`); |
||||
await AbacusCoreDeployer.transferOwnershipOfDomain(core, domain, owner); |
||||
} |
||||
} |
||||
|
||||
static async transferOwnershipOfDomain( |
||||
core: AbacusCore, |
||||
domain: types.Domain, |
||||
owner: types.Address, |
||||
): Promise<ethers.ContractReceipt> { |
||||
const contracts = core.mustGetContracts(domain); |
||||
const overrides = core.getOverrides(domain); |
||||
await contracts.validatorManager.transferOwnership(owner, overrides); |
||||
await contracts.xAppConnectionManager.transferOwnership(owner, overrides); |
||||
await contracts.upgradeBeaconController.transferOwnership(owner, overrides); |
||||
for (const chain of Object.keys( |
||||
contracts.addresses.inboxes, |
||||
) as ChainName[]) { |
||||
await contracts.inbox(chain).transferOwnership(owner, overrides); |
||||
} |
||||
const tx = await contracts.outbox.transferOwnership(owner, overrides); |
||||
return tx.wait(core.getConfirmations(domain)); |
||||
} |
||||
} |
@ -1,82 +0,0 @@ |
||||
import { Inbox__factory, Outbox__factory } from '@abacus-network/core'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { CoreDeploy } from './CoreDeploy'; |
||||
import { CoreInstance } from './CoreInstance'; |
||||
import { CoreContracts } from './CoreContracts'; |
||||
|
||||
export class ImplementationDeployer { |
||||
private deploy: CoreDeploy; |
||||
|
||||
constructor(deploy: CoreDeploy) { |
||||
this.deploy = deploy; |
||||
} |
||||
|
||||
deployOutboxImplementations(): Promise<void> { |
||||
return this._deployImplementations(this._deployOutboxImplementation); |
||||
} |
||||
|
||||
deployInboxImplementations(): Promise<void> { |
||||
return this._deployImplementations(this._deployInboxImplementation); |
||||
} |
||||
|
||||
/** |
||||
* Deploys a Outbox implementation on the chain of the given deploy and updates |
||||
* the deploy instance with the new contract. |
||||
* |
||||
* @param deploy - The deploy instance |
||||
*/ |
||||
private async _deployOutboxImplementation(domain: types.Domain) { |
||||
const signer = this.deploy.signer(domain); |
||||
const factory = new Outbox__factory(signer); |
||||
const implementation = await factory.deploy( |
||||
domain, |
||||
this.deploy.chains[domain].overrides, |
||||
); |
||||
const addresses = this.deploy.instances[domain].contracts.toObject(); |
||||
addresses.outbox.implementation = implementation.address; |
||||
const contracts = CoreContracts.fromObject(addresses, signer); |
||||
const instance = new CoreInstance(this.deploy.chains[domain], contracts); |
||||
this.deploy.instances[domain] = instance; |
||||
} |
||||
|
||||
/** |
||||
* Deploys a Inbox implementation on the chain of the given deploy and updates |
||||
* the deploy instance with the new contracts. |
||||
* |
||||
* @param deploy - The deploy instance |
||||
*/ |
||||
private async _deployInboxImplementation(domain: types.Domain) { |
||||
const signer = this.deploy.signer(domain); |
||||
const factory = new Inbox__factory(signer); |
||||
const implementation = await factory.deploy( |
||||
domain, |
||||
this.deploy.chains[domain].overrides, |
||||
); |
||||
const addresses = this.deploy.instances[domain].contracts.toObject(); |
||||
for (const remote of this.deploy.remotes(domain)) { |
||||
addresses.inboxes[remote].implementation = implementation.address; |
||||
} |
||||
const contracts = CoreContracts.fromObject(addresses, signer); |
||||
const instance = new CoreInstance(this.deploy.chains[domain], contracts); |
||||
this.deploy.instances[domain] = instance; |
||||
} |
||||
|
||||
/** |
||||
* Deploy a new contract implementation to each chain in the deploys |
||||
* array. |
||||
* |
||||
* @dev The first chain in the array will be the governing chain |
||||
* |
||||
* @param deploys - An array of chain deploys |
||||
* @param deployImplementation - A function that deploys a new implementation |
||||
*/ |
||||
private async _deployImplementations( |
||||
deployImplementation: (d: types.Domain) => void, |
||||
) { |
||||
await this.deploy.ready(); |
||||
// Do it sequentially
|
||||
for (const domain of this.deploy.domains) { |
||||
await deployImplementation(domain); |
||||
} |
||||
} |
||||
} |
@ -1,5 +1,3 @@ |
||||
export { CoreDeploy } from './CoreDeploy'; |
||||
export { CoreInstance } from './CoreInstance'; |
||||
export { CoreContracts } from './CoreContracts'; |
||||
export { CoreContractAddresses, CoreConfig } from './types'; |
||||
export { CoreInvariantChecker } from './CoreInvariantChecker'; |
||||
export { AbacusCoreDeployer } from './deploy'; |
||||
export { AbacusCoreChecker } from './check'; |
||||
export { CoreConfig } from './types'; |
||||
|
@ -1,14 +1,6 @@ |
||||
import { types } from '@abacus-network/utils'; |
||||
import { ProxiedAddress } from '../common'; |
||||
|
||||
export type CoreContractAddresses = { |
||||
upgradeBeaconController: types.Address; |
||||
xAppConnectionManager: types.Address; |
||||
validatorManager: types.Address; |
||||
outbox: ProxiedAddress; |
||||
inboxes: Record<types.Domain, ProxiedAddress>; |
||||
}; |
||||
import { ChainName } from '@abacus-network/sdk'; |
||||
|
||||
export type CoreConfig = { |
||||
validators: Record<string, types.Address>; |
||||
validators: Partial<Record<ChainName, types.Address>>; |
||||
}; |
||||
|
@ -0,0 +1,222 @@ |
||||
import path from 'path'; |
||||
import fs from 'fs'; |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
ChainName, |
||||
NameOrDomain, |
||||
MultiProvider, |
||||
ProxiedAddress, |
||||
} from '@abacus-network/sdk'; |
||||
import { |
||||
UpgradeBeacon, |
||||
UpgradeBeacon__factory, |
||||
UpgradeBeaconProxy, |
||||
UpgradeBeaconProxy__factory, |
||||
} from '@abacus-network/core'; |
||||
|
||||
import { |
||||
VerificationInput, |
||||
getContractVerificationInput, |
||||
} from './verification'; |
||||
|
||||
export class ProxiedContract<T extends ethers.Contract> { |
||||
constructor( |
||||
public readonly contract: T, |
||||
public readonly addresses: ProxiedAddress, |
||||
) {} |
||||
|
||||
get address() { |
||||
return this.contract.address; |
||||
} |
||||
} |
||||
|
||||
export abstract class AbacusAppDeployer<T, C> extends MultiProvider { |
||||
protected addresses: Map<number, T>; |
||||
protected verification: Map<number, VerificationInput>; |
||||
|
||||
constructor() { |
||||
super(); |
||||
this.addresses = new Map(); |
||||
this.verification = new Map(); |
||||
} |
||||
|
||||
getAddresses(nameOrDomain: NameOrDomain): T | undefined { |
||||
return this.getFromMap(nameOrDomain, this.addresses); |
||||
} |
||||
|
||||
mustGetAddresses(nameOrDomain: NameOrDomain): T { |
||||
return this.mustGetFromMap(nameOrDomain, this.addresses, 'Addresses'); |
||||
} |
||||
|
||||
getVerification(nameOrDomain: NameOrDomain): VerificationInput | undefined { |
||||
return this.getFromMap(nameOrDomain, this.verification); |
||||
} |
||||
|
||||
mustGetVerification(nameOrDomain: NameOrDomain): VerificationInput { |
||||
return this.mustGetFromMap(nameOrDomain, this.verification, 'Verification'); |
||||
} |
||||
|
||||
get addressesRecord(): Partial<Record<ChainName, T>> { |
||||
const addresses: Partial<Record<ChainName, T>> = {}; |
||||
this.domainNumbers.map((domain) => { |
||||
addresses[this.mustResolveDomainName(domain)] = |
||||
this.mustGetAddresses(domain); |
||||
}); |
||||
return addresses; |
||||
} |
||||
|
||||
addVerificationInput(nameOrDomain: NameOrDomain, input: VerificationInput) { |
||||
const domain = this.resolveDomain(nameOrDomain); |
||||
const verification = this.verification.get(domain) || []; |
||||
this.verification.set(domain, verification.concat(input)); |
||||
} |
||||
|
||||
abstract deployContracts(domain: types.Domain, config: C): Promise<T>; |
||||
|
||||
async deploy(config: C) { |
||||
await this.ready(); |
||||
for (const domain of this.domainNumbers) { |
||||
if (this.addresses.has(domain)) throw new Error('cannot deploy twice'); |
||||
this.addresses.set(domain, await this.deployContracts(domain, config)); |
||||
} |
||||
} |
||||
|
||||
async deployContract<L extends ethers.Contract>( |
||||
nameOrDomain: NameOrDomain, |
||||
contractName: string, |
||||
factory: ethers.ContractFactory, |
||||
...args: any[] |
||||
): Promise<L> { |
||||
const overrides = this.getOverrides(nameOrDomain); |
||||
const contract = (await factory.deploy(...args, overrides)) as L; |
||||
await contract.deployTransaction.wait(this.getConfirmations(nameOrDomain)); |
||||
this.addVerificationInput(nameOrDomain, [ |
||||
getContractVerificationInput( |
||||
contractName, |
||||
contract, |
||||
factory.bytecode, |
||||
contractName.includes(' Proxy'), |
||||
), |
||||
]); |
||||
return contract; |
||||
} |
||||
|
||||
/** |
||||
* Deploys the UpgradeBeacon, Implementation and Proxy for a given contract |
||||
* |
||||
* @param T - The contract |
||||
*/ |
||||
async deployProxiedContract<L extends ethers.Contract>( |
||||
nameOrDomain: NameOrDomain, |
||||
contractName: string, |
||||
factory: ethers.ContractFactory, |
||||
ubcAddress: types.Address, |
||||
deployArgs: any[], |
||||
initArgs: any[], |
||||
): Promise<ProxiedContract<L>> { |
||||
const signer = this.mustGetSigner(nameOrDomain); |
||||
const implementation: L = await this.deployContract( |
||||
nameOrDomain, |
||||
`${contractName} Implementation`, |
||||
factory, |
||||
...deployArgs, |
||||
); |
||||
const beacon: UpgradeBeacon = await this.deployContract( |
||||
nameOrDomain, |
||||
`${contractName} UpgradeBeacon`, |
||||
new UpgradeBeacon__factory(signer), |
||||
implementation.address, |
||||
ubcAddress, |
||||
); |
||||
|
||||
const initData = implementation.interface.encodeFunctionData( |
||||
'initialize', |
||||
initArgs, |
||||
); |
||||
const proxy: UpgradeBeaconProxy = await this.deployContract( |
||||
nameOrDomain, |
||||
`${contractName} Proxy`, |
||||
new UpgradeBeaconProxy__factory(signer), |
||||
beacon.address, |
||||
initData, |
||||
); |
||||
// proxy wait(x) implies implementation and beacon wait(>=x)
|
||||
// due to nonce ordering
|
||||
await proxy.deployTransaction.wait(this.getConfirmations(nameOrDomain)); |
||||
return new ProxiedContract(factory.attach(proxy.address) as L, { |
||||
proxy: proxy.address, |
||||
implementation: implementation.address, |
||||
beacon: beacon.address, |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Sets up a new proxy with the same beacon and implementation |
||||
* |
||||
* @param T - The contract |
||||
*/ |
||||
async duplicateProxiedContract<L extends ethers.Contract>( |
||||
nameOrDomain: NameOrDomain, |
||||
contractName: string, |
||||
contract: ProxiedContract<L>, |
||||
initArgs: any[], |
||||
): Promise<ProxiedContract<L>> { |
||||
const initData = contract.contract.interface.encodeFunctionData( |
||||
'initialize', |
||||
initArgs, |
||||
); |
||||
const proxy: UpgradeBeaconProxy = await this.deployContract( |
||||
nameOrDomain, |
||||
`${contractName} Proxy`, |
||||
new UpgradeBeaconProxy__factory(this.mustGetSigner(nameOrDomain)), |
||||
contract.addresses.beacon, |
||||
initData, |
||||
); |
||||
|
||||
return new ProxiedContract(contract.contract.attach(proxy.address) as L, { |
||||
...contract.addresses, |
||||
proxy: proxy.address, |
||||
}); |
||||
} |
||||
|
||||
async ready(): Promise<void> { |
||||
await Promise.all( |
||||
this.domainNumbers.map( |
||||
(domain) => |
||||
(this.mustGetProvider(domain) as ethers.providers.JsonRpcProvider) |
||||
.ready, |
||||
), |
||||
); |
||||
} |
||||
|
||||
writeContracts(filepath: string) { |
||||
const contents = `export const addresses = ${AbacusAppDeployer.stringify( |
||||
this.addressesRecord, |
||||
)}`;
|
||||
AbacusAppDeployer.write(filepath, contents); |
||||
} |
||||
|
||||
writeVerification(directory: string) { |
||||
for (const name of this.domainNames) { |
||||
AbacusAppDeployer.writeJson( |
||||
path.join(directory, `${name}.json`), |
||||
this.mustGetVerification(name), |
||||
); |
||||
} |
||||
} |
||||
|
||||
static stringify(obj: Object) { |
||||
return JSON.stringify(obj, null, 2); |
||||
} |
||||
|
||||
static write(filepath: string, contents: string) { |
||||
const dir = path.dirname(filepath); |
||||
fs.mkdirSync(dir, { recursive: true }); |
||||
fs.writeFileSync(filepath, contents); |
||||
} |
||||
|
||||
static writeJson(filepath: string, obj: Object) { |
||||
AbacusAppDeployer.write(filepath, AbacusAppDeployer.stringify(obj)); |
||||
} |
||||
} |
@ -1,41 +0,0 @@ |
||||
import fs from 'fs'; |
||||
import { |
||||
GovernanceRouter, |
||||
GovernanceRouter__factory, |
||||
} from '@abacus-network/apps'; |
||||
import { ethers } from 'ethers'; |
||||
import { CommonContracts, BeaconProxy } from '../common'; |
||||
import { GovernanceContractAddresses } from './types'; |
||||
|
||||
export class GovernanceContracts extends CommonContracts<GovernanceContractAddresses> { |
||||
constructor(public readonly router: BeaconProxy<GovernanceRouter>) { |
||||
super(); |
||||
} |
||||
|
||||
toObject(): GovernanceContractAddresses { |
||||
return { |
||||
router: this.router.toObject(), |
||||
}; |
||||
} |
||||
|
||||
static readJson( |
||||
filepath: string, |
||||
signer: ethers.Signer, |
||||
): GovernanceContracts { |
||||
const contents = fs.readFileSync(filepath, 'utf8'); |
||||
const addresses: GovernanceContractAddresses = JSON.parse(contents); |
||||
return GovernanceContracts.fromObject(addresses, signer); |
||||
} |
||||
|
||||
static fromObject( |
||||
addresses: GovernanceContractAddresses, |
||||
signer: ethers.Signer, |
||||
): GovernanceContracts { |
||||
const router: BeaconProxy<GovernanceRouter> = BeaconProxy.fromObject( |
||||
addresses.router, |
||||
GovernanceRouter__factory.abi, |
||||
signer, |
||||
); |
||||
return new GovernanceContracts(router); |
||||
} |
||||
} |
@ -1,53 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { GovernanceRouter } from '@abacus-network/apps'; |
||||
import { CommonDeploy, DeployType } from '../common'; |
||||
import { ChainConfig } from '../config'; |
||||
import { RouterDeploy } from '../router'; |
||||
import { GovernanceInstance } from './GovernanceInstance'; |
||||
import { GovernanceContracts } from './GovernanceContracts'; |
||||
import { GovernanceConfig } from './types'; |
||||
|
||||
export class GovernanceDeploy extends RouterDeploy< |
||||
GovernanceInstance, |
||||
GovernanceConfig |
||||
> { |
||||
deployType = DeployType.GOVERNANCE; |
||||
|
||||
async deployInstance( |
||||
domain: types.Domain, |
||||
config: GovernanceConfig, |
||||
): Promise<GovernanceInstance> { |
||||
return GovernanceInstance.deploy(domain, this.chains, config); |
||||
} |
||||
|
||||
async postDeploy(config: GovernanceConfig) { |
||||
await super.postDeploy(config); |
||||
for (const domain of this.domains) { |
||||
const addresses = config.addresses[this.name(domain)]; |
||||
if (addresses === undefined) throw new Error('could not find addresses'); |
||||
if (addresses.governor !== undefined) { |
||||
await this.router(domain).setGovernor(addresses.governor); |
||||
} else { |
||||
await this.router(domain).setGovernor(ethers.constants.AddressZero); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static readContracts( |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
directory: string, |
||||
): GovernanceDeploy { |
||||
return CommonDeploy.readContractsHelper( |
||||
GovernanceDeploy, |
||||
GovernanceInstance, |
||||
GovernanceContracts.readJson, |
||||
chains, |
||||
directory, |
||||
); |
||||
} |
||||
|
||||
router(domain: types.Domain): GovernanceRouter { |
||||
return this.instances[domain].router; |
||||
} |
||||
} |
@ -1,55 +0,0 @@ |
||||
import { |
||||
GovernanceRouter, |
||||
GovernanceRouter__factory, |
||||
} from '@abacus-network/apps'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { ChainConfig } from '../config'; |
||||
import { BeaconProxy } from '../common'; |
||||
import { RouterInstance } from '../router'; |
||||
import { |
||||
getBeaconProxyVerificationInput, |
||||
VerificationInput, |
||||
} from '../verification'; |
||||
import { GovernanceContracts } from './GovernanceContracts'; |
||||
import { GovernanceConfig } from './types'; |
||||
|
||||
export class GovernanceInstance extends RouterInstance<GovernanceContracts> { |
||||
async transferOwnership(owner: types.Address) {} |
||||
|
||||
static async deploy( |
||||
domain: types.Domain, |
||||
chains: Record<types.Domain, ChainConfig>, |
||||
config: GovernanceConfig, |
||||
): Promise<GovernanceInstance> { |
||||
const chain = chains[domain]; |
||||
const core = config.core[chain.name]; |
||||
if (core === undefined) throw new Error('could not find core'); |
||||
|
||||
const router: BeaconProxy<GovernanceRouter> = await BeaconProxy.deploy( |
||||
chain, |
||||
new GovernanceRouter__factory(chain.signer), |
||||
core.upgradeBeaconController, |
||||
[config.recoveryTimelock], |
||||
[core.xAppConnectionManager], |
||||
); |
||||
|
||||
const addresses = config.addresses[chain.name]; |
||||
if (addresses === undefined) throw new Error('could not find addresses'); |
||||
await router.contract.transferOwnership(addresses.recoveryManager); |
||||
|
||||
const contracts = new GovernanceContracts(router); |
||||
return new GovernanceInstance(chain, contracts); |
||||
} |
||||
|
||||
get router(): GovernanceRouter { |
||||
return this.contracts.router.contract; |
||||
} |
||||
|
||||
get verificationInput(): VerificationInput { |
||||
return getBeaconProxyVerificationInput( |
||||
'GovernanceRouter', |
||||
this.contracts.router, |
||||
GovernanceRouter__factory.bytecode, |
||||
); |
||||
} |
||||
} |
@ -1,47 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { expect } from 'chai'; |
||||
import { types } from '@abacus-network/utils'; |
||||
|
||||
import { GovernanceConfig } from './types'; |
||||
import { GovernanceDeploy } from './GovernanceDeploy'; |
||||
import { RouterInvariantChecker } from '../router'; |
||||
|
||||
export class GovernanceInvariantChecker extends RouterInvariantChecker< |
||||
GovernanceDeploy, |
||||
GovernanceConfig |
||||
> { |
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
await this.checkBeaconProxies(domain); |
||||
await this.checkGovernor(domain); |
||||
await this.checkRecoveryManager(domain); |
||||
await this.checkXAppConnectionManager(domain); |
||||
await this.checkEnrolledRouters(domain); |
||||
await this.checkOwnership(domain); |
||||
} |
||||
|
||||
async checkBeaconProxies(domain: types.Domain): Promise<void> { |
||||
await this.checkBeaconProxyImplementation( |
||||
domain, |
||||
'GovernanceRouter', |
||||
this.deploy.instances[domain].contracts.router, |
||||
); |
||||
} |
||||
|
||||
async checkGovernor(domain: types.Domain): Promise<void> { |
||||
const actual = await this.deploy.router(domain).governor(); |
||||
const addresses = this.config.addresses[this.deploy.name(domain)]; |
||||
if (addresses === undefined) throw new Error('could not find addresses'); |
||||
if (addresses.governor) { |
||||
expect(actual).to.equal(addresses.governor); |
||||
} else { |
||||
expect(actual).to.equal(ethers.constants.AddressZero); |
||||
} |
||||
} |
||||
|
||||
async checkRecoveryManager(domain: types.Domain): Promise<void> { |
||||
const actual = await this.deploy.router(domain).recoveryManager(); |
||||
const addresses = this.config.addresses[this.deploy.name(domain)]; |
||||
if (addresses === undefined) throw new Error('could not find addresses'); |
||||
expect(actual).to.equal(addresses.recoveryManager); |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { expect } from 'chai'; |
||||
import { GovernanceRouter } from '@abacus-network/apps'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { AbacusGovernance } from '@abacus-network/sdk'; |
||||
|
||||
import { GovernanceConfig } from './types'; |
||||
import { AbacusRouterChecker } from '../router'; |
||||
|
||||
export class AbacusGovernanceChecker extends AbacusRouterChecker< |
||||
AbacusGovernance, |
||||
GovernanceConfig |
||||
> { |
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
await super.checkDomain(domain); |
||||
await this.checkProxiedContracts(domain); |
||||
await this.checkGovernor(domain); |
||||
await this.checkRecoveryManager(domain); |
||||
} |
||||
|
||||
async checkProxiedContracts(domain: types.Domain): Promise<void> { |
||||
const addresses = this.app.mustGetContracts(domain).addresses; |
||||
// Outbox upgrade setup contracts are defined
|
||||
await this.checkProxiedContract(domain, 'GovernanceRouter', addresses); |
||||
} |
||||
|
||||
async checkGovernor(domain: types.Domain): Promise<void> { |
||||
const actual = await this.mustGetRouter(domain).governor(); |
||||
const addresses = |
||||
this.config.addresses[this.app.mustResolveDomainName(domain)]; |
||||
if (!addresses) throw new Error('could not find addresses'); |
||||
if (addresses.governor) { |
||||
expect(actual).to.equal(addresses.governor); |
||||
} else { |
||||
expect(actual).to.equal(ethers.constants.AddressZero); |
||||
} |
||||
} |
||||
|
||||
async checkRecoveryManager(domain: types.Domain): Promise<void> { |
||||
const actual = await this.mustGetRouter(domain).recoveryManager(); |
||||
const addresses = |
||||
this.config.addresses[this.app.mustResolveDomainName(domain)]; |
||||
if (!addresses) throw new Error('could not find addresses'); |
||||
expect(actual).to.equal(addresses.recoveryManager); |
||||
} |
||||
|
||||
mustGetRouter(domain: types.Domain): GovernanceRouter { |
||||
return this.app.mustGetContracts(domain).router; |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
GovernanceRouter, |
||||
GovernanceRouter__factory, |
||||
} from '@abacus-network/apps'; |
||||
import { ProxiedAddress } from '@abacus-network/sdk'; |
||||
import { AbacusRouterDeployer } from '../router'; |
||||
import { GovernanceConfig } from './types'; |
||||
|
||||
export class AbacusGovernanceDeployer extends AbacusRouterDeployer< |
||||
ProxiedAddress, |
||||
GovernanceConfig |
||||
> { |
||||
async deployContracts( |
||||
domain: types.Domain, |
||||
config: GovernanceConfig, |
||||
): Promise<ProxiedAddress> { |
||||
const signer = this.mustGetSigner(domain); |
||||
const name = this.mustResolveDomainName(domain); |
||||
const core = config.core[name]; |
||||
if (!core) throw new Error('could not find core'); |
||||
|
||||
const router = await this.deployProxiedContract( |
||||
domain, |
||||
'GovernanceRouter', |
||||
new GovernanceRouter__factory(signer), |
||||
core.upgradeBeaconController, |
||||
[config.recoveryTimelock], |
||||
[core.xAppConnectionManager], |
||||
); |
||||
|
||||
return router.addresses; |
||||
} |
||||
|
||||
async deploy(config: GovernanceConfig) { |
||||
await super.deploy(config); |
||||
|
||||
// Transfer ownership of routers to governor and recovery manager.
|
||||
for (const local of this.domainNumbers) { |
||||
const router = this.mustGetRouter(local); |
||||
const name = this.mustResolveDomainName(local); |
||||
const addresses = config.addresses[name]; |
||||
if (!addresses) throw new Error('could not find addresses'); |
||||
await router.transferOwnership(addresses.recoveryManager); |
||||
if (addresses.governor !== undefined) { |
||||
await router.setGovernor(addresses.governor); |
||||
} else { |
||||
await router.setGovernor(ethers.constants.AddressZero); |
||||
} |
||||
} |
||||
} |
||||
|
||||
mustGetRouter(domain: number): GovernanceRouter { |
||||
return GovernanceRouter__factory.connect( |
||||
this.mustGetAddresses(domain).proxy, |
||||
this.mustGetSigner(domain), |
||||
); |
||||
} |
||||
} |
@ -1,38 +0,0 @@ |
||||
import { types, utils } from '@abacus-network/utils'; |
||||
import { CommonDeploy } from '../common'; |
||||
import { ChainConfig } from '../config'; |
||||
import { RouterInstance } from './RouterInstance'; |
||||
import { Router } from './types'; |
||||
|
||||
export abstract class RouterDeploy< |
||||
T extends RouterInstance<any>, |
||||
V, |
||||
> extends CommonDeploy<T, V> { |
||||
async deploy(chains: Record<types.Domain, ChainConfig>, config: V) { |
||||
await super.deploy(chains, config); |
||||
await this.postDeploy(config); |
||||
} |
||||
|
||||
async postDeploy(_: V) { |
||||
// Make all routers aware of eachother.
|
||||
for (const local of this.domains) { |
||||
for (const remote of this.domains) { |
||||
if (local === remote) continue; |
||||
await this.router(local).enrollRemoteRouter( |
||||
remote, |
||||
utils.addressToBytes32(this.router(remote).address), |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
routerAddresses(): Record<types.Domain, types.Address> { |
||||
const addresses: Record<types.Domain, types.Address> = {}; |
||||
for (const domain of this.domains) { |
||||
addresses[domain] = this.router(domain).address; |
||||
} |
||||
return addresses; |
||||
} |
||||
|
||||
abstract router(domain: types.Domain): Router; |
||||
} |
@ -1,10 +0,0 @@ |
||||
import { types } from '@abacus-network/utils'; |
||||
import { Router } from './types'; |
||||
import { CommonContracts, CommonInstance } from '../common'; |
||||
|
||||
export abstract class RouterInstance< |
||||
T extends CommonContracts<any>, |
||||
> extends CommonInstance<T> { |
||||
abstract transferOwnership(owner: types.Address): Promise<void>; |
||||
abstract router: Router; |
||||
} |
@ -1,37 +0,0 @@ |
||||
import { expect } from 'chai'; |
||||
import { utils, types } from '@abacus-network/utils'; |
||||
import { CommonInvariantChecker } from '../common'; |
||||
import { RouterInstance } from './RouterInstance'; |
||||
import { RouterDeploy } from './RouterDeploy'; |
||||
import { RouterConfig } from './types'; |
||||
|
||||
export abstract class RouterInvariantChecker< |
||||
T extends RouterDeploy<RouterInstance<any>, any>, |
||||
V extends RouterConfig, |
||||
> extends CommonInvariantChecker<T, V> { |
||||
async checkEnrolledRouters(domain: types.Domain): Promise<void> { |
||||
const router = this.deploy.router(domain); |
||||
await Promise.all( |
||||
this.deploy.remotes(domain).map(async (remote) => { |
||||
const remoteRouter = await this.deploy.router(remote); |
||||
expect(await router.routers(remote)).to.equal( |
||||
utils.addressToBytes32(remoteRouter.address), |
||||
); |
||||
}), |
||||
); |
||||
} |
||||
|
||||
async checkOwnership(domain: types.Domain): Promise<void> { |
||||
const actual = await this.deploy.router(domain).owner(); |
||||
const expected = this.owners[domain]; |
||||
expect(actual).to.equal(expected); |
||||
} |
||||
|
||||
async checkXAppConnectionManager(domain: types.Domain): Promise<void> { |
||||
const actual = await this.deploy.router(domain).xAppConnectionManager(); |
||||
const core = this.config.core[this.deploy.name(domain)]; |
||||
if (core === undefined) throw new Error('could not find core'); |
||||
const expected = core.xAppConnectionManager; |
||||
expect(actual).to.equal(expected); |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
import { expect } from 'chai'; |
||||
import { utils, types } from '@abacus-network/utils'; |
||||
import { AbacusApp } from '@abacus-network/sdk'; |
||||
import { AbacusAppChecker } from '../check'; |
||||
import { Router, RouterConfig } from './types'; |
||||
|
||||
export abstract class AbacusRouterChecker< |
||||
A extends AbacusApp<any, any>, |
||||
C extends RouterConfig, |
||||
> extends AbacusAppChecker<A, C> { |
||||
abstract mustGetRouter(domain: types.Domain): Router; |
||||
|
||||
async checkDomain(domain: types.Domain): Promise<void> { |
||||
await this.checkEnrolledRouters(domain); |
||||
await this.checkOwnership(domain); |
||||
await this.checkXAppConnectionManager(domain); |
||||
} |
||||
|
||||
async checkEnrolledRouters(domain: types.Domain): Promise<void> { |
||||
const router = this.mustGetRouter(domain); |
||||
await Promise.all( |
||||
this.app.remoteDomainNumbers(domain).map(async (remote) => { |
||||
const remoteRouter = await this.mustGetRouter(remote); |
||||
expect(await router.routers(remote)).to.equal( |
||||
utils.addressToBytes32(remoteRouter.address), |
||||
); |
||||
}), |
||||
); |
||||
} |
||||
|
||||
async checkOwnership(domain: types.Domain): Promise<void> { |
||||
const actual = await this.mustGetRouter(domain).owner(); |
||||
const expected = this.owners[domain]; |
||||
expect(actual).to.equal(expected); |
||||
} |
||||
|
||||
async checkXAppConnectionManager(domain: types.Domain): Promise<void> { |
||||
const actual = await this.mustGetRouter(domain).xAppConnectionManager(); |
||||
const core = this.config.core[this.app.mustResolveDomainName(domain)]; |
||||
if (!core) throw new Error('could not find core'); |
||||
const expected = core.xAppConnectionManager; |
||||
expect(actual).to.equal(expected); |
||||
} |
||||
} |
@ -0,0 +1,34 @@ |
||||
import { types, utils } from '@abacus-network/utils'; |
||||
import { AbacusAppDeployer } from '../deploy'; |
||||
import { Router } from './types'; |
||||
|
||||
export abstract class AbacusRouterDeployer<T, C> extends AbacusAppDeployer< |
||||
T, |
||||
C |
||||
> { |
||||
async deploy(config: C) { |
||||
await super.deploy(config); |
||||
|
||||
// Make all routers aware of eachother.
|
||||
for (const local of this.domainNumbers) { |
||||
const router = this.mustGetRouter(local); |
||||
for (const remote of this.remoteDomainNumbers(local)) { |
||||
const remoteRouter = this.mustGetRouter(remote); |
||||
await router.enrollRemoteRouter( |
||||
remote, |
||||
utils.addressToBytes32(remoteRouter.address), |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
get routerAddresses(): Record<types.Domain, types.Address> { |
||||
const addresses: Record<types.Domain, types.Address> = {}; |
||||
for (const domain of this.domainNumbers) { |
||||
addresses[domain] = this.mustGetRouter(domain).address; |
||||
} |
||||
return addresses; |
||||
} |
||||
|
||||
abstract mustGetRouter(domain: types.Domain): Router; |
||||
} |
@ -1,4 +1,3 @@ |
||||
export { RouterDeploy } from './RouterDeploy'; |
||||
export { RouterInstance } from './RouterInstance'; |
||||
export { RouterInvariantChecker } from './RouterInvariantChecker'; |
||||
export { AbacusRouterDeployer } from './deploy'; |
||||
export { AbacusRouterChecker } from './check'; |
||||
export { RouterAddresses, RouterConfig } from './types'; |
||||
|
@ -1,49 +0,0 @@ |
||||
import { types } from '@abacus-network/utils'; |
||||
import { BridgeDeploy } from './bridge/BridgeDeploy'; |
||||
import { CoreDeploy } from './core/CoreDeploy'; |
||||
import { GovernanceDeploy } from './governance/GovernanceDeploy'; |
||||
import { writeFileSync } from 'fs'; |
||||
import { resolve } from 'path'; |
||||
|
||||
export function updateSdkDomain( |
||||
environment: string, |
||||
coreDeploy: CoreDeploy, |
||||
governanceDeploy: GovernanceDeploy, |
||||
bridgeDeploy: BridgeDeploy, |
||||
) { |
||||
let ret = "import { AbacusDomain } from './domain';\n"; |
||||
coreDeploy.domains.forEach((domain: types.Domain, i: number) => { |
||||
ret += ` |
||||
export const ${coreDeploy.chains[domain].name}: AbacusDomain = { |
||||
name: '${coreDeploy.chains[domain].name}', |
||||
id: ${domain}, |
||||
bridgeRouter: '${bridgeDeploy.router(domain).address}',${ |
||||
!!bridgeDeploy.helper(domain) |
||||
? `\n ethHelper: '${bridgeDeploy.helper(domain)!.address}',` |
||||
: '' |
||||
} |
||||
outbox: '${coreDeploy.outbox(domain).address}', |
||||
governanceRouter: '${governanceDeploy.router(domain).address}', |
||||
xAppConnectionManager: '${coreDeploy.xAppConnectionManager(domain).address}', |
||||
inboxes: [ |
||||
${coreDeploy |
||||
.remotes(domain) |
||||
.map( |
||||
(remote) => |
||||
` { domain: ${remote}, address: '${ |
||||
coreDeploy.inbox(domain, remote).address |
||||
}' },`,
|
||||
) |
||||
.join('\n')} |
||||
], |
||||
};\n`;
|
||||
}); |
||||
|
||||
ret += `\nexport const ${environment}Domains = [${coreDeploy.domains |
||||
.map((_) => coreDeploy.chains[_].name) |
||||
.join(', ')}];`;
|
||||
writeFileSync( |
||||
resolve(__dirname, `../../abacus-sdk/src/abacus/domains/${environment}.ts`), |
||||
ret, |
||||
); |
||||
} |
@ -1,10 +1,3 @@ |
||||
export { ContractVerifier } from './ContractVerifier'; |
||||
export { |
||||
getContractVerificationInput, |
||||
getBeaconProxyVerificationInput, |
||||
} from './utils'; |
||||
export { |
||||
BeaconProxyPrefix, |
||||
ContractVerificationInput, |
||||
VerificationInput, |
||||
} from './types'; |
||||
export { getContractVerificationInput } from './utils'; |
||||
export { ContractVerificationInput, VerificationInput } from './types'; |
||||
|
@ -1,61 +1,63 @@ |
||||
import path from 'path'; |
||||
import '@nomiclabs/hardhat-waffle'; |
||||
import { ethers } from 'hardhat'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { ChainConfig } from '../src/config'; |
||||
import { CoreDeploy } from '../src/core'; |
||||
import { AbacusBridge } from '@abacus-network/sdk'; |
||||
import { AbacusCoreDeployer } from '../src/core'; |
||||
import { |
||||
BridgeDeploy, |
||||
BridgeInvariantChecker, |
||||
AbacusBridgeDeployer, |
||||
AbacusBridgeChecker, |
||||
BridgeConfig, |
||||
} from '../src/bridge'; |
||||
import { |
||||
getTestChains, |
||||
testCore as coreConfig, |
||||
testBridge, |
||||
outputDir, |
||||
} from './inputs'; |
||||
core as coreConfig, |
||||
registerMultiProviderTest, |
||||
bridge as partialBridgeConfig, |
||||
} from '../config/environments/test'; |
||||
|
||||
describe('bridge', async () => { |
||||
const core = new CoreDeploy(); |
||||
let chains: Record<types.Domain, ChainConfig>; |
||||
let bridge = new BridgeDeploy(); |
||||
let bridgeConfig: BridgeConfig; |
||||
const coreDeployer = new AbacusCoreDeployer(); |
||||
const bridgeDeployer = new AbacusBridgeDeployer(); |
||||
const owners: Record<types.Domain, types.Address> = {}; |
||||
let bridge: AbacusBridge; |
||||
let bridgeConfig: BridgeConfig; |
||||
|
||||
before(async () => { |
||||
const [signer, owner] = await ethers.getSigners(); |
||||
chains = getTestChains(signer); |
||||
await core.deploy(chains, coreConfig); |
||||
bridgeConfig = { ...testBridge, core: {} }; |
||||
core.domains.map((domain) => { |
||||
registerMultiProviderTest(bridgeDeployer, signer); |
||||
registerMultiProviderTest(coreDeployer, signer); |
||||
await coreDeployer.deploy(coreConfig); |
||||
|
||||
bridgeConfig = { ...partialBridgeConfig, core: {} }; |
||||
coreDeployer.domainNumbers.map((domain) => { |
||||
owners[domain] = owner.address; |
||||
bridgeConfig.core[chains[domain].name] = { |
||||
upgradeBeaconController: core.upgradeBeaconController(domain).address, |
||||
xAppConnectionManager: core.xAppConnectionManager(domain).address, |
||||
const coreAddresses = coreDeployer.mustGetAddresses(domain); |
||||
bridgeConfig.core[coreDeployer.mustResolveDomainName(domain)] = { |
||||
upgradeBeaconController: coreAddresses.upgradeBeaconController, |
||||
xAppConnectionManager: coreAddresses.xAppConnectionManager, |
||||
}; |
||||
}); |
||||
}); |
||||
|
||||
it('deploys', async () => { |
||||
await bridge.deploy(chains, bridgeConfig); |
||||
}); |
||||
|
||||
it('transfers ownership', async () => { |
||||
await bridge.transferOwnership(owners); |
||||
await bridgeDeployer.deploy(bridgeConfig); |
||||
}); |
||||
|
||||
it('checks', async () => { |
||||
const checker = new BridgeInvariantChecker(bridge, bridgeConfig, owners); |
||||
await checker.check(); |
||||
it('writes', async () => { |
||||
const base = './test/outputs/bridge'; |
||||
bridgeDeployer.writeVerification(path.join(base, 'verification')); |
||||
bridgeDeployer.writeContracts(path.join(base, 'contracts.ts')); |
||||
}); |
||||
|
||||
it('writes', async () => { |
||||
bridge.writeOutput(outputDir); |
||||
it('transfers ownership', async () => { |
||||
bridge = new AbacusBridge(bridgeDeployer.addressesRecord); |
||||
const [signer] = await ethers.getSigners(); |
||||
registerMultiProviderTest(bridge, signer); |
||||
await AbacusBridgeDeployer.transferOwnership(bridge, owners); |
||||
}); |
||||
|
||||
it('reads', async () => { |
||||
bridge = BridgeDeploy.readContracts(chains, outputDir); |
||||
const checker = new BridgeInvariantChecker(bridge, bridgeConfig, owners); |
||||
it('checks', async () => { |
||||
const checker = new AbacusBridgeChecker(bridge, bridgeConfig, owners); |
||||
await checker.check(); |
||||
}); |
||||
}); |
||||
|
@ -1,44 +1,47 @@ |
||||
import path from 'path'; |
||||
import '@nomiclabs/hardhat-waffle'; |
||||
import { ethers } from 'hardhat'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { DeployEnvironment, ChainConfig } from '../src/config'; |
||||
import { CoreDeploy, CoreInvariantChecker } from '../src/core'; |
||||
import { getTestChains, outputDir, testCore as coreConfig } from './inputs'; |
||||
import { AbacusCore } from '@abacus-network/sdk'; |
||||
import { AbacusCoreDeployer, AbacusCoreChecker } from '../src/core'; |
||||
import { |
||||
core as coreConfig, |
||||
registerMultiProviderTest, |
||||
} from '../config/environments/test'; |
||||
|
||||
describe('core', async () => { |
||||
let core = new CoreDeploy(); |
||||
let chains: Record<types.Domain, ChainConfig>; |
||||
const owners: Record<types.Domain, types.Address> = {}; |
||||
const deployer = new AbacusCoreDeployer(); |
||||
let core: AbacusCore; |
||||
|
||||
const owners: Record<types.Domain, types.Address> = {}; |
||||
before(async () => { |
||||
const [signer, owner] = await ethers.getSigners(); |
||||
chains = getTestChains(signer); |
||||
Object.keys(chains).map((d) => { |
||||
owners[parseInt(d)] = owner.address; |
||||
registerMultiProviderTest(deployer, signer); |
||||
deployer.domainNumbers.map((d) => { |
||||
owners[d] = owner.address; |
||||
}); |
||||
}); |
||||
|
||||
it('deploys', async () => { |
||||
await core.deploy(chains, coreConfig); |
||||
}); |
||||
|
||||
it('transfers ownership', async () => { |
||||
await core.transferOwnership(owners); |
||||
await deployer.deploy(coreConfig); |
||||
}); |
||||
|
||||
it('checks', async () => { |
||||
const checker = new CoreInvariantChecker(core, coreConfig, owners); |
||||
await checker.check(); |
||||
it('writes', async () => { |
||||
const base = './test/outputs/core'; |
||||
deployer.writeVerification(path.join(base, 'verification')); |
||||
deployer.writeContracts(path.join(base, 'contracts.ts')); |
||||
deployer.writeRustConfigs('test', path.join(base, 'rust')); |
||||
}); |
||||
|
||||
it('writes', async () => { |
||||
core.writeOutput(outputDir); |
||||
core.writeRustConfigs(DeployEnvironment.test, outputDir); |
||||
it('transfers ownership', async () => { |
||||
core = new AbacusCore(deployer.addressesRecord); |
||||
const [signer] = await ethers.getSigners(); |
||||
registerMultiProviderTest(core, signer); |
||||
await AbacusCoreDeployer.transferOwnership(core, owners); |
||||
}); |
||||
|
||||
it('reads', async () => { |
||||
core = CoreDeploy.readContracts(chains, outputDir); |
||||
const checker = new CoreInvariantChecker(core, coreConfig, owners); |
||||
it('checks', async () => { |
||||
const checker = new AbacusCoreChecker(core, coreConfig, owners); |
||||
await checker.check(); |
||||
}); |
||||
}); |
||||
|
@ -1,72 +0,0 @@ |
||||
import { ethers } from 'ethers'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import { |
||||
ChainConfigWithoutSigner, |
||||
ChainName, |
||||
ChainConfig, |
||||
} from '../src/config'; |
||||
import { CoreConfig } from '../src/core'; |
||||
import { GovernanceConfigWithoutCore } from '../src/governance'; |
||||
import { BridgeConfigWithoutCore } from '../src/bridge'; |
||||
|
||||
export const outputDir = './test/outputs'; |
||||
|
||||
const testCelo: ChainConfigWithoutSigner = { |
||||
name: ChainName.CELO, |
||||
domain: 1000, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
const testEthereum: ChainConfigWithoutSigner = { |
||||
name: ChainName.ETHEREUM, |
||||
domain: 2000, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
const testPolygon: ChainConfigWithoutSigner = { |
||||
name: ChainName.POLYGON, |
||||
domain: 3000, |
||||
overrides: {}, |
||||
}; |
||||
|
||||
export const testCore: CoreConfig = { |
||||
validators: { |
||||
celo: '0x91631845fab02614e53e5F5A68dFBB0E2f1a9B6d', |
||||
polygon: '0x91631845fab02614e53e5F5A68dFBB0E2f1a9B6d', |
||||
ethereum: '0x91631845fab02614e53e5F5A68dFBB0E2f1a9B6d', |
||||
}, |
||||
}; |
||||
|
||||
export const testGovernance: GovernanceConfigWithoutCore = { |
||||
recoveryTimelock: 180, |
||||
addresses: { |
||||
celo: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
governor: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
polygon: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
ethereum: { |
||||
recoveryManager: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e', |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
export const testBridge: BridgeConfigWithoutCore = { |
||||
weth: { |
||||
// Deployment calls weth.approve()
|
||||
// celo: '0x4FbBB2b0820CF0cF027BbB58DC7F7f760BC0c57e',
|
||||
}, |
||||
}; |
||||
|
||||
export function getTestChains( |
||||
signer: ethers.Signer, |
||||
): Record<types.Domain, ChainConfig> { |
||||
const testChains: Record<types.Domain, ChainConfig> = {}; |
||||
const chains = [testCelo, testEthereum, testPolygon]; |
||||
chains.map((chain) => { |
||||
testChains[chain.domain] = { ...chain, signer }; |
||||
}); |
||||
return testChains; |
||||
} |
@ -1 +1,9 @@ |
||||
export { local } from './local'; |
||||
import { addresses as test } from './test'; |
||||
import { ChainName } from '../../'; |
||||
import { BridgeContractAddresses } from '../'; |
||||
export const addresses: Record< |
||||
any, |
||||
Partial<Record<ChainName, BridgeContractAddresses>> |
||||
> = { |
||||
test, |
||||
}; |
||||
|
@ -1,41 +0,0 @@ |
||||
import { BridgeContractAddresses } from '../contracts'; |
||||
import { ChainName } from '../../types'; |
||||
|
||||
export const local: Partial<Record<ChainName, BridgeContractAddresses>> = { |
||||
celo: { |
||||
router: { |
||||
proxy: '0xCD8a1C3ba11CF5ECfa6267617243239504a98d90', |
||||
implementation: '0x5f3f1dBD7B74C6B46e8c44f98792A1dAf8d69154', |
||||
beacon: '0xb7278A61aa25c888815aFC32Ad3cC52fF24fE575', |
||||
}, |
||||
token: { |
||||
proxy: '0x1291Be112d480055DaFd8a610b7d1e203891C274', |
||||
implementation: '0x809d550fca64d94Bd9F66E60752A544199cfAC3D', |
||||
beacon: '0x4c5859f0F772848b2D91F1D83E2Fe57935348029', |
||||
}, |
||||
}, |
||||
ethereum: { |
||||
router: { |
||||
proxy: '0xFD471836031dc5108809D173A067e8486B9047A3', |
||||
implementation: '0x7bc06c482DEAd17c0e297aFbC32f6e63d3846650', |
||||
beacon: '0xc351628EB244ec633d5f21fBD6621e1a683B1181', |
||||
}, |
||||
token: { |
||||
proxy: '0x7969c5eD335650692Bc04293B07F5BF2e7A673C0', |
||||
implementation: '0x82e01223d51Eb87e16A03E24687EDF0F294da6f1', |
||||
beacon: '0x2bdCC0de6bE1f7D2ee689a0342D76F52E8EFABa3', |
||||
}, |
||||
}, |
||||
polygon: { |
||||
router: { |
||||
proxy: '0x5081a39b8A5f0E35a8D959395a630b68B74Dd30f', |
||||
implementation: '0x162A433068F51e18b7d13932F27e66a3f99E6890', |
||||
beacon: '0x922D6956C99E12DFeB3224DEA977D0939758A1Fe', |
||||
}, |
||||
token: { |
||||
proxy: '0xB0D4afd8879eD9F52b28595d31B441D079B2Ca07', |
||||
implementation: '0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc', |
||||
beacon: '0x1429859428C0aBc9C2C47C8Ee9FBaf82cFA0F20f', |
||||
}, |
||||
}, |
||||
}; |
@ -0,0 +1,50 @@ |
||||
export const addresses = { |
||||
alfajores: { |
||||
router: { |
||||
proxy: '0x4EE6eCAD1c2Dae9f525404De8555724e3c35d07B', |
||||
implementation: '0xf4B146FbA71F41E0592668ffbF264F1D186b2Ca8', |
||||
beacon: '0x172076E0166D1F9Cc711C77Adf8488051744980C', |
||||
}, |
||||
token: { |
||||
proxy: '0x202CCe504e04bEd6fC0521238dDf04Bc9E8E15aB', |
||||
implementation: '0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7', |
||||
beacon: '0x0355B7B8cb128fA5692729Ab3AAa199C1753f726', |
||||
}, |
||||
}, |
||||
kovan: { |
||||
router: { |
||||
proxy: '0xC9a43158891282A2B1475592D5719c001986Aaec', |
||||
implementation: '0xfbC22278A96299D91d41C453234d97b4F5Eb9B2d', |
||||
beacon: '0x46b142DD1E924FAb83eCc3c08e4D46E82f005e0E', |
||||
}, |
||||
token: { |
||||
proxy: '0x2B0d36FACD61B71CC05ab8F3D2355ec3631C0dd5', |
||||
implementation: '0xBEc49fA140aCaA83533fB00A2BB19bDdd0290f25', |
||||
beacon: '0xD84379CEae14AA33C123Af12424A37803F885889', |
||||
}, |
||||
}, |
||||
mumbai: { |
||||
router: { |
||||
proxy: '0x4631BCAbD6dF18D94796344963cB60d44a4136b6', |
||||
implementation: '0x7A9Ec1d04904907De0ED7b6839CcdD59c3716AC9', |
||||
beacon: '0x49fd2BE640DB2910c2fAb69bB8531Ab6E76127ff', |
||||
}, |
||||
token: { |
||||
proxy: '0x4C2F7092C2aE51D986bEFEe378e50BD4dB99C901', |
||||
implementation: '0x1c85638e118b37167e9298c2268758e058DdfDA0', |
||||
beacon: '0x367761085BF3C12e5DA2Df99AC6E1a824612b8fb', |
||||
}, |
||||
}, |
||||
fuji: { |
||||
router: { |
||||
proxy: '0x720472c8ce72c2A2D711333e064ABD3E6BbEAdd3', |
||||
implementation: '0xAA292E8611aDF267e563f334Ee42320aC96D0463', |
||||
beacon: '0x5c74c94173F05dA1720953407cbb920F3DF9f887', |
||||
}, |
||||
token: { |
||||
proxy: '0xf953b3A269d80e3eB0F2947630Da976B896A8C5b', |
||||
implementation: '0x86A2EE8FAf9A840F7a2c64CA3d51209F9A02081D', |
||||
beacon: '0xA4899D35897033b927acFCf422bc745916139776', |
||||
}, |
||||
}, |
||||
}; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue