Use MultiProvider and the SDK in abacus-deploy (#277)

pull/295/head
Asa Oines 3 years ago committed by GitHub
parent f6168cbe47
commit d40b1e4dc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 34
      typescript/contract-metrics/src/registerContext.ts
  2. 2
      typescript/deploy/.gitignore
  3. 7
      typescript/deploy/config/environments/test/agent.ts
  4. 8
      typescript/deploy/config/environments/test/bridge.ts
  5. 0
      typescript/deploy/config/environments/test/chains.ts
  6. 0
      typescript/deploy/config/environments/test/core.ts
  7. 37
      typescript/deploy/config/environments/test/domains.ts
  8. 20
      typescript/deploy/config/environments/test/governance.ts
  9. 9
      typescript/deploy/config/environments/test/index.ts
  10. 26
      typescript/deploy/config/networks/mainnets.ts
  11. 50
      typescript/deploy/config/networks/testnets.ts
  12. 54
      typescript/deploy/hardhat.config.ts
  13. 4
      typescript/deploy/package.json
  14. 22
      typescript/deploy/scripts/bridge.ts
  15. 50
      typescript/deploy/scripts/check-deploy.ts
  16. 22
      typescript/deploy/scripts/core.ts
  17. 9
      typescript/deploy/scripts/create-keys.ts
  18. 9
      typescript/deploy/scripts/delete-keys.ts
  19. 10
      typescript/deploy/scripts/deploy-agents.ts
  20. 6
      typescript/deploy/scripts/deploy-contract-metrics.ts
  21. 11
      typescript/deploy/scripts/deploy-inbox.ts
  22. 6
      typescript/deploy/scripts/deploy-keymaster.ts
  23. 39
      typescript/deploy/scripts/governance.ts
  24. 11
      typescript/deploy/scripts/output-agent-env-vars.ts
  25. 3
      typescript/deploy/scripts/rotate-key.ts
  26. 50
      typescript/deploy/scripts/set-validator.ts
  27. 17
      typescript/deploy/scripts/update-abacus-sdk.ts
  28. 10
      typescript/deploy/scripts/update-agents-diff.ts
  29. 13
      typescript/deploy/scripts/update-agents.ts
  30. 3
      typescript/deploy/scripts/update-key.ts
  31. 61
      typescript/deploy/scripts/upgrade-inbox.ts
  32. 6
      typescript/deploy/scripts/upgrade-keymaster.ts
  33. 246
      typescript/deploy/scripts/utils.ts
  34. 116
      typescript/deploy/src/agents/index.ts
  35. 59
      typescript/deploy/src/bridge/BridgeContracts.ts
  36. 44
      typescript/deploy/src/bridge/BridgeDeploy.ts
  37. 106
      typescript/deploy/src/bridge/BridgeInstance.ts
  38. 39
      typescript/deploy/src/bridge/BridgeInvariantChecker.ts
  39. 37
      typescript/deploy/src/bridge/check.ts
  40. 95
      typescript/deploy/src/bridge/deploy.ts
  41. 12
      typescript/deploy/src/bridge/index.ts
  42. 9
      typescript/deploy/src/bridge/types.ts
  43. 52
      typescript/deploy/src/check.ts
  44. 122
      typescript/deploy/src/common/BeaconProxy.ts
  45. 22
      typescript/deploy/src/common/CommonContracts.ts
  46. 166
      typescript/deploy/src/common/CommonDeploy.ts
  47. 14
      typescript/deploy/src/common/CommonInstance.ts
  48. 20
      typescript/deploy/src/common/ContractDeployer.ts
  49. 14
      typescript/deploy/src/common/index.ts
  50. 5
      typescript/deploy/src/common/types.ts
  51. 31
      typescript/deploy/src/config/addresses.ts
  52. 3
      typescript/deploy/src/config/agent.ts
  53. 81
      typescript/deploy/src/config/chain.ts
  54. 9
      typescript/deploy/src/config/core.ts
  55. 7
      typescript/deploy/src/config/environment.ts
  56. 4
      typescript/deploy/src/config/index.ts
  57. 11
      typescript/deploy/src/contract-metrics.ts
  58. 96
      typescript/deploy/src/core/CoreContracts.ts
  59. 108
      typescript/deploy/src/core/CoreDeploy.ts
  60. 198
      typescript/deploy/src/core/CoreInstance.ts
  61. 144
      typescript/deploy/src/core/CoreInvariantChecker.ts
  62. 131
      typescript/deploy/src/core/check.ts
  63. 203
      typescript/deploy/src/core/deploy.ts
  64. 23
      typescript/deploy/src/core/govern.ts
  65. 82
      typescript/deploy/src/core/implementation.ts
  66. 8
      typescript/deploy/src/core/index.ts
  67. 12
      typescript/deploy/src/core/types.ts
  68. 222
      typescript/deploy/src/deploy.ts
  69. 41
      typescript/deploy/src/governance/GovernanceContracts.ts
  70. 53
      typescript/deploy/src/governance/GovernanceDeploy.ts
  71. 55
      typescript/deploy/src/governance/GovernanceInstance.ts
  72. 47
      typescript/deploy/src/governance/GovernanceInvariantChecker.ts
  73. 50
      typescript/deploy/src/governance/check.ts
  74. 60
      typescript/deploy/src/governance/deploy.ts
  75. 7
      typescript/deploy/src/governance/index.ts
  76. 7
      typescript/deploy/src/governance/types.ts
  77. 38
      typescript/deploy/src/router/RouterDeploy.ts
  78. 10
      typescript/deploy/src/router/RouterInstance.ts
  79. 37
      typescript/deploy/src/router/RouterInvariantChecker.ts
  80. 44
      typescript/deploy/src/router/check.ts
  81. 34
      typescript/deploy/src/router/deploy.ts
  82. 5
      typescript/deploy/src/router/index.ts
  83. 2
      typescript/deploy/src/router/types.ts
  84. 49
      typescript/deploy/src/sdk.ts
  85. 26
      typescript/deploy/src/verification/ContractVerifier.ts
  86. 11
      typescript/deploy/src/verification/index.ts
  87. 22
      typescript/deploy/src/verification/types.ts
  88. 47
      typescript/deploy/src/verification/utils.ts
  89. 68
      typescript/deploy/test/bridge.test.ts
  90. 49
      typescript/deploy/test/core.test.ts
  91. 70
      typescript/deploy/test/governance.test.ts
  92. 72
      typescript/deploy/test/inputs.ts
  93. 9
      typescript/sdk/src/app.ts
  94. 3
      typescript/sdk/src/bridge/app.ts
  95. 8
      typescript/sdk/src/bridge/contracts.ts
  96. 10
      typescript/sdk/src/bridge/environments/index.ts
  97. 41
      typescript/sdk/src/bridge/environments/local.ts
  98. 50
      typescript/sdk/src/bridge/environments/test.ts
  99. 3
      typescript/sdk/src/bridge/index.ts
  100. 8
      typescript/sdk/src/contracts.ts
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,9 +3,9 @@ import {
AbacusBridge,
AbacusGovernance,
ChainName,
localCore,
localBridge,
localGovernance,
bridgeAddresses,
coreAddresses,
governanceAddresses,
} from '@abacus-network/sdk';
import config from './config';
@ -14,30 +14,22 @@ type Rpc = {
chain: ChainName;
rpc: string;
};
const rpcs: Rpc[] = [
{ chain: 'celo', rpc: config.celoRpc },
{ chain: 'ethereum', rpc: config.ethereumRpc },
{ chain: 'polygon', rpc: config.polygonRpc },
];
rpcs.map((rpc) => {
localCore.registerRpcProvider(rpc.chain, rpc.rpc);
localBridge.registerRpcProvider(rpc.chain, rpc.rpc);
localGovernance.registerRpcProvider(rpc.chain, rpc.rpc);
});
let core: AbacusCore;
let bridge: AbacusBridge;
let governance: AbacusGovernance;
switch (config.environment) {
case 'local':
core = localCore;
bridge = localBridge;
governance = localGovernance;
break;
const environment = config.environment;
const core = new AbacusCore(coreAddresses[environment]);
const bridge = new AbacusBridge(bridgeAddresses[environment]);
const governance = new AbacusGovernance(governanceAddresses[environment]);
default:
throw new Error('Unrecognized environment');
break;
}
rpcs.map((rpc) => {
core.registerRpcProvider(rpc.chain, rpc.rpc);
bridge.registerRpcProvider(rpc.chain, rpc.rpc);
governance.registerRpcProvider(rpc.chain, rpc.rpc);
});
export { core, bridge, governance };

@ -4,4 +4,4 @@ dist/
.env
cache/
test/outputs
config/environments/local/core/
config/environments/test/core/

@ -1,7 +1,7 @@
import { AgentConfig, DeployEnvironment } from '../../../src/config';
import { AgentConfig } from '../../../src/config';
export const agentConfig: AgentConfig = {
environment: DeployEnvironment.local,
export const agent: AgentConfig = {
environment: 'local',
namespace: 'abacus-local',
runEnv: 'local',
docker: {
@ -10,6 +10,7 @@ export const agentConfig: AgentConfig = {
},
validator: {
interval: 5,
confirmations: 1,
},
relayer: {
interval: 5,

@ -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,15 +1,12 @@
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 celo: ChainConfigWithoutSigner = {
name: ChainName.CELO,
domain: 0x63656c6f, // b'celo' interpreted as an int
export const celo: TransactionConfig = {
overrides: {},
};
export const ethereum: ChainConfigWithoutSigner = {
name: ChainName.ETHEREUM,
domain: 0x657468, // b'eth' interpreted as an int
export const ethereum: TransactionConfig = {
overrides: {
// This isn't actually used because Ethereum supports EIP 1559 - but just in case
gasPrice: '400000000000', // 400 gwei
@ -19,9 +16,7 @@ export const ethereum: ChainConfigWithoutSigner = {
},
};
export const avalanche: ChainConfigWithoutSigner = {
name: ChainName.AVALANCHE,
domain: 0x61766178, // b'avax' interpreted as an int
export const avalanche: TransactionConfig = {
overrides: {
// This isn't actually used because Avalanche supports EIP 1559 - but just in case
gasPrice: BigNumber.from(50_000_000_000), // 50 nAVAX (50 gwei)
@ -31,10 +26,15 @@ export const avalanche: ChainConfigWithoutSigner = {
},
};
export const polygon: ChainConfigWithoutSigner = {
name: ChainName.POLYGON,
domain: 0x706f6c79, // b'poly' interpreted as an int
export const polygon: TransactionConfig = {
overrides: {
gasPrice: '5000000000', // 50 gwei
},
};
export const configs: Partial<Record<ChainName, TransactionConfig>> = {
celo,
ethereum,
avalanche,
polygon,
};

@ -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,27 +1,30 @@
import '@nomiclabs/hardhat-waffle';
import '@nomiclabs/hardhat-etherscan';
import { task } from 'hardhat/config';
import { types, utils } from '@abacus-network/utils';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { BadRandomRecipient__factory } from '@abacus-network/core';
import { coreAddresses, AbacusCore } from '@abacus-network/sdk';
import { utils, types } from '@abacus-network/utils';
import { sleep } from './src/utils/utils';
import {
getCoreVerificationDirectory,
getCoreContractsSdkFilepath,
getCoreRustDirectory,
registerMultiProvider,
getCoreConfig,
getCoreDeploy,
getEnvironmentDirectory,
getChainConfigsRecord,
} from './scripts/utils';
import { CoreDeploy } from './src/core';
import { AbacusCoreDeployer } from './src/core';
import { ContractVerifier } from './src/verification';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
const domainSummary = async (deploy: CoreDeploy, domain: types.Domain) => {
const outbox = deploy.outbox(domain);
const domainSummary = async (core: AbacusCore, domain: types.Domain) => {
const contracts = core.mustGetContracts(domain);
const outbox = contracts.outbox;
const [outboxCheckpointRoot, outboxCheckpointIndex] =
await outbox.latestCheckpoint();
const count = (await outbox.tree()).toNumber();
const summary: any = {
domain,
domain: core.mustResolveDomainName(domain),
outbox: {
count,
checkpoint: {
@ -32,19 +35,21 @@ const domainSummary = async (deploy: CoreDeploy, domain: types.Domain) => {
};
const inboxSummary = async (remote: types.Domain) => {
const inbox = deploy.inbox(remote, domain);
const inbox = core.mustGetInbox(domain, remote);
const [inboxCheckpointRoot, inboxCheckpointIndex] =
await inbox.latestCheckpoint();
const processFilter = inbox.filters.Process();
const processes = await inbox.queryFilter(processFilter);
return {
domain: remote,
domain: core.mustResolveDomainName(remote),
processed: processes.length,
root: inboxCheckpointRoot,
index: inboxCheckpointIndex.toNumber(),
};
};
summary.inboxes = await Promise.all(deploy.remotes(domain).map(inboxSummary));
summary.inboxes = await Promise.all(
core.remoteDomainNumbers(domain).map(inboxSummary),
);
return summary;
};
@ -55,15 +60,15 @@ task('abacus', 'Deploys abacus on top of an already running Harthat Network')
)
.setAction(async (args: any) => {
const environment = args.environment;
// Deploy core
const chains = await getChainConfigsRecord(environment);
const config = await getCoreConfig(environment);
const deploy = new CoreDeploy();
await deploy.deploy(chains, config);
const deployer = new AbacusCoreDeployer();
await registerMultiProvider(deployer, environment);
const coreConfig = await getCoreConfig(environment);
await deployer.deploy(coreConfig);
// Write configs
deploy.writeOutput(getEnvironmentDirectory(environment));
deploy.writeRustConfigs(environment, getEnvironmentDirectory(environment));
deployer.writeVerification(getCoreVerificationDirectory(environment));
deployer.writeRustConfigs(environment, getCoreRustDirectory(environment));
deployer.writeContracts(getCoreContractsSdkFilepath(environment));
});
task('kathy', 'Dispatches random abacus messages')
@ -73,7 +78,8 @@ task('kathy', 'Dispatches random abacus messages')
)
.setAction(async (args: any, hre: HardhatRuntimeEnvironment) => {
const environment = args.environment;
const deploy = await getCoreDeploy(environment);
const core = new AbacusCore(coreAddresses[environment]);
await registerMultiProvider(core, environment);
const randomElement = (list: types.Domain[]) =>
list[Math.floor(Math.random() * list.length)];
@ -85,9 +91,9 @@ task('kathy', 'Dispatches random abacus messages')
// Generate artificial traffic
while (true) {
const local = deploy.domains[0];
const remote = randomElement(deploy.remotes(local));
const outbox = deploy.outbox(local);
const local = core.domainNumbers[0];
const remote = randomElement(core.remoteDomainNumbers(local));
const outbox = core.mustGetContracts(local).outbox;
// Send a batch of messages to the remote domain to test
// the checkpointer/relayer submitting only greedily
for (let i = 0; i < 10; i++) {
@ -101,7 +107,7 @@ task('kathy', 'Dispatches random abacus messages')
(await outbox.count()).toNumber() - 1
}`,
);
console.log(await domainSummary(deploy, local));
console.log(await domainSummary(core, local));
await sleep(5000);
}
}

@ -24,8 +24,8 @@
"test": "hardhat test",
"check": "tsc --noEmit",
"node": "hardhat node",
"abacus": "hardhat abacus --environment local",
"kathy": "hardhat kathy --environment local --network localhost",
"abacus": "hardhat abacus --environment test",
"kathy": "hardhat kathy --environment test --network localhost",
"prettier": "prettier --write *.ts ./src ./config ./scripts ./test"
},
"license": "MIT OR Apache-2.0",

@ -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,32 +1,45 @@
import { ethers } from 'ethers';
import { types } from '@abacus-network/utils';
import {
AbacusCore,
AbacusBridge,
AbacusGovernance,
coreAddresses,
bridgeAddresses,
governanceAddresses,
} from '@abacus-network/sdk';
import {
getBridgeDeploy,
getBridgeConfig,
getCoreDeploy,
getCoreConfig,
getEnvironment,
getGovernanceDeploy,
getGovernanceConfig,
registerMultiProvider,
} from './utils';
import { CoreInvariantChecker } from '../src/core';
import { BridgeInvariantChecker } from '../src/bridge';
import { GovernanceInvariantChecker } from '../src/governance';
import { AbacusCoreChecker } from '../src/core';
import { AbacusBridgeChecker } from '../src/bridge';
import { AbacusGovernanceChecker } from '../src/governance';
async function check() {
const environment = await getEnvironment();
const governance = await getGovernanceDeploy(environment);
const governanceConfig = await getGovernanceConfig(environment);
const core = new AbacusCore(coreAddresses[environment]);
const bridge = new AbacusBridge(bridgeAddresses[environment]);
const governance = new AbacusGovernance(governanceAddresses[environment]);
registerMultiProvider(core, environment);
registerMultiProvider(bridge, environment);
registerMultiProvider(governance, environment);
const governanceConfig = await getGovernanceConfig(environment, core);
const governors: Record<types.Domain, types.Address> = {};
governance.domains.map((domain) => {
const addresses = governanceConfig.addresses[governance.name(domain)];
if (addresses === undefined) throw new Error('could not find addresses');
governance.domainNumbers.map((domain) => {
const addresses =
governanceConfig.addresses[governance.mustResolveDomainName(domain)];
if (!addresses) throw new Error('could not find addresses');
governors[domain] = addresses.governor
? addresses.governor
: ethers.constants.AddressZero;
});
const governanceChecker = new GovernanceInvariantChecker(
const governanceChecker = new AbacusGovernanceChecker(
governance,
governanceConfig,
governors,
@ -34,24 +47,23 @@ async function check() {
await governanceChecker.check();
governanceChecker.expectEmpty();
const core = await getCoreDeploy(environment);
const coreConfig = await getCoreConfig(environment);
const coreChecker = new CoreInvariantChecker(
const coreChecker = new AbacusCoreChecker(
core,
coreConfig,
governance.routerAddresses(),
governance.routerAddresses,
);
await coreChecker.check();
coreChecker.expectEmpty();
const bridge = await getBridgeDeploy(environment);
const bridgeConfig = await getBridgeConfig(environment);
const bridgeChecker = new BridgeInvariantChecker(
const bridgeConfig = await getBridgeConfig(environment, core);
const bridgeChecker = new AbacusBridgeChecker(
bridge,
bridgeConfig,
governance.routerAddresses(),
governance.routerAddresses,
);
await bridgeChecker.check();
bridgeChecker.expectEmpty();
}
check().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,18 +1,18 @@
import { getAgentConfig, getEnvironment, getChainConfigs } from './utils';
import { getAgentConfig, getDomainNames, getEnvironment } from './utils';
import { runAgentHelmCommand } from '../src/agents';
import { HelmCommand } from '../src/utils/helm';
async function deploy() {
const environment = await getEnvironment();
const chains = await getChainConfigs(environment);
const agentConfig = await getAgentConfig(environment);
const domainNames = await getDomainNames(environment);
await Promise.all(
chains.map((chain) => {
domainNames.map((name) => {
return runAgentHelmCommand(
HelmCommand.Upgrade,
agentConfig,
chain,
chains,
name,
domainNames,
);
}),
);

@ -2,18 +2,18 @@ import { runContractMetricsHelmCommand } from '../src/contract-metrics';
import { HelmCommand } from '../src/utils/helm';
import {
getContractMetricsConfig,
getChainConfigs,
getDomainNames,
getEnvironment,
} from './utils';
async function main() {
const environment = await getEnvironment();
const contractMetricsConfig = await getContractMetricsConfig(environment);
const chains = await getChainConfigs(environment);
const domainNames = await getDomainNames(environment);
return runContractMetricsHelmCommand(
HelmCommand.Install,
contractMetricsConfig,
chains,
domainNames,
environment,
);
}

@ -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);

@ -4,7 +4,7 @@ import {
getKeyRoleAndChainArgs,
getAgentConfig,
getEnvironment,
getChainConfigs,
getDomainNames,
} from './utils';
async function main() {
@ -17,8 +17,13 @@ async function main() {
const environment = await getEnvironment();
const agentConfig = await getAgentConfig(environment);
const chains = await getChainConfigs(environment);
const envVars = await getAgentEnvVars(argv.c, argv.r, agentConfig, chains);
const domainNames = await getDomainNames(environment);
const envVars = await getAgentEnvVars(
argv.c,
argv.r,
agentConfig,
domainNames,
);
await writeFile(argv.f, envVars.join('\n'));
}

@ -3,7 +3,6 @@ import {
getAgentConfig,
getEnvironment,
} from './utils';
import { DeployEnvironment } from '../src/config';
async function rotateKey() {
const args = await getKeyRoleAndChainArgs();
@ -14,7 +13,7 @@ async function rotateKey() {
switch (environment) {
// TODO: re-implement this when the environments actually get readded
case DeployEnvironment.local: {
case 'local': {
console.log("I don't do anything");
console.log(argv, agentConfig);
}

@ -1,45 +1,47 @@
import {
getCoreDeploy,
getCoreConfig,
getChainConfigs,
getGovernance,
getEnvironment,
getGovernanceDeploy,
registerRpcProviders,
registerGovernorSigner,
} from './utils';
import { ViolationType } from '../src/common';
import { CoreInvariantChecker } from '../src/core';
AbacusCore,
AbacusGovernance,
coreAddresses,
governanceAddresses,
} from '@abacus-network/sdk';
import { getCoreConfig, getEnvironment, registerMultiProvider } from './utils';
import { ViolationType } from '../src/check';
import { AbacusCoreChecker } from '../src/core';
import { expectCalls, GovernanceCallBatchBuilder } from '../src/core/govern';
async function main() {
const environment = await getEnvironment();
const abacusGovernance = await getGovernance(environment);
const chains = await getChainConfigs(environment);
registerRpcProviders(abacusGovernance, chains);
await registerGovernorSigner(abacusGovernance, chains);
const core = new AbacusCore(coreAddresses[environment]);
const governance = new AbacusGovernance(governanceAddresses[environment]);
registerMultiProvider(core, environment);
registerMultiProvider(governance, environment);
const deploy = await getCoreDeploy(environment);
const governance = await getGovernanceDeploy(environment);
const config = await getCoreConfig(environment);
const checker = new CoreInvariantChecker(
deploy,
const checker = new AbacusCoreChecker(
core,
config,
governance.routerAddresses(),
governance.routerAddresses,
);
await checker.check();
checker.expectViolations([ViolationType.Validator], [chains.length]);
checker.expectViolations(
[ViolationType.Validator],
[core.domainNumbers.length],
);
const builder = new GovernanceCallBatchBuilder(
deploy,
abacusGovernance,
core,
governance,
checker.violations,
);
const batch = await builder.build();
await batch.build();
// For each domain, expect one call to set the updater.
expectCalls(batch, deploy.domains, new Array(chains.length).fill(1));
expectCalls(
batch,
core.domainNumbers,
new Array(core.domainNumbers.length).fill(1),
);
// Change to `batch.execute` in order to run.
const receipts = await batch.estimateGas();
console.log(receipts);

@ -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,17 +1,17 @@
import { runAgentHelmCommand } from '../src/agents';
import { HelmCommand } from '../src/utils/helm';
import { getAgentConfig, getEnvironment, getChainConfigs } from './utils';
import { getAgentConfig, getEnvironment, getDomainNames } from './utils';
async function deploy() {
const environment = await getEnvironment();
const agentConfig = await getAgentConfig(environment);
const chains = await getChainConfigs(environment);
for (const chain of chains) {
const domainNames = await getDomainNames(environment);
for (const name of domainNames) {
await runAgentHelmCommand(
HelmCommand.UpgradeDiff,
agentConfig,
chain,
chains,
name,
domainNames,
);
}
}

@ -1,13 +1,18 @@
import { runAgentHelmCommand } from '../src/agents';
import { HelmCommand } from '../src/utils/helm';
import { getAgentConfig, getEnvironment, getChainConfigs } from './utils';
import { getAgentConfig, getEnvironment, getDomainNames } from './utils';
async function deploy() {
const environment = await getEnvironment();
const agentConfig = await getAgentConfig(environment);
const chains = await getChainConfigs(environment);
for (const chain of chains) {
await runAgentHelmCommand(HelmCommand.Upgrade, agentConfig, chain, chains);
const domainNames = await getDomainNames(environment);
for (const name of domainNames) {
await runAgentHelmCommand(
HelmCommand.Upgrade,
agentConfig,
name,
domainNames,
);
}
}

@ -3,7 +3,6 @@ import {
getAgentConfig,
getEnvironment,
} from './utils';
import { DeployEnvironment } from '../src/config';
async function rotateKey() {
const args = await getKeyRoleAndChainArgs();
@ -14,7 +13,7 @@ async function rotateKey() {
switch (environment) {
// TODO: Reimplement this when the environments get readded
case DeployEnvironment.local: {
case 'local': {
console.log("I don't do anything");
console.log(argv, agentConfig);
}

@ -1,51 +1,46 @@
import {
getCoreDeploy,
getCoreConfig,
getChainConfigs,
getCore,
getGovernance,
getEnvironment,
getGovernanceDeploy,
registerRpcProviders,
registerGovernorSigner,
} from './utils';
import { ViolationType } from '../src/common';
import { CoreInvariantChecker } from '../src/core';
AbacusCore,
AbacusGovernance,
coreAddresses,
governanceAddresses,
Call,
} from '@abacus-network/sdk';
import { getCoreConfig, getEnvironment, registerMultiProvider } from './utils';
import { ViolationType } from '../src/check';
import { AbacusCoreChecker } from '../src/core';
import { expectCalls, GovernanceCallBatchBuilder } from '../src/core/govern';
import { Call } from '@abacus-network/sdk';
async function main() {
const environment = await getEnvironment();
const chains = await getChainConfigs(environment);
const abacusCore = await getCore(environment);
const abacusGovernance = await getGovernance(environment);
registerRpcProviders(abacusCore, chains);
registerRpcProviders(abacusGovernance, chains);
await registerGovernorSigner(abacusGovernance, chains);
const core = new AbacusCore(coreAddresses[environment]);
const governance = new AbacusGovernance(governanceAddresses[environment]);
registerMultiProvider(core, environment);
registerMultiProvider(governance, environment);
const deploy = await getCoreDeploy(environment);
const governance = await getGovernanceDeploy(environment);
const config = await getCoreConfig(environment);
const checker = new CoreInvariantChecker(
deploy,
const checker = new AbacusCoreChecker(
core,
config,
governance.routerAddresses(),
governance.routerAddresses,
);
await checker.check();
checker.expectViolations([ViolationType.UpgradeBeacon], [chains.length]);
checker.expectViolations(
[ViolationType.UpgradeBeacon],
[core.domainNumbers.length],
);
const builder = new GovernanceCallBatchBuilder(
deploy,
abacusGovernance,
core,
governance,
checker.violations,
);
const batch = await builder.build();
for (const local of deploy.domains) {
for (const remote of deploy.remotes(local)) {
const inbox = abacusCore.mustGetInbox(local, remote);
for (const local of core.domainNumbers) {
for (const remote of core.remoteDomainNumbers(local)) {
const inbox = core.mustGetInbox(local, remote);
const transferOwnership =
await inbox.populateTransaction.transferOwnership(
abacusGovernance.mustGetContracts(remote).router.address,
governance.mustGetContracts(remote).router.address,
);
batch.push(remote, transferOwnership as Call);
}
@ -56,8 +51,8 @@ async function main() {
// calls to transfer inbox ownership.
expectCalls(
batch,
deploy.domains,
new Array(chains.length).fill(chains.length),
core.domainNumbers,
new Array(core.domainNumbers.length).fill(core.domainNumbers.length),
);
// Change to `batch.execute` in order to run.
const receipts = await batch.estimateGas();

@ -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,35 +1,29 @@
import { ethers } from 'ethers';
import path from 'path';
import yargs from 'yargs';
import {
ALL_CHAIN_NAMES,
AbacusCore,
AbacusGovernance,
ChainName,
MultiProvider,
} from '@abacus-network/sdk';
import { types } from '@abacus-network/utils';
import { KEY_ROLE_ENUM } from '../src/agents';
import {
ALL_ENVIRONMENTS,
AgentConfig,
ChainName,
ChainConfig,
DeployEnvironment,
InfrastructureConfig,
ContractMetricsConfig,
} from '../src/config';
import { CoreDeploy, CoreContracts, CoreConfig } from '../src/core';
import { BridgeDeploy, BridgeContracts, BridgeConfig } from '../src/bridge';
import {
GovernanceDeploy,
GovernanceContracts,
GovernanceConfig,
} from '../src/governance';
import { CoreConfig } from '../src/core';
import { BridgeConfig } from '../src/bridge';
import { GovernanceConfig } from '../src/governance';
import { RouterConfig, RouterAddresses } from '../src/router';
export function getArgs() {
return yargs(process.argv.slice(2))
.alias('e', 'env')
.describe('e', 'deploy environment')
.choices('e', Object.values(DeployEnvironment))
.choices('e', ALL_ENVIRONMENTS)
.require('e')
.help('h')
.alias('h', 'help');
@ -40,86 +34,102 @@ async function importModule(moduleName: string): Promise<any> {
return importedModule;
}
export async function getChainConfigs(
function moduleName(environment: DeployEnvironment) {
return `../config/environments/${environment}`;
}
export async function registerMultiProvider(
multiProvider: MultiProvider,
environment: DeployEnvironment,
): Promise<ChainConfig[]> {
const moduleName = `../config/environments/${environment}/chains`;
return (await importModule(moduleName)).getChains();
): Promise<void> {
return (await importModule(moduleName(environment))).registerMultiProvider(
multiProvider,
);
}
export async function getChainConfigsRecord(
export async function getDomainNames(
environment: DeployEnvironment,
): Promise<Record<types.Domain, ChainConfig>> {
const array = await getChainConfigs(environment);
const f = (chain: ChainConfig) => chain;
return recordFromArray(array, f);
): Promise<ChainName[]> {
return (await importModule(moduleName(environment))).domains;
}
export async function getCoreConfig(
environment: DeployEnvironment,
): Promise<CoreConfig> {
const moduleName = `../config/environments/${environment}/core`;
return (await importModule(moduleName)).core;
return (await importModule(moduleName(environment))).core;
}
export async function getRouterConfig(
environment: DeployEnvironment,
core: AbacusCore,
): Promise<RouterConfig> {
const chains = await getChainConfigs(environment);
const contracts = await getCoreContracts(environment, chains);
const addresses: Record<string, RouterAddresses> = {};
for (const chain of chains) {
addresses[chain.name] = {
upgradeBeaconController:
contracts[chain.domain].upgradeBeaconController.address,
xAppConnectionManager:
contracts[chain.domain].xAppConnectionManager.address,
core.domainNames.map((name) => {
const contracts = core.mustGetContracts(name);
addresses[name] = {
upgradeBeaconController: contracts.upgradeBeaconController.address,
xAppConnectionManager: contracts.xAppConnectionManager.address,
};
}
});
return { core: addresses };
}
export async function getBridgeConfig(
environment: DeployEnvironment,
core: AbacusCore,
): Promise<BridgeConfig> {
const moduleName = `../config/environments/${environment}/bridge`;
const partial = (await importModule(moduleName)).bridge;
return { ...partial, core: await getRouterConfig(environment) };
const partial = (await importModule(moduleName(environment))).bridge;
return { ...partial, core: await getRouterConfig(environment, core) };
}
export async function getGovernanceConfig(
environment: DeployEnvironment,
core: AbacusCore,
): Promise<GovernanceConfig> {
const moduleName = `../config/environments/${environment}/governance`;
const partial = (await importModule(moduleName)).governance;
return { ...partial, core: await getRouterConfig(environment) };
const partial = (await importModule(moduleName(environment))).governance;
return { ...partial, core: await getRouterConfig(environment, core) };
}
export async function getInfrastructureConfig(
environment: DeployEnvironment,
): Promise<InfrastructureConfig> {
const moduleName = `../config/environments/${environment}/infrastructure`;
return (await importModule(moduleName)).infrastructure;
return (await importModule(moduleName(environment))).infrastructure;
}
export async function getAgentConfig(
environment: DeployEnvironment,
): Promise<AgentConfig> {
const moduleName = `../config/environments/${environment}/agent`;
return (await importModule(moduleName)).agentConfig;
return (await importModule(moduleName(environment))).agent;
}
export async function getContractMetricsConfig(
environment: DeployEnvironment,
): Promise<ContractMetricsConfig> {
const moduleName = `../config/environments/${environment}/contract-metrics`;
return (await importModule(moduleName)).contractMetrics;
return (await importModule(moduleName(environment))).metrics;
}
export async function getEnvironment(): Promise<DeployEnvironment> {
return (await getArgs().argv).e;
}
function getContractsSdkFilepath(mod: string, environment: DeployEnvironment) {
return path.join('../sdk/src/', mod, 'environments', `${environment}.ts`);
}
export function getCoreContractsSdkFilepath(environment: DeployEnvironment) {
return getContractsSdkFilepath('core', environment);
}
export function getBridgeContractsSdkFilepath(environment: DeployEnvironment) {
return getContractsSdkFilepath('bridge', environment);
}
export function getGovernanceContractsSdkFilepath(
environment: DeployEnvironment,
) {
return getContractsSdkFilepath('governance', environment);
}
export function getEnvironmentDirectory(environment: DeployEnvironment) {
return path.join('./config/environments/', environment);
}
@ -128,10 +138,6 @@ export function getCoreDirectory(environment: DeployEnvironment) {
return path.join(getEnvironmentDirectory(environment), 'core');
}
export function getCoreContractsDirectory(environment: DeployEnvironment) {
return path.join(getCoreDirectory(environment), 'contracts');
}
export function getCoreVerificationDirectory(environment: DeployEnvironment) {
return path.join(getCoreDirectory(environment), 'verification');
}
@ -144,10 +150,6 @@ export function getBridgeDirectory(environment: DeployEnvironment) {
return path.join(getEnvironmentDirectory(environment), 'bridge');
}
export function getBridgeContractsDirectory(environment: DeployEnvironment) {
return path.join(getBridgeDirectory(environment), 'contracts');
}
export function getBridgeVerificationDirectory(environment: DeployEnvironment) {
return path.join(getBridgeDirectory(environment), 'verification');
}
@ -156,148 +158,12 @@ export function getGovernanceDirectory(environment: DeployEnvironment) {
return path.join(getEnvironmentDirectory(environment), 'governance');
}
export function getGovernanceContractsDirectory(
environment: DeployEnvironment,
) {
return path.join(getGovernanceDirectory(environment), 'contracts');
}
export function getGovernanceVerificationDirectory(
environment: DeployEnvironment,
) {
return path.join(getGovernanceDirectory(environment), 'verification');
}
function recordFromArray<T>(
chains: ChainConfig[],
f: (chain: ChainConfig) => T,
): Record<types.Domain, T> {
const ret: Record<types.Domain, T> = {};
for (const chain of chains) {
ret[chain.domain] = f(chain);
}
return ret;
}
export function getCoreContracts(
environment: DeployEnvironment,
chains: ChainConfig[],
) {
const directory = getCoreContractsDirectory(environment);
const f = (chain: ChainConfig): CoreContracts => {
return CoreContracts.readJson(
path.join(directory, `${chain.name}.json`),
chain.signer,
);
};
return recordFromArray(chains, f);
}
export function getBridgeContracts(
environment: DeployEnvironment,
chains: ChainConfig[],
) {
const directory = getBridgeContractsDirectory(environment);
const contracts: Record<types.Domain, BridgeContracts> = {};
for (const chain of chains) {
contracts[chain.domain] = BridgeContracts.readJson(
path.join(directory, `${chain.name}.json`),
chain.signer,
);
}
return contracts;
}
export function getGovernanceContracts(
environment: DeployEnvironment,
chains: ChainConfig[],
) {
const directory = getGovernanceContractsDirectory(environment);
const contracts: Record<types.Domain, GovernanceContracts> = {};
for (const chain of chains) {
contracts[chain.domain] = GovernanceContracts.readJson(
path.join(directory, `${chain.name}.json`),
chain.signer,
);
}
return contracts;
}
export async function getCoreDeploy(
environment: DeployEnvironment,
): Promise<CoreDeploy> {
const chains = await getChainConfigsRecord(environment);
return CoreDeploy.readContracts(chains, getEnvironmentDirectory(environment));
}
export async function getBridgeDeploy(
environment: DeployEnvironment,
): Promise<BridgeDeploy> {
const chains = await getChainConfigsRecord(environment);
return BridgeDeploy.readContracts(
chains,
getEnvironmentDirectory(environment),
);
}
export async function getGovernanceDeploy(
environment: DeployEnvironment,
): Promise<GovernanceDeploy> {
const chains = await getChainConfigsRecord(environment);
return GovernanceDeploy.readContracts(
chains,
getEnvironmentDirectory(environment),
);
}
export function getCore(environment: DeployEnvironment): AbacusCore {
switch (environment) {
default: {
throw new Error('invalid environment');
break;
}
}
}
export function getGovernance(
environment: DeployEnvironment,
): AbacusGovernance {
switch (environment) {
default: {
throw new Error('invalid environment');
break;
}
}
}
export function registerRpcProviders(
multiProvider: MultiProvider,
chains: ChainConfig[],
): void {
chains.map((c) =>
multiProvider.registerRpcProvider(
c.name,
process.env[`${c.name.toUpperCase()}_RPC`]!,
),
);
}
export async function registerGovernorSigner(
governance: AbacusGovernance,
chains: ChainConfig[],
): Promise<void> {
const governor = await governance.governor();
const govChains = chains.filter((c) => c.domain === governor.domain);
if (govChains.length !== 1) throw new Error('could not find governor chain');
const govChain = govChains[0];
governance.registerSigner(
govChain.name,
new ethers.Wallet(
process.env[`${govChain.name.toUpperCase()}_DEPLOYER_KEY`]!,
),
);
}
export async function getKeyRoleAndChainArgs() {
const args = await getArgs();
return args
@ -307,6 +173,6 @@ export async function getKeyRoleAndChainArgs() {
.require('r')
.alias('c', 'chain')
.describe('c', 'chain name')
.choices('c', Object.values(ChainName))
.choices('c', ALL_CHAIN_NAMES)
.require('c');
}

@ -1,6 +1,7 @@
import { ethers } from 'ethers';
import { rm, writeFile } from 'fs/promises';
import { AgentConfig, ChainConfig, ChainName } from '../config';
import { ChainName } from '@abacus-network/sdk';
import { AgentConfig } from '../config';
import { fetchGCPSecret } from '../utils/gcloud';
import { HelmCommand, helmifyValues } from '../utils/helm';
import { ensure0x, execCmd, include, strip0x } from '../utils/utils';
@ -26,7 +27,7 @@ export const KEY_ROLES = [
async function helmValuesForChain(
chainName: ChainName,
agentConfig: AgentConfig,
chains: ChainConfig[],
chainNames: ChainName[],
) {
// Credentials are only needed if AWS keys are needed -- otherwise, the
// key is pulled from GCP Secret Manager by the helm chart
@ -38,8 +39,6 @@ async function helmValuesForChain(
return undefined;
};
const chain = chains.find((_) => _.name === chainName)!;
return {
image: {
repository: agentConfig.docker.repo,
@ -52,11 +51,11 @@ async function helmValuesForChain(
name: chainName,
},
aws: !!agentConfig.aws,
replicaChains: chains
.filter((_) => _.name !== chainName)
.map((remoteChain) => {
replicaChains: chainNames
.filter((name) => name !== chainName)
.map((remoteChainName) => {
return {
name: remoteChain.name,
name: remoteChainName,
};
}),
validator: {
@ -64,7 +63,7 @@ async function helmValuesForChain(
attestationSigner: {
...credentials(KEY_ROLE_ENUM.Validator),
},
reorg_period: chain.confirmations,
reorg_period: agentConfig.validator?.confirmations,
...include(!!agentConfig.validator?.interval, {
pollingInterval: agentConfig.validator?.interval || '',
}),
@ -74,8 +73,8 @@ async function helmValuesForChain(
},
relayer: {
enabled: true,
transactionSigners: chains.map((chain) => ({
name: chain.name,
transactionSigners: chainNames.map((name) => ({
name,
...credentials(KEY_ROLE_ENUM.Relayer),
})),
...include(!!agentConfig.validator?.interval, {
@ -84,8 +83,8 @@ async function helmValuesForChain(
},
checkpointer: {
enabled: true,
transactionSigners: chains.map((chain) => ({
name: chain.name,
transactionSigners: chainNames.map((name) => ({
name,
...credentials(KEY_ROLE_ENUM.Checkpointer),
})),
indexonly: agentConfig.processor?.indexOnly || [],
@ -100,15 +99,15 @@ export async function getAgentEnvVars(
homeChainName: ChainName,
role: KEY_ROLE_ENUM,
agentConfig: AgentConfig,
chains: ChainConfig[],
chainNames: ChainName[],
) {
const valueDict = await helmValuesForChain(
homeChainName,
agentConfig,
chains,
chainNames,
);
const envVars: string[] = [];
const rpcEndpoints = await getSecretRpcEndpoints(agentConfig, chains);
const rpcEndpoints = await getSecretRpcEndpoints(agentConfig, chainNames);
envVars.push(`OPT_BASE_HOME_CONNECTION_URL=${rpcEndpoints[homeChainName]}`);
valueDict.optics.replicaChains.forEach((replicaChain: any) => {
envVars.push(
@ -135,9 +134,9 @@ export async function getAgentEnvVars(
// Only checkpointer and relayer need to sign txs
if (role === KEY_ROLE_ENUM.Checkpointer || role === KEY_ROLE_ENUM.Relayer) {
chains.forEach((network) => {
chainNames.forEach((name) => {
envVars.push(
`OPT_BASE_SIGNERS_${network.name.toUpperCase()}_KEY=${strip0x(
`OPT_BASE_SIGNERS_${name.toUpperCase()}_KEY=${strip0x(
gcpKeys[role].privateKey,
)}`,
);
@ -183,16 +182,16 @@ export async function getAgentEnvVars(
// Only checkpointer and relayer need to sign txs
if (role === KEY_ROLE_ENUM.Checkpointer || role === KEY_ROLE_ENUM.Relayer) {
Object.keys(chains).forEach((network) => {
const key = new AgentAwsKey(agentConfig, role, network);
envVars.push(`OPT_BASE_SIGNERS_${network.toUpperCase()}_TYPE=aws`);
chainNames.forEach((name) => {
const key = new AgentAwsKey(agentConfig, role, name);
envVars.push(`OPT_BASE_SIGNERS_${name.toUpperCase()}_TYPE=aws`);
envVars.push(
`OPT_BASE_SIGNERS_${network.toUpperCase()}_ID=${
`OPT_BASE_SIGNERS_${name.toUpperCase()}_ID=${
key.credentialsAsHelmValue.aws.keyId
}`,
);
envVars.push(
`OPT_BASE_SIGNERS_${network.toUpperCase()}_REGION=${
`OPT_BASE_SIGNERS_${name.toUpperCase()}_REGION=${
key.credentialsAsHelmValue.aws.region
}`,
);
@ -251,30 +250,30 @@ export async function getSecretDeployerKey(deployerKeySecretName: string) {
async function getSecretRpcEndpoints(
agentConfig: AgentConfig,
chains: ChainConfig[],
chainNames: ChainName[],
) {
const environment = agentConfig.runEnv;
return getSecretForEachChain(
chains,
(chain: ChainConfig) => `${environment}-rpc-endpoint-${chain.name}`,
chainNames,
(name: ChainName) => `${environment}-rpc-endpoint-${name}`,
false,
);
}
async function getSecretForEachChain(
chains: ChainConfig[],
secretNameGetter: (chain: ChainConfig) => string,
chainNames: ChainName[],
secretNameGetter: (name: ChainName) => string,
parseJson: boolean,
) {
const secrets = await Promise.all(
chains.map((chain: ChainConfig) =>
fetchGCPSecret(secretNameGetter(chain), parseJson),
chainNames.map((name: ChainName) =>
fetchGCPSecret(secretNameGetter(name), parseJson),
),
);
return secrets.reduce(
(prev: any, secret: string, index: number) => ({
...prev,
[chains[index].name]: secret,
[chainNames[index]]: secret,
}),
{},
);
@ -283,13 +282,13 @@ async function getSecretForEachChain(
export async function runAgentHelmCommand(
action: HelmCommand,
agentConfig: AgentConfig,
homeChainConfig: ChainConfig,
chains: ChainConfig[],
homeChainName: ChainName,
chainNames: ChainName[],
) {
const valueDict = await helmValuesForChain(
homeChainConfig.name as ChainName,
homeChainName,
agentConfig,
chains,
chainNames,
);
const values = helmifyValues(valueDict);
@ -299,9 +298,7 @@ export async function runAgentHelmCommand(
: '';
return execCmd(
`helm ${action} ${
homeChainConfig.name
} ../../rust/helm/abacus-agent/ --namespace ${
`helm ${action} ${homeChainName} ../../rust/helm/abacus-agent/ --namespace ${
agentConfig.namespace
} ${values.join(' ')} ${extraPipe}`,
{},
@ -313,38 +310,41 @@ export async function runAgentHelmCommand(
export async function runKeymasterHelmCommand(
action: HelmCommand,
agentConfig: AgentConfig,
chains: ChainConfig[],
chainNames: ChainName[],
) {
// It's ok to use pick an arbitrary chain here since we are only grabbing the signers
const gcpKeys = await fetchAgentGCPKeys(
agentConfig.environment,
chains[0].name,
chainNames[0],
);
const bankKey = gcpKeys[KEY_ROLE_ENUM.Bank];
const config = {
networks: Object.fromEntries(
chains.map((chain) => {
return [
chain.name,
{
endpoint: (
chain.signer.provider as ethers.providers.JsonRpcProvider
).connection.url,
bank: {
signer: ensure0x(bankKey.privateKey),
address: bankKey.address,
await Promise.all(
chainNames.map(async (name) => {
return [
name,
{
endpoint: await getSecretRpcEndpoint(
agentConfig.environment,
name,
),
bank: {
signer: ensure0x(bankKey.privateKey),
address: bankKey.address,
},
threshold: 200000000000000000,
},
threshold: 200000000000000000,
},
];
}),
];
}),
),
),
homes: Object.fromEntries(
chains.map((chain) => {
chainNames.map((name) => {
return [
chain.name,
name,
{
replicas: chains.map((c) => c.name),
replicas: chainNames,
addresses: Object.fromEntries(
KEY_ROLES.filter((_) => _.endsWith('signer')).map((role) => [
role,

@ -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,14 +1,7 @@
import { types } from '@abacus-network/utils';
import { ChainName } from '../config';
import { ProxiedAddress } from '../common';
import { ChainName } from '@abacus-network/sdk';
import { RouterConfig } from '../router';
export type BridgeContractAddresses = {
router: ProxiedAddress;
token: ProxiedAddress;
helper?: types.Address;
};
export type BridgeConfig = RouterConfig & {
weth: Partial<Record<ChainName, types.Address>>;
};

@ -1,10 +1,7 @@
import { expect } from 'chai';
import { Contract, ethers } from 'ethers';
import { types } from '@abacus-network/utils';
import { BeaconProxy } from './BeaconProxy';
import { CommonDeploy } from './CommonDeploy';
import { CommonInstance } from './CommonInstance';
import { BeaconProxyPrefix } from '../verification';
import { AbacusApp, ProxiedAddress } from '@abacus-network/sdk';
export enum ViolationType {
UpgradeBeacon = 'UpgradeBeacon',
@ -14,9 +11,9 @@ export enum ViolationType {
export interface UpgradeBeaconViolation {
domain: number;
name: BeaconProxyPrefix;
name: string;
type: ViolationType.UpgradeBeacon;
beaconProxy: BeaconProxy<ethers.Contract>;
proxiedAddress: ProxiedAddress;
expected: string;
actual: string;
}
@ -43,24 +40,17 @@ export type Violation =
export type VerificationInput = [string, Contract];
export abstract class CommonInvariantChecker<
T extends CommonDeploy<CommonInstance<any>, any>,
V,
> {
readonly deploy: T;
readonly config: V;
export abstract class AbacusAppChecker<A extends AbacusApp<any, any>, C> {
readonly app: A;
readonly config: C;
readonly owners: Record<types.Domain, types.Address>;
readonly violations: Violation[];
abstract checkDomain(domain: types.Domain): Promise<void>;
abstract checkOwnership(domain: types.Domain): Promise<void>;
constructor(
deploy: T,
config: V,
owners: Record<types.Domain, types.Address>,
) {
this.deploy = deploy;
constructor(app: A, config: C, owners: Record<types.Domain, types.Address>) {
this.app = app;
this.config = config;
this.owners = owners;
this.violations = [];
@ -68,7 +58,7 @@ export abstract class CommonInvariantChecker<
async check(): Promise<void> {
await Promise.all(
this.deploy.domains.map((domain: types.Domain) =>
this.app.domainNumbers.map((domain: types.Domain) =>
this.checkDomain(domain),
),
);
@ -92,32 +82,28 @@ export abstract class CommonInvariantChecker<
}
}
async checkBeaconProxyImplementation(
async checkProxiedContract(
domain: types.Domain,
name: BeaconProxyPrefix,
beaconProxy: BeaconProxy<Contract>,
name: string,
proxiedAddress: ProxiedAddress,
) {
// TODO: This should check the correct upgrade beacon controller
expect(beaconProxy.beacon).to.not.be.undefined;
expect(beaconProxy.proxy).to.not.be.undefined;
expect(beaconProxy.contract).to.not.be.undefined;
expect(beaconProxy.implementation).to.not.be.undefined;
expect(proxiedAddress.beacon).to.not.be.undefined;
expect(proxiedAddress.proxy).to.not.be.undefined;
expect(proxiedAddress.implementation).to.not.be.undefined;
const provider = this.app.mustGetProvider(domain);
// Assert that the implementation is actually set
const provider = beaconProxy.beacon.provider;
const storageValue = await provider.getStorageAt(
beaconProxy.beacon.address,
0,
);
const storageValue = await provider.getStorageAt(proxiedAddress.beacon, 0);
const actual = ethers.utils.getAddress(storageValue.slice(26));
const expected = beaconProxy.implementation.address;
const expected = proxiedAddress.implementation;
if (actual != expected) {
const violation: UpgradeBeaconViolation = {
domain,
type: ViolationType.UpgradeBeacon,
name,
beaconProxy,
proxiedAddress,
actual,
expected,
};

@ -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,5 +1,5 @@
import { types } from '@abacus-network/utils';
import { ChainName } from './chain';
import { ChainName } from '@abacus-network/sdk';
import { DeployEnvironment } from './environment';
interface IndexingConfig {
@ -26,6 +26,7 @@ interface ValidatorConfig {
interval?: number;
// How long an validator should wait for relevant state changes afterwords
pause?: number;
confirmations: number;
}
interface CheckpointerConfig {

@ -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,16 +1,17 @@
import { ChainConfig, ContractMetricsConfig } from './config';
import { ChainName } from '@abacus-network/sdk';
import { ContractMetricsConfig } from './config';
import { HelmCommand, helmifyValues } from './utils/helm';
import { execCmd } from './utils/utils';
export async function runContractMetricsHelmCommand(
action: HelmCommand,
contractMetricsConfig: ContractMetricsConfig,
chainConfigs: ChainConfig[],
chainNames: ChainName[],
environment: string,
) {
const values = await getContractMetricsHelmChartValues(
contractMetricsConfig,
chainConfigs,
chainNames,
environment,
);
@ -26,7 +27,7 @@ export async function runContractMetricsHelmCommand(
async function getContractMetricsHelmChartValues(
contractMetricsConfig: ContractMetricsConfig,
chainConfigs: ChainConfig[],
chainNames: ChainName[],
environment: string,
) {
const config = {
@ -36,7 +37,7 @@ async function getContractMetricsHelmChartValues(
},
monitor: {
environment,
networks: chainConfigs.map((chainConfig) => chainConfig.name),
networks: chainNames,
},
fullnameOverride: 'contract-metrics',
};

@ -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,13 +1,16 @@
import { expect } from 'chai';
import { AbacusGovernance } from '@abacus-network/sdk';
import { CoreDeploy } from './CoreDeploy';
import {
Call,
CallBatch,
AbacusCore,
AbacusGovernance,
} from '@abacus-network/sdk';
import {
ValidatorViolation,
UpgradeBeaconViolation,
Violation,
ViolationType,
} from '../common';
import { Call, CallBatch } from '@abacus-network/sdk';
} from '../check';
interface DomainedCall {
domain: number;
@ -15,16 +18,16 @@ interface DomainedCall {
}
export class GovernanceCallBatchBuilder {
private _deploy: CoreDeploy;
private _core: AbacusCore;
private _governance: AbacusGovernance;
private _violations: Violation[];
constructor(
deploy: CoreDeploy,
core: AbacusCore,
governance: AbacusGovernance,
violations: Violation[],
) {
this._deploy = deploy;
this._core = core;
this._governance = governance;
this._violations = violations;
}
@ -58,10 +61,10 @@ export class GovernanceCallBatchBuilder {
violation: UpgradeBeaconViolation,
): Promise<DomainedCall> {
const domain = violation.domain;
const ubc = this._deploy.upgradeBeaconController(domain);
const ubc = this._core.mustGetContracts(domain).upgradeBeaconController;
if (ubc === undefined) throw new Error('Undefined ubc');
const tx = await ubc.populateTransaction.upgrade(
violation.beaconProxy.beacon.address,
violation.proxiedAddress.beacon,
violation.expected,
);
if (tx.to === undefined) throw new Error('undefined tx.to');
@ -72,7 +75,7 @@ export class GovernanceCallBatchBuilder {
violation: ValidatorViolation,
): Promise<DomainedCall> {
const domain = violation.local;
const manager = this._deploy.validatorManager(domain);
const manager = this._core.mustGetContracts(domain).validatorManager;
expect(manager).to.not.be.undefined;
const tx = await manager.populateTransaction.enrollValidator(
violation.remote,

@ -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,9 +1,6 @@
export { GovernanceDeploy } from './GovernanceDeploy';
export { GovernanceInstance } from './GovernanceInstance';
export { GovernanceContracts } from './GovernanceContracts';
export { GovernanceInvariantChecker } from './GovernanceInvariantChecker';
export { AbacusGovernanceDeployer } from './deploy';
export { AbacusGovernanceChecker } from './check';
export {
GovernanceContractAddresses,
GovernanceAddresses,
GovernanceConfig,
GovernanceConfigWithoutCore,

@ -1,12 +1,7 @@
import { types } from '@abacus-network/utils';
import { ChainName } from '../config';
import { ProxiedAddress } from '../common';
import { ChainName } from '@abacus-network/sdk';
import { RouterConfig } from '../router';
export type GovernanceContractAddresses = {
router: ProxiedAddress;
};
export type GovernanceAddresses = {
recoveryManager: types.Address;
governor?: types.Address;

@ -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,5 +1,5 @@
import { types } from '@abacus-network/utils';
import { ChainName } from '../config';
import { ChainName } from '@abacus-network/sdk';
export interface Router {
address: types.Address;

@ -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,
);
}

@ -2,25 +2,25 @@ import axios from 'axios';
import fs from 'fs';
import path from 'path';
import { types } from '@abacus-network/utils';
import { ChainName } from '@abacus-network/sdk';
import { ChainName, DeployEnvironment } from '../config';
import { DeployType } from '../common';
import { DeployEnvironment } from '../config';
import { ContractVerificationInput, VerificationInput } from './types';
const etherscanChains = [
ChainName.ETHEREUM,
ChainName.KOVAN,
ChainName.GOERLI,
ChainName.ROPSTEN,
ChainName.RINKEBY,
ChainName.POLYGON,
'ethereum',
'kovan',
'goerli',
'ropsten',
'rinkeby',
'polygon',
];
export class ContractVerifier {
constructor(
public readonly environment: DeployEnvironment,
public readonly deployType: DeployType,
public readonly deployType: string,
public readonly key: string,
) {}
@ -63,11 +63,11 @@ export class ContractVerifier {
}
static etherscanLink(network: ChainName, address: types.Address) {
if (network === ChainName.POLYGON) {
if (network === 'polygon') {
return `https://polygonscan.com/address/${address}`;
}
const prefix = network === ChainName.ETHEREUM ? '' : `${network}.`;
const prefix = network === 'ethereum' ? '' : `${network}.`;
return `https://${prefix}etherscan.io/address/${address}`;
}
@ -75,7 +75,7 @@ export class ContractVerifier {
let network = hre.network.name;
if (network === 'mainnet') {
network = ChainName.ETHEREUM;
network = 'ethereum';
}
const envError = (network: string) =>
@ -131,7 +131,7 @@ export class ContractVerifier {
}
async verifyProxy(network: ChainName, address: types.Address) {
const suffix = network === ChainName.ETHEREUM ? '' : `-${network}`;
const suffix = network === 'ethereum' ? '' : `-${network}`;
console.log(` Submit ${address} for proxy verification on ${network}`);
// Submit contract for verification

@ -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,25 +1,5 @@
type XAppConnectionName = 'XAppConnectionManager';
type ValidatorManagerName = 'ValidatorManager';
type UBCName = 'UpgradeBeaconController';
type EthHelperName = 'ETH Helper';
export type BeaconProxyPrefix =
| 'Outbox'
| 'Inbox'
| 'GovernanceRouter'
| 'BridgeToken'
| 'BridgeRouter';
type BeaconProxySuffix = 'Implementation' | 'UpgradeBeacon' | 'Proxy';
type BeaconProxyName = `${BeaconProxyPrefix} ${BeaconProxySuffix}`;
export type ContractVerificationName =
| XAppConnectionName
| ValidatorManagerName
| UBCName
| EthHelperName
| BeaconProxyName;
export type ContractVerificationInput = {
name: ContractVerificationName;
name: string;
address: string;
constructorArguments: any[];
isProxy?: boolean;

@ -1,16 +1,6 @@
import { ethers } from 'ethers';
import {
UpgradeBeacon__factory,
UpgradeBeaconProxy__factory,
} from '@abacus-network/core';
import { BeaconProxy } from '../common';
import {
ContractVerificationName,
ContractVerificationInput,
VerificationInput,
BeaconProxyPrefix,
} from './types';
import { ContractVerificationInput } from './types';
function getConstructorArguments(
contract: ethers.Contract,
@ -32,7 +22,7 @@ function getConstructorArguments(
}
export function getContractVerificationInput(
name: ContractVerificationName,
name: string,
contract: ethers.Contract,
bytecode: string,
isProxy?: boolean,
@ -44,36 +34,3 @@ export function getContractVerificationInput(
isProxy,
};
}
export function getBeaconProxyVerificationInput(
name: BeaconProxyPrefix,
contract: BeaconProxy<any>,
bytecode: string,
): VerificationInput {
const implementation: ContractVerificationInput = {
name: `${name} Implementation`,
address: contract.implementation.address,
constructorArguments: getConstructorArguments(
contract.implementation,
bytecode,
),
};
const beacon: ContractVerificationInput = {
name: `${name} UpgradeBeacon`,
address: contract.beacon.address,
constructorArguments: getConstructorArguments(
contract.beacon,
UpgradeBeacon__factory.bytecode,
),
};
const proxy: ContractVerificationInput = {
name: `${name} Proxy`,
address: contract.proxy.address,
constructorArguments: getConstructorArguments(
contract.proxy,
UpgradeBeaconProxy__factory.bytecode,
),
isProxy: true,
};
return [implementation, beacon, proxy];
}

@ -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,65 +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 { AbacusGovernance } from '@abacus-network/sdk';
import { AbacusCoreDeployer } from '../src/core';
import {
GovernanceDeploy,
GovernanceInvariantChecker,
AbacusGovernanceDeployer,
AbacusGovernanceChecker,
GovernanceConfig,
} from '../src/governance';
import {
getTestChains,
outputDir,
testCore as coreConfig,
testGovernance,
} from './inputs';
core as coreConfig,
registerMultiProviderTest,
governance as partialGovernanceConfig,
} from '../config/environments/test';
describe('governance', async () => {
const core = new CoreDeploy();
let chains: Record<types.Domain, ChainConfig>;
let governance = new GovernanceDeploy();
let governanceConfig: GovernanceConfig;
const coreDeployer = new AbacusCoreDeployer();
const governanceDeployer = new AbacusGovernanceDeployer();
const owners: Record<types.Domain, types.Address> = {};
let governanceConfig: GovernanceConfig;
before(async () => {
const [signer] = await ethers.getSigners();
chains = getTestChains(signer);
await core.deploy(chains, coreConfig);
governanceConfig = { ...testGovernance, core: {} };
core.domains.map((domain) => {
const name = chains[domain].name;
const addresses = testGovernance.addresses[name];
if (addresses === undefined) throw new Error('could not find addresses');
registerMultiProviderTest(governanceDeployer, signer);
registerMultiProviderTest(coreDeployer, signer);
await coreDeployer.deploy(coreConfig);
governanceConfig = { ...partialGovernanceConfig, core: {} };
coreDeployer.domainNumbers.map((domain) => {
const name = coreDeployer.mustResolveDomainName(domain);
const addresses = partialGovernanceConfig.addresses[name];
if (!addresses) throw new Error('could not find addresses');
const owner = addresses.governor;
owners[domain] = owner ? owner : ethers.constants.AddressZero;
const coreAddresses = coreDeployer.mustGetAddresses(domain);
governanceConfig.core[name] = {
upgradeBeaconController: core.upgradeBeaconController(domain).address,
xAppConnectionManager: core.xAppConnectionManager(domain).address,
upgradeBeaconController: coreAddresses.upgradeBeaconController,
xAppConnectionManager: coreAddresses.xAppConnectionManager,
};
});
});
it('deploys', async () => {
await governance.deploy(chains, governanceConfig);
});
it('checks', async () => {
const checker = new GovernanceInvariantChecker(
governance,
governanceConfig,
owners,
);
await checker.check();
await governanceDeployer.deploy(governanceConfig);
});
it('writes', async () => {
governance.writeOutput(outputDir);
const base = './test/outputs/governance';
governanceDeployer.writeVerification(path.join(base, 'verification'));
governanceDeployer.writeContracts(path.join(base, 'contracts.ts'));
});
it('reads', async () => {
governance = GovernanceDeploy.readContracts(chains, outputDir);
const checker = new GovernanceInvariantChecker(
it('checks', async () => {
const governance = new AbacusGovernance(governanceDeployer.addressesRecord);
const [signer] = await ethers.getSigners();
registerMultiProviderTest(governance, signer);
const checker = new AbacusGovernanceChecker(
governance,
governanceConfig,
owners,

@ -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;
}

@ -19,16 +19,11 @@ export abstract class AbacusApp<
}
getContracts(nameOrDomain: NameOrDomain): V | undefined {
const domain = this.resolveDomain(nameOrDomain);
return this.contracts.get(domain);
return this.getFromMap(nameOrDomain, this.contracts);
}
mustGetContracts(nameOrDomain: NameOrDomain): V {
const contracts = this.getContracts(nameOrDomain);
if (!contracts) {
throw new Error('unregistered name or domain');
}
return contracts;
return this.mustGetFromMap(nameOrDomain, this.contracts, 'Contracts');
}
/**

@ -9,7 +9,6 @@ import { ChainName, NameOrDomain } from '../types';
import { Address, canonizeId, evmId } from '../utils';
import { BridgeContractAddresses, BridgeContracts } from './contracts';
import { local } from './environments';
import { TransferMessage } from './message';
import { TokenIdentifier, ResolvedTokenInfo } from './tokens';
@ -273,5 +272,3 @@ export class AbacusBridge extends AbacusApp<
return message as TransferMessage;
}
}
export const localBridge = new AbacusBridge(local);

@ -20,20 +20,20 @@ export type BridgeContractAddresses = {
export class BridgeContracts extends AbacusAppContracts<BridgeContractAddresses> {
get router(): BridgeRouter {
return BridgeRouter__factory.connect(
this._addresses.router.proxy,
this.addresses.router.proxy,
this.connection,
);
}
get token(): BridgeToken {
return BridgeToken__factory.connect(
this._addresses.token.proxy,
this.addresses.token.proxy,
this.connection,
);
}
get helper(): ETHHelper | undefined {
if (this._addresses.helper == undefined) return undefined;
return ETHHelper__factory.connect(this._addresses.helper, this.connection);
if (this.addresses.helper == undefined) return undefined;
return ETHHelper__factory.connect(this.addresses.helper, this.connection);
}
}

@ -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',
},
},
};

@ -1,4 +1,4 @@
export { AbacusBridge, localBridge } from './app';
export { AbacusBridge } from './app';
export { BridgeContractAddresses, BridgeContracts } from './contracts';
export {
AnnotatedSend,
@ -10,3 +10,4 @@ export {
TokenDeployedTypes,
TokenDeployedEvent,
} from './events';
export { addresses } from './environments';

@ -4,15 +4,11 @@ import { Connection } from './types';
* Abstract class for managing collections of contracts
*/
export abstract class AbacusAppContracts<T> {
protected _addresses: T;
public readonly addresses: T;
private _connection?: Connection;
constructor(addresses: T) {
this._addresses = addresses;
}
toJson(): string {
return JSON.stringify(this._addresses, null, 2);
this.addresses = addresses;
}
connect(connection: Connection) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save