Merge SDK + Hardhat + Deploy packages (#643)

- Merge hardhat package into SDK
- Merge deploy package into SDK
- Reorganize SDK consts and utils
- Rename TestCoreDeploy -> TestCoreDeployer
- Rename some file names to their class names
- Remove runtime dependencies on Chai and Yargs
- Replace Axios dep with cross-fetch
- Setup type shorthand for Address type in SDK
- Update infra package
- Fix deploy's hardhat tests
- Fix incorrect test in AbacusCoreChecker
- Colocate tests into SDK src
- Add instructions for publishing
- Bump version to 0.3.1 and publish
pull/670/head v0.3.1
J M Rossy 2 years ago committed by GitHub
parent 0644e4b22d
commit 86f280e826
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/node.yml
  2. 2
      Dockerfile
  3. 34
      README.md
  4. 7
      package.json
  5. 8
      solidity/app/package.json
  6. 6
      solidity/core/package.json
  7. 3
      typescript/deploy/.gitignore
  8. 2
      typescript/deploy/.prettierignore
  9. 42
      typescript/deploy/package.json
  10. 21
      typescript/deploy/src/index.ts
  11. 3
      typescript/deploy/src/router/index.ts
  12. 42
      typescript/deploy/src/utils.ts
  13. 3
      typescript/deploy/src/verify/index.ts
  14. 15
      typescript/deploy/tsconfig.json
  15. 2
      typescript/hardhat/.gitignore
  16. 5
      typescript/hardhat/.prettierrc
  17. 40
      typescript/hardhat/index.ts
  18. 45
      typescript/hardhat/package.json
  19. 9
      typescript/hardhat/tsconfig.json
  20. 3
      typescript/infra/config/environments/dev/core.ts
  21. 3
      typescript/infra/config/environments/mainnet/core.ts
  22. 3
      typescript/infra/config/environments/test/core.ts
  23. 4
      typescript/infra/config/environments/test/index.ts
  24. 3
      typescript/infra/config/environments/testnet/core.ts
  25. 3
      typescript/infra/config/environments/testnet2/core.ts
  26. 4
      typescript/infra/hardhat.config.ts
  27. 9
      typescript/infra/package.json
  28. 3
      typescript/infra/scripts/check-deploy.ts
  29. 6
      typescript/infra/scripts/funding/fund-relayers-from-deployer.ts
  30. 4
      typescript/infra/scripts/helloworld/deploy.ts
  31. 9
      typescript/infra/scripts/helloworld/utils.ts
  32. 21
      typescript/infra/scripts/utils.ts
  33. 9
      typescript/infra/src/config/environment.ts
  34. 8
      typescript/infra/src/core/deploy.ts
  35. 7
      typescript/infra/src/verify.ts
  36. 8
      typescript/infra/test/core.test.ts
  37. 0
      typescript/sdk/hardhat.config.ts
  38. 34
      typescript/sdk/package.json
  39. 2
      typescript/sdk/src/AbacusApp.ts
  40. 16
      typescript/sdk/src/consts/chainConnectionConfigs.ts
  41. 24
      typescript/sdk/src/consts/chainMetadata.ts
  42. 26
      typescript/sdk/src/consts/chains.ts
  43. 0
      typescript/sdk/src/consts/environments/dev.json
  44. 0
      typescript/sdk/src/consts/environments/index.ts
  45. 0
      typescript/sdk/src/consts/environments/mainnet.json
  46. 0
      typescript/sdk/src/consts/environments/test.json
  47. 0
      typescript/sdk/src/consts/environments/testnet.json
  48. 0
      typescript/sdk/src/consts/environments/testnet2.json
  49. 0
      typescript/sdk/src/consts/metamask.ts
  50. 2
      typescript/sdk/src/contracts.ts
  51. 6
      typescript/sdk/src/core/AbacusCore.ts
  52. 29
      typescript/sdk/src/core/TestCoreApp.ts
  53. 31
      typescript/sdk/src/core/TestCoreDeployer.ts
  54. 21
      typescript/sdk/src/core/index.ts
  55. 24
      typescript/sdk/src/core/message.ts
  56. 37
      typescript/sdk/src/core/testAbacusDeploy.hardhat-test.ts
  57. 0
      typescript/sdk/src/deploy/.eslintrc
  58. 35
      typescript/sdk/src/deploy/AbacusAppChecker.ts
  59. 22
      typescript/sdk/src/deploy/AbacusDeployer.ts
  60. 83
      typescript/sdk/src/deploy/core/AbacusCoreChecker.ts
  61. 44
      typescript/sdk/src/deploy/core/AbacusCoreDeployer.ts
  62. 35
      typescript/sdk/src/deploy/core/types.ts
  63. 8
      typescript/sdk/src/deploy/proxy.ts
  64. 21
      typescript/sdk/src/deploy/router/AbacusRouterChecker.ts
  65. 33
      typescript/sdk/src/deploy/router/AbacusRouterDeployer.ts
  66. 5
      typescript/sdk/src/deploy/router/types.ts
  67. 2
      typescript/sdk/src/deploy/types.ts
  68. 20
      typescript/sdk/src/deploy/utils.ts
  69. 37
      typescript/sdk/src/deploy/verify/ContractVerifier.ts
  70. 0
      typescript/sdk/src/deploy/verify/types.ts
  71. 8
      typescript/sdk/src/deploy/verify/utils.ts
  72. 11
      typescript/sdk/src/domains.ts
  73. 1
      typescript/sdk/src/ethers/index.ts
  74. 10
      typescript/sdk/src/events.ts
  75. 18
      typescript/sdk/src/gas/calculator.test.ts
  76. 6
      typescript/sdk/src/gas/calculator.ts
  77. 2
      typescript/sdk/src/gas/index.ts
  78. 129
      typescript/sdk/src/index.ts
  79. 31
      typescript/sdk/src/providers/ChainConnection.ts
  80. 24
      typescript/sdk/src/providers/MultiProvider.ts
  81. 17
      typescript/sdk/src/providers/RetryProvider.ts
  82. 2
      typescript/sdk/src/proxy.ts
  83. 4
      typescript/sdk/src/router.ts
  84. 5
      typescript/sdk/src/test/.eslintrc
  85. 2
      typescript/sdk/src/test/testUtils.ts
  86. 62
      typescript/sdk/src/types.ts
  87. 142
      typescript/sdk/src/utils.ts
  88. 5
      typescript/sdk/src/utils/.eslintrc
  89. 44
      typescript/sdk/src/utils/MultiGeneric.ts
  90. 41
      typescript/sdk/src/utils/ids.ts
  91. 6
      typescript/sdk/src/utils/index.ts
  92. 0
      typescript/sdk/src/utils/number.ts
  93. 29
      typescript/sdk/src/utils/objects.ts
  94. 11
      typescript/sdk/src/utils/sets.ts
  95. 11
      typescript/sdk/src/utils/time.ts
  96. 2
      typescript/sdk/src/utils/utils.test.ts
  97. 3
      typescript/sdk/tsconfig.json
  98. 5
      typescript/utils/package.json
  99. 26
      typescript/utils/src/utils.ts
  100. 106
      yarn.lock

@ -90,8 +90,6 @@ jobs:
run: yarn workspace @abacus-network/sdk run test run: yarn workspace @abacus-network/sdk run test
- name: infra - name: infra
run: yarn workspace @abacus-network/infra run test run: yarn workspace @abacus-network/infra run test
- name: hardhat
run: yarn workspace @abacus-network/hardhat run test
test-sol: test-sol:
env: env:

@ -12,9 +12,7 @@ COPY package.json yarn.lock .yarnrc.yml ./
COPY .yarn/plugins ./.yarn/plugins COPY .yarn/plugins ./.yarn/plugins
COPY .yarn/releases ./.yarn/releases COPY .yarn/releases ./.yarn/releases
COPY typescript/utils/package.json ./typescript/utils/ COPY typescript/utils/package.json ./typescript/utils/
COPY typescript/hardhat/package.json ./typescript/hardhat/
COPY typescript/sdk/package.json ./typescript/sdk/ COPY typescript/sdk/package.json ./typescript/sdk/
COPY typescript/deploy/package.json ./typescript/deploy/
COPY typescript/infra/package.json ./typescript/infra/ COPY typescript/infra/package.json ./typescript/infra/
COPY solidity/core/package.json ./solidity/core/ COPY solidity/core/package.json ./solidity/core/
COPY solidity/app/package.json ./solidity/app/ COPY solidity/app/package.json ./solidity/app/

@ -26,12 +26,6 @@ This monorepo uses [Yarn Workspaces](https://yarnpkg.com/features/workspaces). I
yarn build yarn build
``` ```
- Running prettier
```bash
yarn prettier
```
If you are using [VSCode](https://code.visualstudio.com/), you can launch the [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) with `code mono.code-workspace`, install the recommended workspace extensions, and use the editor settings. If you are using [VSCode](https://code.visualstudio.com/), you can launch the [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) with `code mono.code-workspace`, install the recommended workspace extensions, and use the editor settings.
### Rust ### Rust
@ -53,7 +47,7 @@ cd rust
./release.sh <image_tag> ./release.sh <image_tag>
``` ```
## Deploy Procedure #### Deploy Procedure
The contract addresses of each deploy can be found in `rust/config`. The latest The contract addresses of each deploy can be found in `rust/config`. The latest
deploy will be at `rust/config/[latest timestamp]` with bridge contracts within deploy will be at `rust/config/[latest timestamp]` with bridge contracts within
@ -68,3 +62,29 @@ messages will not be relayed between chains).
Off-chain agents are **not** automatically re-deployed when new contract deploys Off-chain agents are **not** automatically re-deployed when new contract deploys
are merged. Auto-redeploys will be implemented at some future date. are merged. Auto-redeploys will be implemented at some future date.
### Publishing Packages
Packages can be versioned and published all at once with commands from the root.
First, increment the version to the desired value:
```bash
# An example of a prerelease version
yarn version:prepare 0.5.0-beta0
# Or for a release which increments the minor version
yarn version:prepare minor
```
Next, ensure packages are cleaned and rebuilt:
```bash
yarn clean && yarn build
```
Finally, publish the packages to NPM
```bash
# Note: If you have not yet logged in, first run yarn npm login
yarn publish:all --otp YOUR_OTP_HERE
```

@ -16,11 +16,14 @@
"private": true, "private": true,
"scripts": { "scripts": {
"build": "yarn workspaces foreach --verbose --parallel --topological run build", "build": "yarn workspaces foreach --verbose --parallel --topological run build",
"publish": "yarn workspaces foreach --no-private --verbose --topological npm publish --access public", "clean": "yarn workspaces foreach --verbose --parallel run clean",
"postinstall": "husky install", "postinstall": "husky install",
"prettier": "yarn workspaces foreach --verbose --parallel run prettier", "prettier": "yarn workspaces foreach --verbose --parallel run prettier",
"lint-ts": "eslint . --ext .ts", "lint-ts": "eslint . --ext .ts",
"test": "yarn workspaces foreach --verbose --parallel run test" "test": "yarn workspaces foreach --verbose --parallel run test",
"version:check": "yarn version check --interactive",
"version:prepare": "yarn workspaces foreach --no-private --verbose --topological version --immediate",
"publish:all": "yarn workspaces foreach --no-private --verbose --topological npm publish --access public"
}, },
"workspaces": [ "workspaces": [
"solidity/*", "solidity/*",

@ -1,10 +1,10 @@
{ {
"name": "@abacus-network/app", "name": "@abacus-network/app",
"description": "Solidity contracts for Abacus apps", "description": "Solidity contracts for Abacus apps",
"version": "0.2.3", "version": "0.3.1",
"dependencies": { "dependencies": {
"@abacus-network/core": "^0.2.3", "@abacus-network/core": "0.3.1",
"@abacus-network/utils": "^0.2.3", "@abacus-network/utils": "0.3.1",
"@openzeppelin/contracts-upgradeable": "^4.5.0" "@openzeppelin/contracts-upgradeable": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
@ -45,7 +45,7 @@
"repository": "https://github.com/abacus-network/abacus-monorepo", "repository": "https://github.com/abacus-network/abacus-monorepo",
"scripts": { "scripts": {
"build": "hardhat compile && tsc", "build": "hardhat compile && tsc",
"clean": "hardhat clean", "clean": "hardhat clean && rm -rf ./dist ./cache",
"coverage": "hardhat coverage", "coverage": "hardhat coverage",
"prettier": "prettier --write ./contracts ./test", "prettier": "prettier --write ./contracts ./test",
"test": "hardhat test" "test": "hardhat test"

@ -1,9 +1,9 @@
{ {
"name": "@abacus-network/core", "name": "@abacus-network/core",
"description": "Core solidity contracts for Abacus", "description": "Core solidity contracts for Abacus",
"version": "0.2.3", "version": "0.3.1",
"dependencies": { "dependencies": {
"@abacus-network/utils": "^0.2.3", "@abacus-network/utils": "0.3.1",
"@openzeppelin/contracts": "^4.6.0", "@openzeppelin/contracts": "^4.6.0",
"@openzeppelin/contracts-upgradeable": "^4.6.0", "@openzeppelin/contracts-upgradeable": "^4.6.0",
"@summa-tx/memview-sol": "^2.0.0" "@summa-tx/memview-sol": "^2.0.0"
@ -45,7 +45,7 @@
"repository": "https://github.com/abacus-network/abacus-monorepo", "repository": "https://github.com/abacus-network/abacus-monorepo",
"scripts": { "scripts": {
"build": "hardhat compile && tsc", "build": "hardhat compile && tsc",
"clean": "hardhat clean", "clean": "hardhat clean && rm -rf ./dist ./cache",
"coverage": "hardhat coverage", "coverage": "hardhat coverage",
"prettier": "prettier --write ./contracts ./interfaces ./test", "prettier": "prettier --write ./contracts ./interfaces ./test",
"test": "hardhat test" "test": "hardhat test"

@ -1,3 +0,0 @@
node_modules/
dist/
*.swo

@ -1,42 +0,0 @@
{
"name": "@abacus-network/deploy",
"description": "Deploy tools for the Abacus network",
"version": "0.2.3",
"dependencies": {
"@abacus-network/app": "^0.2.3",
"@abacus-network/core": "^0.2.3",
"@abacus-network/sdk": "^0.2.3",
"@abacus-network/utils": "^0.2.3",
"@types/debug": "^4.1.7",
"@types/yargs": "^17.0.10",
"axios": "^0.21.3",
"debug": "^4.3.4",
"yargs": "^17.4.1"
},
"devDependencies": {
"@types/node": "^16.9.1",
"ethers": "^5.6.8",
"prettier": "^2.4.1",
"ts-node": "^10.8.0",
"typescript": "^4.7.2"
},
"homepage": "https://www.useabacus.network",
"keywords": [
"Abacus",
"Typescript",
"Deployment"
],
"license": "Apache-2.0",
"main": "dist/src/index.js",
"prepublish": "yarn build",
"repository": "https://github.com/abacus-network/abacus-monorepo",
"scripts": {
"build": "tsc",
"check": "tsc --noEmit",
"prettier": "prettier --write ./src"
},
"types": "dist/src/index.d.ts",
"files": [
"/dist"
]
}

@ -1,21 +0,0 @@
export { AbacusAppChecker, Ownable } from './check';
export { CheckerViolation, EnvironmentConfig } from './config';
export {
AbacusCoreDeployer,
CoreConfig,
ValidatorManagerConfig,
} from './core/deploy';
export { AbacusCoreChecker } from './core/check';
export { AbacusDeployer } from './deploy';
export { UpgradeBeaconViolation } from './proxy';
export {
AbacusRouterChecker,
AbacusRouterDeployer,
RouterConfig,
} from './router';
export * as utils from './utils';
export {
ContractVerifier,
getConstructorArguments,
VerificationInput,
} from './verify';

@ -1,3 +0,0 @@
export { AbacusRouterChecker } from './check';
export { AbacusRouterDeployer } from './deploy';
export { RouterConfig } from './types';

@ -1,42 +0,0 @@
import { ethers } from 'ethers';
import yargs from 'yargs';
import { ChainName, MultiProvider, objMap } from '@abacus-network/sdk';
import { EnvironmentConfig } from './config';
export function getArgs() {
return yargs(process.argv.slice(2))
.alias('e', 'env')
.describe('e', 'deploy environment')
.string('e')
.help('h')
.alias('h', 'help');
}
export async function getEnvironment(): Promise<string> {
return (await getArgs().argv).e as string;
}
export const getMultiProviderFromConfigAndSigner = <Chain extends ChainName>(
environmentConfig: EnvironmentConfig<Chain>,
signer: ethers.Signer,
): MultiProvider<Chain> => {
const chainProviders = objMap(environmentConfig, (_, config) => ({
provider: signer.provider!,
signer,
confirmations: config.confirmations,
overrides: config.overrides,
}));
return new MultiProvider(chainProviders);
};
// Returns a \ b
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#implementing_basic_set_operations
export function setDifference<T>(a: Set<T>, b: Set<T>) {
const diff = new Set(a);
for (const element of b) {
diff.delete(element);
}
return diff;
}

@ -1,3 +0,0 @@
export { ContractVerifier } from './ContractVerifier';
export { ContractVerificationInput, VerificationInput } from './types';
export { getConstructorArguments, getContractVerificationInput } from './utils';

@ -1,15 +0,0 @@
{
"compilerOptions": {
"outDir": "./dist/",
"rootDir": "./"
},
"exclude": ["./node_modules/", "./dist/", "./tmp.ts"],
"extends": "../../tsconfig.json",
"include": [
"./*.ts",
"./config/**/*.ts",
"./scripts/**/*.ts",
"./src/**/*.ts",
"./test/**/*.ts"
]
}

@ -1,2 +0,0 @@
dist/
cache/

@ -1,5 +0,0 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all"
}

@ -1,40 +0,0 @@
import { MultiProvider, TestChainNames } from '@abacus-network/sdk';
import '@nomiclabs/hardhat-waffle';
import { ethers } from 'ethers';
import { extendEnvironment } from 'hardhat/config';
import { lazyObject } from "hardhat/plugins";
import 'hardhat/types/runtime';
import { TestCoreDeploy } from './src/TestCoreDeploy';
declare module 'hardhat/types/runtime' {
interface HardhatRuntimeEnvironment {
abacus: TestCoreDeploy;
}
}
export function hardhatMultiProvider(
provider: ethers.providers.Provider,
signer?: ethers.Signer,
): MultiProvider<TestChainNames> {
return new MultiProvider<TestChainNames>({
test1: {
provider,
signer,
},
test2: {
provider,
signer,
},
test3: {
provider,
signer,
},
});
}
// HardhatRuntimeEnvironment
extendEnvironment((hre) => {
hre.abacus = lazyObject(
() => new TestCoreDeploy(hardhatMultiProvider(hre.ethers.provider)),
);
});

@ -1,45 +0,0 @@
{
"name": "@abacus-network/hardhat",
"description": "Hardhat related tools for the Abacus network",
"version": "0.2.3",
"dependencies": {
"@abacus-network/core": "^0.2.3",
"@abacus-network/deploy": "^0.2.3",
"@abacus-network/sdk": "^0.2.3",
"@abacus-network/utils": "^0.2.3",
"@nomiclabs/hardhat-ethers": "^2.0.5",
"@nomiclabs/hardhat-waffle": "^2.0.2",
"ethereum-waffle": "^3.2.2",
"ethers": "^5.6.8",
"hardhat": "^2.8.4"
},
"devDependencies": {
"prettier": "^2.4.1",
"ts-node": "^10.8.0",
"typescript": "^4.7.2"
},
"directories": {
"src": "src",
"test": "test"
},
"homepage": "https://www.useabacus.network",
"keywords": [
"Abacus",
"Typescript",
"Hardhat"
],
"license": "Apache-2.0",
"main": "dist/index.js",
"prepublish": "yarn build",
"repository": "https://github.com/abacus-network/abacus-monorepo",
"scripts": {
"build": "tsc",
"check": "tsc --noEmit",
"prettier": "prettier --write ./src ./test",
"test": "hardhat test"
},
"types": "dist/index.d.ts",
"files": [
"/dist"
]
}

@ -1,9 +0,0 @@
{
"compilerOptions": {
"outDir": "./dist/",
"rootDir": "./"
},
"exclude": ["./node_modules/", "./dist/", "./tmp.ts"],
"extends": "../../tsconfig.json",
"include": ["./index.ts", "./src/*.ts"]
}

@ -1,5 +1,4 @@
import { CoreConfig } from '@abacus-network/deploy'; import { ChainMap, CoreConfig } from '@abacus-network/sdk';
import { ChainMap } from '@abacus-network/sdk';
import { DevChains } from './chains'; import { DevChains } from './chains';

@ -1,5 +1,4 @@
import { CoreConfig } from '@abacus-network/deploy'; import { ChainMap, CoreConfig } from '@abacus-network/sdk';
import { ChainMap } from '@abacus-network/sdk';
import { MainnetChains } from './chains'; import { MainnetChains } from './chains';

@ -1,5 +1,4 @@
import { CoreConfig } from '@abacus-network/deploy'; import { ChainMap, CoreConfig } from '@abacus-network/sdk';
import { ChainMap } from '@abacus-network/sdk';
import { TestChains } from './chains'; import { TestChains } from './chains';

@ -1,6 +1,6 @@
import { JsonRpcProvider } from '@ethersproject/providers'; import { JsonRpcProvider } from '@ethersproject/providers';
import { utils } from '@abacus-network/deploy'; import { getMultiProviderFromConfigAndSigner } from '@abacus-network/sdk';
import { CoreEnvironmentConfig } from '../../../src/config'; import { CoreEnvironmentConfig } from '../../../src/config';
@ -19,6 +19,6 @@ export const environment: CoreEnvironmentConfig<TestChains> = {
getMultiProvider: async () => { getMultiProvider: async () => {
const provider = testConfigs.test1.provider! as JsonRpcProvider; const provider = testConfigs.test1.provider! as JsonRpcProvider;
const signer = provider.getSigner(0); const signer = provider.getSigner(0);
return utils.getMultiProviderFromConfigAndSigner(testConfigs, signer); return getMultiProviderFromConfigAndSigner(testConfigs, signer);
}, },
}; };

@ -1,5 +1,4 @@
import { CoreConfig } from '@abacus-network/deploy'; import { ChainMap, CoreConfig } from '@abacus-network/sdk';
import { ChainMap } from '@abacus-network/sdk';
import { TestnetChains } from './chains'; import { TestnetChains } from './chains';

@ -1,5 +1,4 @@
import { CoreConfig } from '@abacus-network/deploy'; import { ChainMap, CoreConfig } from '@abacus-network/sdk';
import { ChainMap } from '@abacus-network/sdk';
import { TestnetChains } from './chains'; import { TestnetChains } from './chains';

@ -4,11 +4,11 @@ import { task } from 'hardhat/config';
import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { TestSendReceiver__factory } from '@abacus-network/core'; import { TestSendReceiver__factory } from '@abacus-network/core';
import { utils as deployUtils } from '@abacus-network/deploy';
import { import {
AbacusCore, AbacusCore,
ChainName, ChainName,
ChainNameToDomainId, ChainNameToDomainId,
getMultiProviderFromConfigAndSigner,
} from '@abacus-network/sdk'; } from '@abacus-network/sdk';
import { getCoreEnvironmentConfig } from './scripts/utils'; import { getCoreEnvironmentConfig } from './scripts/utils';
@ -64,7 +64,7 @@ task('kathy', 'Dispatches random abacus messages')
const interchainGasPayment = hre.ethers.utils.parseUnits('100', 'gwei'); const interchainGasPayment = hre.ethers.utils.parseUnits('100', 'gwei');
const config = getCoreEnvironmentConfig(environment); const config = getCoreEnvironmentConfig(environment);
const [signer] = await hre.ethers.getSigners(); const [signer] = await hre.ethers.getSigners();
const multiProvider = deployUtils.getMultiProviderFromConfigAndSigner( const multiProvider = getMultiProviderFromConfigAndSigner(
config.transactionConfigs, config.transactionConfigs,
signer, signer,
); );

@ -1,13 +1,11 @@
{ {
"name": "@abacus-network/infra", "name": "@abacus-network/infra",
"description": "Infrastructure utilities for the Abacus Network", "description": "Infrastructure utilities for the Abacus Network",
"version": "0.2.3", "version": "0.3.1",
"dependencies": { "dependencies": {
"@abacus-network/celo-ethers-provider": "^0.1.0", "@abacus-network/celo-ethers-provider": "^0.1.0",
"@abacus-network/core": "^0.2.3", "@abacus-network/helloworld": "0.3.1-beta0",
"@abacus-network/deploy": "^0.2.3", "@abacus-network/sdk": "0.3.1",
"@abacus-network/helloworld": "0.2.4",
"@abacus-network/sdk": "^0.2.3",
"@aws-sdk/client-iam": "^3.74.0", "@aws-sdk/client-iam": "^3.74.0",
"@aws-sdk/client-kms": "3.48.0", "@aws-sdk/client-kms": "3.48.0",
"@aws-sdk/client-s3": "^3.74.0", "@aws-sdk/client-s3": "^3.74.0",
@ -45,6 +43,7 @@
"scripts": { "scripts": {
"abacus": "ts-node scripts/core.ts -e test", "abacus": "ts-node scripts/core.ts -e test",
"build": "tsc", "build": "tsc",
"clean": "rm -rf ./dist ./cache",
"check": "tsc --noEmit", "check": "tsc --noEmit",
"kathy": "hardhat kathy --network localhost", "kathy": "hardhat kathy --network localhost",
"node": "hardhat node", "node": "hardhat node",

@ -1,5 +1,4 @@
import { AbacusCoreChecker } from '@abacus-network/deploy'; import { AbacusCore, AbacusCoreChecker } from '@abacus-network/sdk';
import { AbacusCore } from '@abacus-network/sdk';
import { getCoreEnvironmentConfig, getEnvironment } from './utils'; import { getCoreEnvironmentConfig, getEnvironment } from './utils';

@ -1,14 +1,13 @@
import { Console } from 'console'; import { Console } from 'console';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { utils } from '@abacus-network/deploy';
import { ChainConnection, CompleteChainMap } from '@abacus-network/sdk'; import { ChainConnection, CompleteChainMap } from '@abacus-network/sdk';
import { AgentKey, ReadOnlyAgentKey } from '../../src/agents/agent'; import { AgentKey, ReadOnlyAgentKey } from '../../src/agents/agent';
import { getRelayerKeys } from '../../src/agents/key-utils'; import { getRelayerKeys } from '../../src/agents/key-utils';
import { KEY_ROLE_ENUM } from '../../src/agents/roles'; import { KEY_ROLE_ENUM } from '../../src/agents/roles';
import { readJSONAtPath } from '../../src/utils/utils'; import { readJSONAtPath } from '../../src/utils/utils';
import { assertEnvironment, getCoreEnvironmentConfig } from '../utils'; import { assertEnvironment, getArgs, getCoreEnvironmentConfig } from '../utils';
// Min delta is 1/10 of the desired balance // Min delta is 1/10 of the desired balance
const MIN_DELTA_NUMERATOR = ethers.BigNumber.from(1); const MIN_DELTA_NUMERATOR = ethers.BigNumber.from(1);
@ -97,8 +96,7 @@ async function fundRelayer(
} }
async function main() { async function main() {
const argv = await utils const argv = await getArgs()
.getArgs()
.alias('f', 'addresses-file') .alias('f', 'addresses-file')
.describe( .describe(
'f', 'f',

@ -1,10 +1,10 @@
import path from 'path'; import path from 'path';
import { HelloWorldDeployer } from '@abacus-network/helloworld';
import { import {
HelloWorldContracts, HelloWorldContracts,
HelloWorldDeployer,
helloWorldFactories, helloWorldFactories,
} from '@abacus-network/helloworld/dist/sdk/contracts'; } from '@abacus-network/helloworld';
import { import {
AbacusCore, AbacusCore,
ChainMap, ChainMap,

@ -1,11 +1,14 @@
import { RouterConfig } from '@abacus-network/deploy'; import {
import { HelloWorldApp, HelloWorldContracts } from '@abacus-network/helloworld'; HelloWorldApp,
import { helloWorldFactories } from '@abacus-network/helloworld/dist/sdk/contracts'; HelloWorldContracts,
helloWorldFactories,
} from '@abacus-network/helloworld';
import { import {
AbacusCore, AbacusCore,
ChainMap, ChainMap,
ChainName, ChainName,
MultiProvider, MultiProvider,
RouterConfig,
buildContracts, buildContracts,
objMap, objMap,
promiseObjAll, promiseObjAll,

@ -1,6 +1,6 @@
import path from 'path'; import path from 'path';
import yargs from 'yargs';
import { utils } from '@abacus-network/deploy';
import { import {
AllChains, AllChains,
ChainMap, ChainMap,
@ -18,6 +18,20 @@ import { CoreEnvironmentConfig } from '../src/config';
import { fetchProvider, fetchSigner } from '../src/config/chain'; import { fetchProvider, fetchSigner } from '../src/config/chain';
import { EnvironmentNames } from '../src/config/environment'; import { EnvironmentNames } from '../src/config/environment';
export function getArgs() {
return yargs(process.argv.slice(2))
.alias('e', 'env')
.describe('e', 'deploy environment')
.string('e')
.help('h')
.alias('h', 'help');
}
export async function getEnvironmentFromArgs(): Promise<string> {
const argv = await getArgs().argv;
return argv.e!;
}
export function assertEnvironment(env: string): DeployEnvironment { export function assertEnvironment(env: string): DeployEnvironment {
if (EnvironmentNames.includes(env)) { if (EnvironmentNames.includes(env)) {
return env as DeployEnvironment; return env as DeployEnvironment;
@ -34,7 +48,7 @@ export function getCoreEnvironmentConfig<Env extends DeployEnvironment>(
} }
export async function getEnvironment() { export async function getEnvironment() {
return assertEnvironment(await utils.getEnvironment()); return assertEnvironment(await getEnvironmentFromArgs());
} }
export async function getEnvironmentConfig() { export async function getEnvironmentConfig() {
@ -84,8 +98,7 @@ export function getCoreRustDirectory(environment: DeployEnvironment) {
} }
export function getKeyRoleAndChainArgs() { export function getKeyRoleAndChainArgs() {
return utils return getArgs()
.getArgs()
.alias('r', 'role') .alias('r', 'role')
.describe('r', 'key role') .describe('r', 'key role')
.choices('r', Object.values(KEY_ROLE_ENUM)) .choices('r', Object.values(KEY_ROLE_ENUM))

@ -1,5 +1,10 @@
import { CoreConfig, EnvironmentConfig } from '@abacus-network/deploy'; import {
import { ChainMap, ChainName, MultiProvider } from '@abacus-network/sdk'; ChainMap,
ChainName,
CoreConfig,
EnvironmentConfig,
MultiProvider,
} from '@abacus-network/sdk';
import { environments } from '../../config/environments'; import { environments } from '../../config/environments';

@ -1,5 +1,9 @@
import { AbacusCoreDeployer } from '@abacus-network/deploy'; import {
import { ChainName, chainMetadata, objMap } from '@abacus-network/sdk'; AbacusCoreDeployer,
ChainName,
chainMetadata,
objMap,
} from '@abacus-network/sdk';
import { DeployEnvironment, RustConfig } from '../config'; import { DeployEnvironment, RustConfig } from '../config';
import { writeJSON } from '../utils/utils'; import { writeJSON } from '../utils/utils';

@ -1,8 +1,11 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { ContractVerifier, VerificationInput } from '@abacus-network/deploy'; import {
import { ChainName } from '@abacus-network/sdk'; ChainName,
ContractVerifier,
VerificationInput,
} from '@abacus-network/sdk';
import { DeployEnvironment } from './config'; import { DeployEnvironment } from './config';

@ -5,16 +5,14 @@ import path from 'path';
import sinon from 'sinon'; import sinon from 'sinon';
import { import {
AbacusCore,
AbacusCoreChecker, AbacusCoreChecker,
AbacusCoreDeployer, AbacusCoreDeployer,
CoreConfig,
} from '@abacus-network/deploy';
import { getMultiProviderFromConfigAndSigner } from '@abacus-network/deploy/dist/src/utils';
import {
AbacusCore,
ChainMap, ChainMap,
CoreConfig,
CoreContractsMap, CoreContractsMap,
MultiProvider, MultiProvider,
getMultiProviderFromConfigAndSigner,
objMap, objMap,
serializeContracts, serializeContracts,
} from '@abacus-network/sdk'; } from '@abacus-network/sdk';

@ -1,29 +1,39 @@
{ {
"name": "@abacus-network/sdk", "name": "@abacus-network/sdk",
"description": "The official SDK for the Abacus Network", "description": "The official SDK for the Abacus Network",
"version": "0.2.3", "version": "0.3.1",
"dependencies": { "dependencies": {
"@abacus-network/app": "^0.2.3", "@abacus-network/app": "0.3.1",
"@abacus-network/celo-ethers-provider": "^0.1.0", "@abacus-network/celo-ethers-provider": "^0.1.0",
"@abacus-network/core": "^0.2.3", "@abacus-network/core": "0.3.1",
"@abacus-network/utils": "^0.2.3", "@abacus-network/utils": "0.3.1",
"@types/debug": "^4.1.7",
"cross-fetch": "^3.1.5",
"debug": "^4.3.4",
"ethers": "^5.6.8" "ethers": "^5.6.8"
}, },
"devDependencies": { "devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.5",
"@nomiclabs/hardhat-waffle": "^2.0.2",
"@types/node": "^16.9.1", "@types/node": "^16.9.1",
"chai": "^4.3.6", "chai": "^4.3.6",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"ethereum-waffle": "^3.2.2",
"fs": "0.0.1-security", "fs": "0.0.1-security",
"hardhat": "^2.8.4",
"mocha": "^9.2.2", "mocha": "^9.2.2",
"prettier": "^2.4.1", "prettier": "^2.4.1",
"sinon": "^13.0.2", "sinon": "^13.0.2",
"typescript": "^4.7.2" "typescript": "^4.7.2"
}, },
"files": [
"/dist"
],
"homepage": "https://www.useabacus.network", "homepage": "https://www.useabacus.network",
"keywords": [ "keywords": [
"Abacus", "Abacus",
"Typescript", "SDK",
"SDK" "Typescript"
], ],
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "dist/index.js", "main": "dist/index.js",
@ -31,12 +41,12 @@
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"check": "tsc --noEmit", "check": "tsc --noEmit",
"clean": "rm -rf ./dist ./cache",
"prepublishOnly": "yarn build", "prepublishOnly": "yarn build",
"prettier": "prettier --write ./src ./test", "prettier": "prettier --write ./src",
"test": "mocha --config .mocharc.json './test/**/*.test.ts'" "test": "yarn test:unit && yarn test:hardhat",
"test:unit": "mocha --config .mocharc.json './src/**/*.test.ts'",
"test:hardhat": "hardhat test ./src/**/*.hardhat-test.ts"
}, },
"types": "dist/index.d.ts", "types": "dist/index.d.ts"
"files": [
"/dist"
]
} }

@ -4,7 +4,7 @@ import {
connectContracts, connectContracts,
serializeContracts, serializeContracts,
} from './contracts'; } from './contracts';
import { MultiProvider } from './provider'; import { MultiProvider } from './providers/MultiProvider';
import { ChainMap, ChainName, Connection } from './types'; import { ChainMap, ChainName, Connection } from './types';
import { MultiGeneric, objMap } from './utils'; import { MultiGeneric, objMap } from './utils';

@ -2,8 +2,7 @@ import { ethers } from 'ethers';
import { StaticCeloJsonRpcProvider } from '@abacus-network/celo-ethers-provider'; import { StaticCeloJsonRpcProvider } from '@abacus-network/celo-ethers-provider';
import { IChainConnection } from './provider'; import { ChainMap, ChainName, IChainConnection } from '../types';
import { ChainMap, ChainName } from './types';
export const ethereum: IChainConnection = { export const ethereum: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider( provider: new ethers.providers.JsonRpcProvider(
@ -168,7 +167,7 @@ export const test3: IChainConnection = {
confirmations: 1, confirmations: 1,
}; };
const _configs = { export const chainConnectionConfigs: ChainMap<ChainName, IChainConnection> = {
arbitrum, arbitrum,
auroratestnet, auroratestnet,
bsc, bsc,
@ -189,14 +188,3 @@ const _configs = {
test2, test2,
test3, test3,
}; };
export const addSignerToConnection =
<Chain extends ChainName>(signer: ethers.Signer) =>
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
(_chain: Chain, connection: IChainConnection) => ({
...connection,
signer,
});
export const chainConnectionConfigs: ChainMap<ChainName, IChainConnection> =
_configs;

@ -1,4 +1,22 @@
import { ChainMetadata, CompleteChainMap } from './types'; import { ChainName } from '../types';
/**
* A Chain and its characteristics
*/
export type ChainMetadata = {
id: number;
finalityBlocks: number;
nativeTokenDecimals?: number;
paginate?: RpcPagination;
};
/**
* RPC Pagination information
*/
export interface RpcPagination {
blocks: number;
from: number;
}
// IDs can be generated in many ways-- for example, in JS: // IDs can be generated in many ways-- for example, in JS:
// > Array.from('celo').map((c, i) => c.charCodeAt(0).toString(16).padStart(2, '0')).join('') // > Array.from('celo').map((c, i) => c.charCodeAt(0).toString(16).padStart(2, '0')).join('')
@ -120,7 +138,7 @@ export const auroratestnet: ChainMetadata = {
finalityBlocks: 1, finalityBlocks: 1,
}; };
export const chainMetadata: CompleteChainMap<ChainMetadata> = { export const chainMetadata = {
arbitrum, arbitrum,
bsc, bsc,
celo, celo,
@ -138,4 +156,4 @@ export const chainMetadata: CompleteChainMap<ChainMetadata> = {
optimismkovan, optimismkovan,
auroratestnet, auroratestnet,
...testChains, ...testChains,
}; } as Record<ChainName, ChainMetadata>;

@ -0,0 +1,26 @@
/**
* Enumeration of Abacus supported chains
*/
export enum Chains { // must be string type to be used with Object.keys
arbitrum = 'arbitrum',
alfajores = 'alfajores',
bsc = 'bsc',
mumbai = 'mumbai',
kovan = 'kovan',
goerli = 'goerli',
fuji = 'fuji',
celo = 'celo',
ethereum = 'ethereum',
avalanche = 'avalanche',
optimism = 'optimism',
polygon = 'polygon',
bsctestnet = 'bsctestnet',
arbitrumrinkeby = 'arbitrumrinkeby',
optimismkovan = 'optimismkovan',
auroratestnet = 'auroratestnet',
test1 = 'test1',
test2 = 'test2',
test3 = 'test3',
}
export const AllChains = Object.keys(Chains) as Array<keyof typeof Chains>;

@ -1,6 +1,6 @@
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { types } from '@abacus-network/utils'; import type { types } from '@abacus-network/utils';
import { ProxiedContract, ProxyAddresses, isProxyAddresses } from './proxy'; import { ProxiedContract, ProxyAddresses, isProxyAddresses } from './proxy';
import { Connection } from './types'; import { Connection } from './types';

@ -1,14 +1,14 @@
import { Inbox, Outbox } from '@abacus-network/core'; import { Inbox, Outbox } from '@abacus-network/core';
import { AbacusApp } from '../app'; import { AbacusApp } from '../AbacusApp';
import { environments } from '../consts/environments';
import { buildContracts } from '../contracts'; import { buildContracts } from '../contracts';
import { MultiProvider } from '../provider'; import { MultiProvider } from '../providers/MultiProvider';
import { ConnectionClientConfig } from '../router'; import { ConnectionClientConfig } from '../router';
import { ChainMap, ChainName, Remotes } from '../types'; import { ChainMap, ChainName, Remotes } from '../types';
import { objMap } from '../utils'; import { objMap } from '../utils';
import { CoreContracts, coreFactories } from './contracts'; import { CoreContracts, coreFactories } from './contracts';
import { environments } from './environments';
export type CoreEnvironment = keyof typeof environments; export type CoreEnvironment = keyof typeof environments;
export type CoreEnvironmentChain<E extends CoreEnvironment> = Extract< export type CoreEnvironmentChain<E extends CoreEnvironment> = Extract<

@ -1,18 +1,15 @@
import { ethers } from 'ethers';
import { TestInbox, TestOutbox } from '@abacus-network/core'; import { TestInbox, TestOutbox } from '@abacus-network/core';
import {
AbacusCore,
ChainMap,
CoreContracts,
DomainIdToChainName,
InboxContracts,
OutboxContracts,
ProxiedContract,
Remotes,
TestChainNames,
chainMetadata,
} from '@abacus-network/sdk';
import { types, utils } from '@abacus-network/utils'; import { types, utils } from '@abacus-network/utils';
import { ethers } from 'ethers';
import { chainMetadata } from '../consts/chainMetadata';
import { DomainIdToChainName } from '../domains';
import { ProxiedContract } from '../proxy';
import { ChainMap, ChainName, Remotes, TestChainNames } from '../types';
import { AbacusCore } from './AbacusCore';
import { CoreContracts, InboxContracts, OutboxContracts } from './contracts';
type MockProxyAddresses = { type MockProxyAddresses = {
kind: 'MOCK'; kind: 'MOCK';
@ -60,8 +57,10 @@ export class TestCoreApp extends AbacusCore<TestChainNames> {
return responses; return responses;
} }
async processOutboundMessages<Local extends TestChainNames>(origin: Local) { async processOutboundMessages<Local extends TestChainNames>(
const responses = new Map(); origin: Local,
): Promise<Map<ChainName, any>> {
const responses = new Map<ChainName, any>();
const contracts = this.getContracts(origin); const contracts = this.getContracts(origin);
const outbox: TestOutbox = contracts.outbox.contract; const outbox: TestOutbox = contracts.outbox.contract;

@ -1,23 +1,20 @@
import { ethers } from 'ethers';
import { TestInbox__factory, TestOutbox__factory } from '@abacus-network/core';
import { chainMetadata } from '../consts/chainMetadata';
import { AbacusCoreDeployer } from '../deploy/core/AbacusCoreDeployer';
import { CoreConfig, ValidatorManagerConfig } from '../deploy/core/types';
import { MultiProvider } from '../providers/MultiProvider';
import { ProxiedContract } from '../proxy';
import { Remotes, TestChainNames } from '../types';
import { import {
TestCoreApp, TestCoreApp,
TestInboxContracts, TestInboxContracts,
TestOutboxContracts, TestOutboxContracts,
} from './TestCoreApp'; } from './TestCoreApp';
import { TestInbox__factory, TestOutbox__factory } from '@abacus-network/core'; import { coreFactories } from './contracts';
import {
AbacusCoreDeployer,
CoreConfig,
ValidatorManagerConfig,
} from '@abacus-network/deploy';
import {
chainMetadata,
coreFactories,
MultiProvider,
ProxiedContract,
Remotes,
TestChainNames,
} from '@abacus-network/sdk';
import { ethers } from 'ethers';
// dummy config as TestInbox and TestOutbox do not use deployed ValidatorManager // dummy config as TestInbox and TestOutbox do not use deployed ValidatorManager
const testValidatorManagerConfig: CoreConfig = { const testValidatorManagerConfig: CoreConfig = {
@ -41,7 +38,7 @@ function mockProxy(contract: ethers.Contract) {
}); });
} }
export class TestCoreDeploy extends AbacusCoreDeployer<TestChainNames> { export class TestCoreDeployer extends AbacusCoreDeployer<TestChainNames> {
constructor(public readonly multiProvider: MultiProvider<TestChainNames>) { constructor(public readonly multiProvider: MultiProvider<TestChainNames>) {
super( super(
multiProvider, multiProvider,
@ -99,7 +96,7 @@ export class TestCoreDeploy extends AbacusCoreDeployer<TestChainNames> {
} as TestInboxContracts; } as TestInboxContracts;
} }
async deployCore() { async deployApp(): Promise<TestCoreApp> {
return new TestCoreApp(await this.deploy(), this.multiProvider); return new TestCoreApp(await this.deploy(), this.multiProvider);
} }
} }

@ -1,21 +0,0 @@
export { AbacusCore, CoreContractsMap } from './app';
export {
CoreContracts,
coreFactories,
InboxContracts,
OutboxContracts,
} from './contracts';
export { environments as coreEnvironments } from './environments';
export {
AbacusLifecyleEvent,
AnnotatedDispatch,
AnnotatedLifecycleEvent,
} from './events';
export {
AbacusMessage,
AbacusStatus,
MessageStatus,
resolveDomain,
resolveId,
resolveNetworks,
} from './message';

@ -1,21 +1,15 @@
import { AbacusCore } from '.'; import { BigNumber, utils as ethersUtils, providers } from 'ethers';
import { TransactionReceipt } from '@ethersproject/abstract-provider';
import { BigNumber } from '@ethersproject/bignumber';
import { keccak256 } from 'ethers/lib/utils';
import { Inbox, Outbox, Outbox__factory } from '@abacus-network/core'; import { Inbox, Outbox, Outbox__factory } from '@abacus-network/core';
import { types, utils } from '@abacus-network/utils'; import { types, utils } from '@abacus-network/utils';
import { ChainNameToDomainId, DomainIdToChainName } from '../domains';
import { Annotated, findAnnotatedSingleEvent } from '../events'; import { Annotated, findAnnotatedSingleEvent } from '../events';
import { MultiProvider } from '../provider'; import { MultiProvider } from '../providers/MultiProvider';
import { import { ChainName, NameOrDomain } from '../types';
ChainName,
ChainNameToDomainId,
DomainIdToChainName,
NameOrDomain,
} from '../types';
import { delay } from '../utils'; import { delay } from '../utils';
import { AbacusCore } from './AbacusCore';
import { import {
AnnotatedDispatch, AnnotatedDispatch,
AnnotatedLifecycleEvent, AnnotatedLifecycleEvent,
@ -104,7 +98,7 @@ export class AbacusMessage {
/** /**
* The receipt of the TX that dispatched this message * The receipt of the TX that dispatched this message
*/ */
get receipt(): TransactionReceipt { get receipt(): providers.TransactionReceipt {
return this.dispatch.receipt; return this.dispatch.receipt;
} }
@ -120,7 +114,7 @@ export class AbacusMessage {
multiProvider: MultiProvider, multiProvider: MultiProvider,
core: AbacusCore, core: AbacusCore,
nameOrDomain: NameOrDomain, nameOrDomain: NameOrDomain,
receipt: TransactionReceipt, receipt: providers.TransactionReceipt,
): AbacusMessage[] { ): AbacusMessage[] {
const messages: AbacusMessage[] = []; const messages: AbacusMessage[] = [];
const outbox = new Outbox__factory().interface; const outbox = new Outbox__factory().interface;
@ -169,7 +163,7 @@ export class AbacusMessage {
multiProvider: MultiProvider, multiProvider: MultiProvider,
core: AbacusCore, core: AbacusCore,
nameOrDomain: NameOrDomain, nameOrDomain: NameOrDomain,
receipt: TransactionReceipt, receipt: providers.TransactionReceipt,
): AbacusMessage { ): AbacusMessage {
const messages: AbacusMessage[] = AbacusMessage.fromReceipt( const messages: AbacusMessage[] = AbacusMessage.fromReceipt(
multiProvider, multiProvider,
@ -384,7 +378,7 @@ export class AbacusMessage {
* The keccak256 hash of the message body * The keccak256 hash of the message body
*/ */
get bodyHash(): string { get bodyHash(): string {
return keccak256(this.body); return ethersUtils.keccak256(this.body);
} }
/** /**

@ -1,26 +1,43 @@
import { hardhatMultiProvider } from '../index'; import '@nomiclabs/hardhat-ethers';
import { TestCoreApp } from '../src/TestCoreApp'; import '@nomiclabs/hardhat-waffle';
import { TestCoreDeploy } from '../src/TestCoreDeploy';
import { TestOutbox, TestRecipient__factory } from '@abacus-network/core';
import { chainMetadata } from '@abacus-network/sdk';
import { utils } from '@abacus-network/utils';
import { expect } from 'chai'; import { expect } from 'chai';
import { ethers } from 'hardhat'; import { ethers } from 'hardhat';
import { TestOutbox, TestRecipient__factory } from '@abacus-network/core';
import { utils } from '@abacus-network/utils';
import { chainMetadata } from '../consts/chainMetadata';
import { getMultiProviderFromConfigAndSigner } from '../deploy/utils';
import { TestCoreApp } from './TestCoreApp';
import { TestCoreDeployer } from './TestCoreDeployer';
const localChain = 'test1'; const localChain = 'test1';
const localDomain = chainMetadata[localChain].id; const localDomain = chainMetadata[localChain].id;
const remoteChain = 'test2'; const remoteChain = 'test2';
const remoteDomain = chainMetadata[remoteChain].id; const remoteDomain = chainMetadata[remoteChain].id;
const message = '0xdeadbeef'; const message = '0xdeadbeef';
describe('TestCoreDeploy', async () => { describe('TestCoreDeployer', async () => {
let abacus: TestCoreApp, localOutbox: TestOutbox, remoteOutbox: TestOutbox; let abacus: TestCoreApp, localOutbox: TestOutbox, remoteOutbox: TestOutbox;
beforeEach(async () => { beforeEach(async () => {
const [signer] = await ethers.getSigners(); const [signer] = await ethers.getSigners();
const multiProvider = hardhatMultiProvider(ethers.provider, signer);
const deployer = new TestCoreDeploy(multiProvider); const config = {
abacus = await deployer.deployCore(); test1: {
provider: ethers.provider,
},
test2: {
provider: ethers.provider,
},
test3: {
provider: ethers.provider,
},
};
const multiProvider = getMultiProviderFromConfigAndSigner(config, signer);
const deployer = new TestCoreDeployer(multiProvider);
abacus = await deployer.deployApp();
const recipient = await new TestRecipient__factory(signer).deploy(); const recipient = await new TestRecipient__factory(signer).deploy();
localOutbox = abacus.getContracts(localChain).outbox.contract; localOutbox = abacus.getContracts(localChain).outbox.contract;

@ -1,16 +1,13 @@
import { expect } from 'chai'; import { utils } from '@abacus-network/utils';
import type { types } from '@abacus-network/utils';
import { import { AbacusApp } from '../AbacusApp';
AbacusApp, import { MultiProvider } from '../providers/MultiProvider';
BeaconProxyAddresses, import { BeaconProxyAddresses } from '../proxy';
ChainMap, import { ChainMap, ChainName } from '../types';
ChainName,
MultiProvider,
} from '@abacus-network/sdk';
import { types } from '@abacus-network/utils';
import { CheckerViolation } from './config';
import { upgradeBeaconImplementation, upgradeBeaconViolation } from './proxy'; import { upgradeBeaconImplementation, upgradeBeaconViolation } from './proxy';
import { CheckerViolation } from './types';
export interface Ownable { export interface Ownable {
owner(): Promise<types.Address>; owner(): Promise<types.Address>;
@ -39,13 +36,13 @@ export abstract class AbacusAppChecker<
abstract checkChain(chain: Chain): Promise<void>; abstract checkChain(chain: Chain): Promise<void>;
async check() { async check(): Promise<void[]> {
return Promise.all( return Promise.all(
this.app.chains().map((chain) => this.checkChain(chain)), this.app.chains().map((chain) => this.checkChain(chain)),
); );
} }
addViolation(violation: CheckerViolation) { addViolation(violation: CheckerViolation): void {
if (!this.isDuplicateViolation(violation)) { if (!this.isDuplicateViolation(violation)) {
this.violations.push(violation); this.violations.push(violation);
} }
@ -55,7 +52,7 @@ export abstract class AbacusAppChecker<
chain: Chain, chain: Chain,
name: string, name: string,
proxiedAddress: BeaconProxyAddresses, proxiedAddress: BeaconProxyAddresses,
) { ): Promise<void> {
const dc = this.multiProvider.getChainConnection(chain); const dc = this.multiProvider.getChainConnection(chain);
const implementation = await upgradeBeaconImplementation( const implementation = await upgradeBeaconImplementation(
dc.provider, dc.provider,
@ -73,10 +70,10 @@ export abstract class AbacusAppChecker<
ownables: Ownable[], ownables: Ownable[],
): Promise<void> { ): Promise<void> {
const owners = await Promise.all(ownables.map((o) => o.owner())); const owners = await Promise.all(ownables.map((o) => o.owner()));
owners.map((_) => expect(_).to.equal(owner)); owners.map((_) => utils.assert(_ == owner));
} }
isDuplicateViolation(violation: CheckerViolation) { isDuplicateViolation(violation: CheckerViolation): boolean {
const duplicates = this.violations.filter( const duplicates = this.violations.filter(
(v) => (v) =>
violation.type === v.type && violation.type === v.type &&
@ -87,20 +84,20 @@ export abstract class AbacusAppChecker<
return duplicates.length > 0; return duplicates.length > 0;
} }
expectViolations(types: string[], expectedMatches: number[]) { expectViolations(types: string[], expectedMatches: number[]): void {
// Every type should have exactly the number of expected matches. // Every type should have exactly the number of expected matches.
const actualMatches = types.map( const actualMatches = types.map(
(t) => this.violations.map((v) => v.type === t).filter(Boolean).length, (t) => this.violations.map((v) => v.type === t).filter(Boolean).length,
); );
expect(actualMatches).to.deep.equal(expectedMatches); utils.assert(utils.deepEquals(actualMatches, expectedMatches));
// Every violation should be matched by at least one partial. // Every violation should be matched by at least one partial.
const unmatched = this.violations.map( const unmatched = this.violations.map(
(v) => types.map((t) => v.type === t).filter(Boolean).length, (v) => types.map((t) => v.type === t).filter(Boolean).length,
); );
expect(unmatched).to.not.include(0); utils.assert(!unmatched.includes(0));
} }
expectEmpty(): void { expectEmpty(): void {
expect(this.violations).to.be.empty; utils.assert(this.violations.length === 0);
} }
} }

@ -5,25 +5,21 @@ import {
UpgradeBeaconProxy__factory, UpgradeBeaconProxy__factory,
UpgradeBeacon__factory, UpgradeBeacon__factory,
} from '@abacus-network/core'; } from '@abacus-network/core';
import type { types } from '@abacus-network/utils';
import { import {
AbacusContracts, AbacusContracts,
AbacusFactories, AbacusFactories,
BeaconProxyAddresses,
ChainMap,
ChainName,
MultiProvider,
ProxiedContract,
connectContracts, connectContracts,
objMap,
serializeContracts, serializeContracts,
} from '@abacus-network/sdk'; } from '../contracts';
import { ProxyKind } from '@abacus-network/sdk/dist/proxy'; import { MultiProvider } from '../providers/MultiProvider';
import { types } from '@abacus-network/utils'; import { BeaconProxyAddresses, ProxiedContract, ProxyKind } from '../proxy';
import { ChainMap, ChainName } from '../types';
import { objMap } from '../utils/objects';
import { import { ContractVerificationInput } from './verify/types';
ContractVerificationInput, import { getContractVerificationInput } from './verify/utils';
getContractVerificationInput,
} from './verify';
export interface DeployerOptions { export interface DeployerOptions {
logger?: Debugger; logger?: Debugger;

@ -1,44 +1,22 @@
import { expect } from 'chai';
import { MultisigValidatorManager } from '@abacus-network/core'; import { MultisigValidatorManager } from '@abacus-network/core';
import { import { utils } from '@abacus-network/utils';
AbacusCore,
BeaconProxyAddresses,
ChainName,
ChainNameToDomainId,
chainMetadata,
objMap,
promiseObjAll,
} from '@abacus-network/sdk';
import { AbacusAppChecker } from '../check';
import { CheckerViolation } from '../config';
import { setDifference } from '../utils';
import { CoreConfig } from './deploy';
export enum CoreViolationType {
ValidatorManager = 'ValidatorManager',
Validator = 'Validator',
}
export enum ValidatorViolationType { import { chainMetadata } from '../../consts/chainMetadata';
EnrollValidator = 'EnrollValidator', import { AbacusCore } from '../../core/AbacusCore';
UnenrollValidator = 'UnenrollValidator', import { ChainNameToDomainId } from '../../domains';
Threshold = 'Threshold', import { BeaconProxyAddresses } from '../../proxy';
} import { ChainName } from '../../types';
import { objMap, promiseObjAll } from '../../utils/objects';
export interface ValidatorManagerViolation extends CheckerViolation { import { setDifference } from '../../utils/sets';
type: CoreViolationType.ValidatorManager; import { AbacusAppChecker } from '../AbacusAppChecker';
}
export interface ValidatorViolation extends CheckerViolation { import {
type: CoreViolationType.Validator; CoreConfig,
data: { CoreViolationType,
type: ValidatorViolationType; ValidatorManagerViolation,
validatorManagerAddress: string; ValidatorViolation,
}; ValidatorViolationType,
} } from './types';
export class AbacusCoreChecker< export class AbacusCoreChecker<
Chain extends ChainName, Chain extends ChainName,
@ -72,7 +50,7 @@ export class AbacusCoreChecker<
const contracts = this.app.getContracts(chain); const contracts = this.app.getContracts(chain);
const outbox = contracts.outbox.contract; const outbox = contracts.outbox.contract;
const localDomain = await outbox.localDomain(); const localDomain = await outbox.localDomain();
expect(localDomain).to.equal(ChainNameToDomainId[chain]); utils.assert(localDomain === ChainNameToDomainId[chain]);
const actualManager = await contracts.outbox.contract.validatorManager(); const actualManager = await contracts.outbox.contract.validatorManager();
const expectedManager = contracts.outboxValidatorManager.address; const expectedManager = contracts.outboxValidatorManager.address;
@ -89,14 +67,14 @@ export class AbacusCoreChecker<
// Checks validator sets of the OutboxValidatorManager and all // Checks validator sets of the OutboxValidatorManager and all
// InboxValidatorManagers on the chain. // InboxValidatorManagers on the chain.
async checkValidatorManagers(chain: Chain) { async checkValidatorManagers(chain: Chain): Promise<void> {
const coreContracts = this.app.getContracts(chain); const coreContracts = this.app.getContracts(chain);
await this.checkValidatorManager( await this.checkValidatorManager(
chain, chain,
chain, chain,
coreContracts.outboxValidatorManager, coreContracts.outboxValidatorManager,
); );
return promiseObjAll( await promiseObjAll(
objMap(coreContracts.inboxes, (remote, inbox) => objMap(coreContracts.inboxes, (remote, inbox) =>
this.checkValidatorManager(chain, remote, inbox.inboxValidatorManager), this.checkValidatorManager(chain, remote, inbox.inboxValidatorManager),
), ),
@ -158,7 +136,7 @@ export class AbacusCoreChecker<
} }
const expectedThreshold = validatorManagerConfig.threshold; const expectedThreshold = validatorManagerConfig.threshold;
expect(expectedThreshold).to.not.be.undefined; utils.assert(expectedThreshold !== undefined);
const actualThreshold = await validatorManager.threshold(); const actualThreshold = await validatorManager.threshold();
@ -186,7 +164,7 @@ export class AbacusCoreChecker<
objMap(coreContracts.inboxes, async (_, inbox) => { objMap(coreContracts.inboxes, async (_, inbox) => {
const expected = inbox.inboxValidatorManager.address; const expected = inbox.inboxValidatorManager.address;
const actual = await inbox.inbox.contract.validatorManager(); const actual = await inbox.inbox.contract.validatorManager();
expect(actual).to.equal(expected); utils.assert(actual === expected);
}), }),
); );
@ -194,10 +172,10 @@ export class AbacusCoreChecker<
objMap(coreContracts.inboxes, async (remoteChain, inbox) => { objMap(coreContracts.inboxes, async (remoteChain, inbox) => {
// check that the inbox has the right local domain // check that the inbox has the right local domain
const actualLocalDomain = await inbox.inbox.contract.localDomain(); const actualLocalDomain = await inbox.inbox.contract.localDomain();
expect(actualLocalDomain).to.equal(ChainNameToDomainId[chain]); utils.assert(actualLocalDomain === ChainNameToDomainId[chain]);
const actualRemoteDomain = await inbox.inbox.contract.remoteDomain(); const actualRemoteDomain = await inbox.inbox.contract.remoteDomain();
expect(actualRemoteDomain).to.equal(ChainNameToDomainId[remoteChain]); utils.assert(actualRemoteDomain === ChainNameToDomainId[remoteChain]);
}), }),
); );
@ -208,10 +186,15 @@ export class AbacusCoreChecker<
coreAddresses.inboxes, coreAddresses.inboxes,
); );
const implementations = inboxes.map((r) => r.implementation); const implementations = inboxes.map((r) => r.implementation);
const identical = (a: any, b: any) => (a === b ? a : false);
const upgradeBeacons = inboxes.map((r) => r.beacon); const upgradeBeacons = inboxes.map((r) => r.beacon);
expect(implementations.reduce(identical)).to.not.be.false; utils.assert(
expect(upgradeBeacons.reduce(identical)).to.not.be.false; implementations.every(
(implementation) => implementation === implementations[0],
),
);
utils.assert(
upgradeBeacons.every((beacon) => beacon === upgradeBeacons[0]),
);
} }
async checkAbacusConnectionManager(chain: Chain): Promise<void> { async checkAbacusConnectionManager(chain: Chain): Promise<void> {
@ -222,13 +205,13 @@ export class AbacusCoreChecker<
// inbox is enrolled in abacusConnectionManager // inbox is enrolled in abacusConnectionManager
const enrolledInboxes = const enrolledInboxes =
await coreContracts.abacusConnectionManager.getInboxes(remoteDomain); await coreContracts.abacusConnectionManager.getInboxes(remoteDomain);
expect(enrolledInboxes).to.deep.equal([inbox.inbox.address]); utils.assert(utils.deepEquals(enrolledInboxes, [inbox.inbox.address]));
}), }),
); );
// Outbox is set on abacusConnectionManager // Outbox is set on abacusConnectionManager
const outbox = await coreContracts.abacusConnectionManager.outbox(); const outbox = await coreContracts.abacusConnectionManager.outbox();
expect(outbox).to.equal(coreContracts.outbox.address); utils.assert(outbox === coreContracts.outbox.address);
} }
async checkProxiedContracts(chain: Chain): Promise<void> { async checkProxiedContracts(chain: Chain): Promise<void> {

@ -2,37 +2,29 @@ import debug from 'debug';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { Inbox, Ownable } from '@abacus-network/core'; import { Inbox, Ownable } from '@abacus-network/core';
import type { types } from '@abacus-network/utils';
import { chainMetadata } from '../../consts/chainMetadata';
import { AbacusCore, CoreContractsMap } from '../../core/AbacusCore';
import { import {
AbacusCore,
BeaconProxyAddresses,
ChainConnection,
ChainMap,
ChainName,
CoreContracts, CoreContracts,
CoreContractsMap,
InboxContracts, InboxContracts,
MultiProvider,
OutboxContracts, OutboxContracts,
ProxiedContract, coreFactories,
} from '../../core/contracts';
import { MultiProvider } from '../../providers/MultiProvider';
import { BeaconProxyAddresses, ProxiedContract } from '../../proxy';
import {
ChainMap,
ChainName,
IChainConnection,
RemoteChainMap, RemoteChainMap,
Remotes, Remotes,
chainMetadata, } from '../../types';
coreFactories, import { objMap, promiseObjAll } from '../../utils/objects';
objMap, import { AbacusDeployer } from '../AbacusDeployer';
promiseObjAll,
} from '@abacus-network/sdk';
import { types } from '@abacus-network/utils';
import { AbacusDeployer } from '../deploy';
export type ValidatorManagerConfig = { import { CoreConfig, ValidatorManagerConfig } from './types';
validators: Array<types.Address>;
threshold: number;
};
export type CoreConfig = {
validatorManager: ValidatorManagerConfig;
};
export class AbacusCoreDeployer<Chain extends ChainName> extends AbacusDeployer< export class AbacusCoreDeployer<Chain extends ChainName> extends AbacusDeployer<
Chain, Chain,
@ -203,7 +195,7 @@ export class AbacusCoreDeployer<Chain extends ChainName> extends AbacusDeployer<
core: AbacusCore<CoreNetworks>, core: AbacusCore<CoreNetworks>,
owners: ChainMap<CoreNetworks, types.Address>, owners: ChainMap<CoreNetworks, types.Address>,
multiProvider: MultiProvider<CoreNetworks>, multiProvider: MultiProvider<CoreNetworks>,
) { ): Promise<ChainMap<CoreNetworks, ethers.ContractReceipt[]>> {
return promiseObjAll( return promiseObjAll(
objMap(core.contractsMap, async (chain, coreContracts) => { objMap(core.contractsMap, async (chain, coreContracts) => {
const owner = owners[chain]; const owner = owners[chain];
@ -223,7 +215,7 @@ export class AbacusCoreDeployer<Chain extends ChainName> extends AbacusDeployer<
>( >(
coreContracts: CoreContracts<Chain, Local>, coreContracts: CoreContracts<Chain, Local>,
owner: types.Address, owner: types.Address,
chainConnection: ChainConnection, chainConnection: IChainConnection,
): Promise<ethers.ContractReceipt[]> { ): Promise<ethers.ContractReceipt[]> {
const ownables: Ownable[] = [ const ownables: Ownable[] = [
coreContracts.outbox.contract, coreContracts.outbox.contract,

@ -0,0 +1,35 @@
import type { types } from '@abacus-network/utils';
import type { CheckerViolation } from '../types';
export type ValidatorManagerConfig = {
validators: Array<types.Address>;
threshold: number;
};
export type CoreConfig = {
validatorManager: ValidatorManagerConfig;
};
export enum CoreViolationType {
ValidatorManager = 'ValidatorManager',
Validator = 'Validator',
}
export enum ValidatorViolationType {
EnrollValidator = 'EnrollValidator',
UnenrollValidator = 'UnenrollValidator',
Threshold = 'Threshold',
}
export interface ValidatorManagerViolation extends CheckerViolation {
type: CoreViolationType.ValidatorManager;
}
export interface ValidatorViolation extends CheckerViolation {
type: CoreViolationType.Validator;
data: {
type: ValidatorViolationType;
validatorManagerAddress: string;
};
}

@ -1,9 +1,11 @@
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { BeaconProxyAddresses, ChainName } from '@abacus-network/sdk'; import type { types } from '@abacus-network/utils';
import { types } from '@abacus-network/utils';
import { CheckerViolation } from './config'; import { BeaconProxyAddresses } from '../proxy';
import { ChainName } from '../types';
import { CheckerViolation } from './types';
export interface UpgradeBeaconViolation extends CheckerViolation { export interface UpgradeBeaconViolation extends CheckerViolation {
type: BeaconProxyAddresses['kind']; type: BeaconProxyAddresses['kind'];

@ -1,14 +1,10 @@
import { expect } from 'chai';
import {
AbacusApp,
ChainName,
RouterContracts,
chainMetadata,
} from '@abacus-network/sdk';
import { utils } from '@abacus-network/utils'; import { utils } from '@abacus-network/utils';
import { AbacusAppChecker, Ownable } from '../check'; import { AbacusApp } from '../../AbacusApp';
import { chainMetadata } from '../../consts/chainMetadata';
import { RouterContracts } from '../../router';
import { ChainName } from '../../types';
import { AbacusAppChecker, Ownable } from '../AbacusAppChecker';
import { RouterConfig } from './types'; import { RouterConfig } from './types';
@ -18,7 +14,7 @@ export class AbacusRouterChecker<
App extends AbacusApp<Contracts, Chain>, App extends AbacusApp<Contracts, Chain>,
Config extends RouterConfig, Config extends RouterConfig,
> extends AbacusAppChecker<Chain, App, Config> { > extends AbacusAppChecker<Chain, App, Config> {
checkOwnership(chain: Chain) { checkOwnership(chain: Chain): Promise<void> {
const owner = this.configMap[chain].owner; const owner = this.configMap[chain].owner;
const ownables = this.ownables(chain); const ownables = this.ownables(chain);
return AbacusAppChecker.checkOwnership(owner, ownables); return AbacusAppChecker.checkOwnership(owner, ownables);
@ -36,9 +32,8 @@ export class AbacusRouterChecker<
this.app.remoteChains(chain).map(async (remoteNetwork) => { this.app.remoteChains(chain).map(async (remoteNetwork) => {
const remoteRouter = this.app.getContracts(remoteNetwork).router; const remoteRouter = this.app.getContracts(remoteNetwork).router;
const remoteChainId = chainMetadata[remoteNetwork].id; const remoteChainId = chainMetadata[remoteNetwork].id;
expect(await router.routers(remoteChainId)).to.equal( const address = await router.routers(remoteChainId);
utils.addressToBytes32(remoteRouter.address), utils.assert(address === utils.addressToBytes32(remoteRouter.address));
);
}), }),
); );
} }

@ -1,19 +1,14 @@
import { debug } from 'debug'; import { debug } from 'debug';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import {
ChainMap,
ChainName,
MultiProvider,
RouterContracts,
RouterFactories,
chainMetadata,
objMap,
promiseObjAll,
} from '@abacus-network/sdk';
import { utils } from '@abacus-network/utils'; import { utils } from '@abacus-network/utils';
import { AbacusDeployer, DeployerOptions } from '../deploy'; import { chainMetadata } from '../../consts/chainMetadata';
import { MultiProvider } from '../../providers/MultiProvider';
import { RouterContracts, RouterFactories } from '../../router';
import { ChainMap, ChainName } from '../../types';
import { objMap, promiseObjAll } from '../../utils/objects';
import { AbacusDeployer, DeployerOptions } from '../AbacusDeployer';
import { RouterConfig } from './types'; import { RouterConfig } from './types';
@ -35,7 +30,9 @@ export abstract class AbacusRouterDeployer<
}); });
} }
async initConnectionClient(contractsMap: ChainMap<Chain, Contracts>) { async initConnectionClient(
contractsMap: ChainMap<Chain, Contracts>,
): Promise<void> {
this.logger(`Initializing connection clients (if not already)...`); this.logger(`Initializing connection clients (if not already)...`);
await promiseObjAll( await promiseObjAll(
objMap(contractsMap, async (local, contracts) => { objMap(contractsMap, async (local, contracts) => {
@ -69,7 +66,9 @@ export abstract class AbacusRouterDeployer<
); );
} }
async enrollRemoteRouters(contractsMap: ChainMap<Chain, Contracts>) { async enrollRemoteRouters(
contractsMap: ChainMap<Chain, Contracts>,
): Promise<void> {
this.logger(`Enrolling deployed routers with each other...`); this.logger(`Enrolling deployed routers with each other...`);
// Make all routers aware of eachother. // Make all routers aware of eachother.
await promiseObjAll( await promiseObjAll(
@ -89,7 +88,9 @@ export abstract class AbacusRouterDeployer<
); );
} }
async transferOwnership(contractsMap: ChainMap<Chain, Contracts>) { async transferOwnership(
contractsMap: ChainMap<Chain, Contracts>,
): Promise<void> {
this.logger(`Transferring ownership of routers...`); this.logger(`Transferring ownership of routers...`);
await promiseObjAll( await promiseObjAll(
objMap(contractsMap, async (chain, contracts) => { objMap(contractsMap, async (chain, contracts) => {
@ -103,7 +104,9 @@ export abstract class AbacusRouterDeployer<
); );
} }
async deploy(partialDeployment?: Partial<Record<Chain, Contracts>>) { async deploy(
partialDeployment?: Partial<Record<Chain, Contracts>>,
): Promise<ChainMap<Chain, Contracts>> {
const contractsMap = await super.deploy(partialDeployment); const contractsMap = await super.deploy(partialDeployment);
await this.enrollRemoteRouters(contractsMap); await this.enrollRemoteRouters(contractsMap);

@ -1,5 +1,6 @@
import { ConnectionClientConfig } from '@abacus-network/sdk'; import type { types } from '@abacus-network/utils';
import { types } from '@abacus-network/utils';
import type { ConnectionClientConfig } from '../../router';
export type OwnableConfig = { export type OwnableConfig = {
owner: types.Address; owner: types.Address;

@ -1,4 +1,4 @@
import { ChainMap, ChainName, IChainConnection } from '@abacus-network/sdk'; import type { ChainMap, ChainName, IChainConnection } from '../types';
export interface CheckerViolation { export interface CheckerViolation {
chain: ChainName; chain: ChainName;

@ -0,0 +1,20 @@
import { ethers } from 'ethers';
import { MultiProvider } from '../providers/MultiProvider';
import { ChainName } from '../types';
import { objMap } from '../utils';
import { EnvironmentConfig } from './types';
export function getMultiProviderFromConfigAndSigner<Chain extends ChainName>(
environmentConfig: EnvironmentConfig<Chain>,
signer: ethers.Signer,
): MultiProvider<Chain> {
const chainProviders = objMap(environmentConfig, (_, config) => ({
provider: signer.provider!,
signer,
confirmations: config.confirmations,
overrides: config.overrides,
}));
return new MultiProvider(chainProviders);
}

@ -1,7 +1,8 @@
import axios from 'axios'; import fetch from 'cross-fetch';
import { ChainName } from '@abacus-network/sdk'; import type { types } from '@abacus-network/utils';
import { types } from '@abacus-network/utils';
import { ChainName } from '../../types';
import { ContractVerificationInput, VerificationInput } from './types'; import { ContractVerificationInput, VerificationInput } from './types';
@ -20,7 +21,7 @@ export abstract class ContractVerifier {
abstract chainNames: ChainName[]; abstract chainNames: ChainName[];
abstract getVerificationInput(chain: ChainName): VerificationInput; abstract getVerificationInput(chain: ChainName): VerificationInput;
static etherscanLink(chain: ChainName, address: types.Address) { static etherscanLink(chain: ChainName, address: types.Address): string {
if (chain === 'polygon') { if (chain === 'polygon') {
return `https://polygonscan.com/address/${address}`; return `https://polygonscan.com/address/${address}`;
} }
@ -29,7 +30,8 @@ export abstract class ContractVerifier {
return `https://${prefix}etherscan.io/address/${address}`; return `https://${prefix}etherscan.io/address/${address}`;
} }
async verify(hre: any) { // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async verify(hre: any): Promise<void> {
let chain = hre.network.name; let chain = hre.network.name;
if (chain === 'mainnet') { if (chain === 'mainnet') {
@ -59,8 +61,9 @@ export abstract class ContractVerifier {
async verifyContract( async verifyContract(
chain: ChainName, chain: ChainName,
input: ContractVerificationInput, input: ContractVerificationInput,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
hre: any, hre: any,
) { ): Promise<void> {
try { try {
console.log( console.log(
` Attempt to verify ${ ` Attempt to verify ${
@ -86,28 +89,32 @@ export abstract class ContractVerifier {
console.log('\n\n'); // add space after each attempt console.log('\n\n'); // add space after each attempt
} }
async verifyProxy(chain: ChainName, address: types.Address) { async verifyProxy(chain: ChainName, address: types.Address): Promise<void> {
const suffix = chain === 'ethereum' ? '' : `-${chain}`; const suffix = chain === 'ethereum' ? '' : `-${chain}`;
console.log(` Submit ${address} for proxy verification on ${chain}`); console.log(` Submit ${address} for proxy verification on ${chain}`);
// Submit contract for verification // Submit contract for verification
const verifyResponse = await axios.post( const verifyResponse = await fetch(
`https://api${suffix}.etherscan.io/api`, `https://api${suffix}.etherscan.io/api?address=${address}`,
`address=${address}`,
{ {
params: { method: 'POST',
body: JSON.stringify({
module: 'contract', module: 'contract',
action: 'verifyproxycontract', action: 'verifyproxycontract',
apikey: this.key, apikey: this.key,
}, }),
}, },
); );
// Validate that submission worked // Validate that submission worked
if (verifyResponse.status !== 200) { if (!verifyResponse.ok) {
throw new Error('Verify POST failed'); throw new Error('Verify POST failed');
} else if (verifyResponse.data.status != '1') { }
throw new Error(verifyResponse.data.result);
const data = await verifyResponse.json();
if (data?.status != '1') {
throw new Error(data?.result);
} }
console.log(` Submitted.`); console.log(` Submitted.`);

@ -1,9 +1,11 @@
import { Fragment } from '@ethersproject/abi'; import { ethers, utils } from 'ethers';
import { ethers } from 'ethers';
import { ContractVerificationInput } from './types'; import { ContractVerificationInput } from './types';
export function formatFunctionArguments(fragment: Fragment, args: any[]) { export function formatFunctionArguments(
fragment: utils.Fragment,
args: any[],
): any {
const params = Object.fromEntries( const params = Object.fromEntries(
fragment.inputs.map((input, index) => [input.name, args[index]]), fragment.inputs.map((input, index) => [input.name, args[index]]),
); );

@ -0,0 +1,11 @@
import { chainMetadata } from './consts/chainMetadata';
import { AllChains } from './consts/chains';
import { ChainName, CompleteChainMap } from './types';
export const DomainIdToChainName = Object.fromEntries(
AllChains.map((chain) => [chainMetadata[chain].id, chain]),
) as Record<number, ChainName>;
export const ChainNameToDomainId = Object.fromEntries(
AllChains.map((chain) => [chain, chainMetadata[chain].id]),
) as CompleteChainMap<number>;

@ -1 +0,0 @@
export { RetryProvider, RetryJsonRpcProvider } from './retry-provider';

@ -1,19 +1,19 @@
import { TransactionReceipt } from '@ethersproject/abstract-provider'; import { providers } from 'ethers';
import { TypedEvent, TypedEventFilter } from '@abacus-network/core/dist/common'; import { TypedEvent, TypedEventFilter } from '@abacus-network/core/dist/common';
import { chainMetadata } from './chain-metadata'; import { chainMetadata } from './consts/chainMetadata';
import { MultiProvider } from './provider'; import { MultiProvider } from './providers/MultiProvider';
import { ChainName } from './types'; import { ChainName } from './types';
export class Annotated<T extends TypedEvent> { export class Annotated<T extends TypedEvent> {
readonly domain: number; readonly domain: number;
readonly eventName?: string; readonly eventName?: string;
readonly event: T; readonly event: T;
readonly receipt: TransactionReceipt; readonly receipt: providers.TransactionReceipt;
constructor( constructor(
domain: number, domain: number,
receipt: TransactionReceipt, receipt: providers.TransactionReceipt,
event: T, event: T,
callerKnowsWhatTheyAreDoing = false, callerKnowsWhatTheyAreDoing = false,
) { ) {

@ -4,16 +4,14 @@ import sinon from 'sinon';
import { utils } from '@abacus-network/utils'; import { utils } from '@abacus-network/utils';
import { import { Chains } from '../consts/chains';
AbacusCore, import { AbacusCore } from '../core/AbacusCore';
Chains, import { CoreContracts } from '../core/contracts';
InterchainGasCalculator, import { MultiProvider } from '../providers/MultiProvider';
MultiProvider, import { MockProvider, MockTokenPriceGetter } from '../test/testUtils';
} from '../..'; import { TestChainNames } from '../types';
import { CoreContracts } from '../../src';
import { ParsedMessage } from '../../src/gas/calculator'; import { InterchainGasCalculator, ParsedMessage } from './calculator';
import { TestChainNames } from '../../src/types';
import { MockProvider, MockTokenPriceGetter } from '../utils';
const HANDLE_GAS = 100_000; const HANDLE_GAS = 100_000;
const SUGGESTED_GAS_PRICE = 10; const SUGGESTED_GAS_PRICE = 10;

@ -1,12 +1,14 @@
import { AbacusCore, MultiProvider, chainMetadata } from '..';
import { BigNumber, FixedNumber, ethers } from 'ethers'; import { BigNumber, FixedNumber, ethers } from 'ethers';
import { utils } from '@abacus-network/utils'; import { utils } from '@abacus-network/utils';
import { chainMetadata } from '../consts/chainMetadata';
import { AbacusCore } from '../core/AbacusCore';
import { MultiProvider } from '../providers/MultiProvider';
import { ChainName, Remotes } from '../types'; import { ChainName, Remotes } from '../types';
import { convertDecimalValue, mulBigAndFixed } from '../utils/number';
import { DefaultTokenPriceGetter, TokenPriceGetter } from './token-prices'; import { DefaultTokenPriceGetter, TokenPriceGetter } from './token-prices';
import { convertDecimalValue, mulBigAndFixed } from './utils';
/** /**
* A note on arithmetic: * A note on arithmetic:

@ -1,2 +0,0 @@
export { InterchainGasCalculator } from './calculator';
export { DefaultTokenPriceGetter, TokenPriceGetter } from './token-prices';

@ -1,6 +1,24 @@
export { AbacusApp } from './app'; export { AllChains, Chains } from './consts/chains';
export { chainMetadata } from './chain-metadata'; export { chainMetadata } from './consts/chainMetadata';
export { addSignerToConnection, chainConnectionConfigs } from './chains'; export { chainConnectionConfigs } from './consts/chainConnectionConfigs';
export { environments as coreEnvironments } from './consts/environments';
export {
ChainMap,
ChainName,
CompleteChainMap,
Connection,
NameOrDomain,
RemoteChainMap,
Remotes,
TestChainNames,
IChainConnection,
} from './types';
export { ChainNameToDomainId, DomainIdToChainName } from './domains';
export { AbacusApp } from './AbacusApp';
export { export {
AbacusAddresses, AbacusAddresses,
AbacusContracts, AbacusContracts,
@ -9,56 +27,77 @@ export {
connectContracts, connectContracts,
serializeContracts, serializeContracts,
} from './contracts'; } from './contracts';
export {
Annotated,
getEvents,
queryAnnotatedEvents,
TSContract,
} from './events';
export { BeaconProxyAddresses, ProxiedContract, ProxyAddresses } from './proxy';
export { Router, RouterContracts, RouterFactories } from './router';
export { ChainConnection } from './providers/ChainConnection';
export { MultiProvider } from './providers/MultiProvider';
export { RetryJsonRpcProvider, RetryProvider } from './providers/RetryProvider';
export { AbacusCore, CoreContractsMap } from './core/AbacusCore';
export { export {
AbacusCore,
AbacusLifecyleEvent,
AbacusMessage,
AbacusStatus,
AnnotatedDispatch,
AnnotatedLifecycleEvent,
CoreContracts, CoreContracts,
CoreContractsMap,
coreEnvironments,
coreFactories, coreFactories,
InboxContracts, InboxContracts,
MessageStatus,
OutboxContracts, OutboxContracts,
} from './core/contracts';
export {
AbacusLifecyleEvent,
AnnotatedDispatch,
AnnotatedLifecycleEvent,
} from './core/events';
export {
AbacusMessage,
AbacusStatus,
MessageStatus,
resolveDomain, resolveDomain,
resolveId, resolveId,
resolveNetworks, resolveNetworks,
} from './core'; } from './core/message';
export { RetryJsonRpcProvider, RetryProvider } from './ethers';
export { export {
Annotated, TestCoreApp,
getEvents, TestCoreContracts,
queryAnnotatedEvents, TestInboxContracts,
TSContract, TestOutboxContracts,
} from './events'; } from './core/TestCoreApp';
export { TestCoreDeployer } from './core/TestCoreDeployer';
export { InterchainGasCalculator } from './gas/calculator';
export { DefaultTokenPriceGetter, TokenPriceGetter } from './gas/token-prices';
export { AbacusAppChecker, Ownable } from './deploy/AbacusAppChecker';
export { CheckerViolation, EnvironmentConfig } from './deploy/types';
export { AbacusCoreDeployer } from './deploy/core/AbacusCoreDeployer';
export { AbacusCoreChecker } from './deploy/core/AbacusCoreChecker';
export { export {
DefaultTokenPriceGetter, CoreConfig,
InterchainGasCalculator, CoreViolationType,
TokenPriceGetter, ValidatorManagerConfig,
} from './gas'; ValidatorManagerViolation,
export { ChainConnection, IChainConnection, MultiProvider } from './provider'; ValidatorViolation,
export { BeaconProxyAddresses, ProxiedContract, ProxyAddresses } from './proxy'; ValidatorViolationType,
export { } from './deploy/core/types';
ConnectionClientConfig, export { AbacusDeployer } from './deploy/AbacusDeployer';
Router, export { UpgradeBeaconViolation } from './deploy/proxy';
RouterContracts, export { AbacusRouterDeployer } from './deploy/router/AbacusRouterDeployer';
RouterFactories, export { AbacusRouterChecker } from './deploy/router/AbacusRouterChecker';
} from './router'; export { RouterConfig } from './deploy/router/types';
export { getMultiProviderFromConfigAndSigner } from './deploy/utils';
export { ContractVerifier } from './deploy/verify/ContractVerifier';
export { export {
AllChains, ContractVerificationInput,
ChainMap, VerificationInput,
ChainName, } from './deploy/verify/types';
ChainNameToDomainId, export * as verificationUtils from './deploy/verify/utils';
Chains,
CompleteChainMap, export { objMap, objMapEntries, promiseObjAll } from './utils';
Connection, export * as utils from './utils';
DomainIdToChainName,
NameOrDomain,
RemoteChainMap,
Remotes,
TestChainNames,
} from './types';
export { objMap, objMapEntries, promiseObjAll, utils } from './utils';

@ -1,16 +1,7 @@
import { Debugger, debug } from 'debug'; import { Debugger, debug } from 'debug';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { ChainMap, ChainName } from './types'; import { IChainConnection } from '../types';
import { MultiGeneric, objMap } from './utils';
export interface IChainConnection {
provider: ethers.providers.Provider;
signer?: ethers.Signer;
overrides?: ethers.Overrides;
confirmations?: number;
blockExplorerUrl?: string;
}
export class ChainConnection { export class ChainConnection {
provider: ethers.providers.Provider; provider: ethers.providers.Provider;
@ -56,23 +47,3 @@ export class ChainConnection {
return response.wait(this.confirmations); return response.wait(this.confirmations);
} }
} }
export class MultiProvider<
Chain extends ChainName = ChainName,
> extends MultiGeneric<Chain, ChainConnection> {
constructor(chainConnectionConfigs: ChainMap<Chain, IChainConnection>) {
super(
objMap(
chainConnectionConfigs,
(_, connection) => new ChainConnection(connection),
),
);
}
getChainConnection(chain: Chain): ChainMap<Chain, ChainConnection>[Chain] {
return this.get(chain);
}
// This doesn't work on hardhat providers so we skip for now
// ready() {
// return Promise.all(this.values().map((dc) => dc.provider!.ready));
// }
}

@ -0,0 +1,24 @@
import { ChainMap, ChainName, IChainConnection } from '../types';
import { MultiGeneric, objMap } from '../utils';
import { ChainConnection } from './ChainConnection';
export class MultiProvider<
Chain extends ChainName = ChainName,
> extends MultiGeneric<Chain, ChainConnection> {
constructor(chainConnectionConfigs: ChainMap<Chain, IChainConnection>) {
super(
objMap(
chainConnectionConfigs,
(_, connection) => new ChainConnection(connection),
),
);
}
getChainConnection(chain: Chain): ChainMap<Chain, ChainConnection>[Chain] {
return this.get(chain);
}
// This doesn't work on hardhat providers so we skip for now
// ready() {
// return Promise.all(this.values().map((dc) => dc.provider!.ready));
// }
}

@ -2,22 +2,21 @@
// //
// Mostly taken from the removed version that was in ethers.js // Mostly taken from the removed version that was in ethers.js
// See: https://github.com/ethers-io/ethers.js/discussions/3006 // See: https://github.com/ethers-io/ethers.js/discussions/3006
import { BaseProvider, JsonRpcProvider } from '@ethersproject/providers';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { retryAsync } from '@abacus-network/utils/dist/src/utils'; import { utils } from '@abacus-network/utils';
export type RetryOptions = { export type RetryOptions = {
// The wait interval in between // The wait interval in between
interval: number; interval: number;
// Maximum number of times to rety // Maximum number of times to retry
retryLimit: number; retryLimit: number;
}; };
export class RetryProvider extends BaseProvider { export class RetryProvider extends ethers.providers.BaseProvider {
constructor( constructor(
readonly provider: BaseProvider, readonly provider: ethers.providers.BaseProvider,
readonly retryOptions: RetryOptions, readonly retryOptions: RetryOptions,
) { ) {
super(provider.getNetwork()); super(provider.getNetwork());
@ -27,7 +26,7 @@ export class RetryProvider extends BaseProvider {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
perform(method: string, params: any): Promise<any> { perform(method: string, params: any): Promise<any> {
return retryAsync( return utils.retryAsync(
() => this.provider.perform(method, params), () => this.provider.perform(method, params),
this.retryOptions.retryLimit, this.retryOptions.retryLimit,
this.retryOptions.interval, this.retryOptions.interval,
@ -36,16 +35,16 @@ export class RetryProvider extends BaseProvider {
} }
// Need this separate class for JsonRpcProvider to still expose `getSigner`, so will retry at the request level // Need this separate class for JsonRpcProvider to still expose `getSigner`, so will retry at the request level
export class RetryJsonRpcProvider extends JsonRpcProvider { export class RetryJsonRpcProvider extends ethers.providers.JsonRpcProvider {
constructor( constructor(
readonly provider: JsonRpcProvider, readonly provider: ethers.providers.JsonRpcProvider,
readonly retryOptions: RetryOptions, readonly retryOptions: RetryOptions,
) { ) {
super(provider.connection, provider.network); super(provider.connection, provider.network);
} }
async send(method: string, params: Array<any>): Promise<any> { async send(method: string, params: Array<any>): Promise<any> {
return retryAsync( return utils.retryAsync(
async () => this.provider.send(method, params), async () => this.provider.send(method, params),
this.retryOptions.retryLimit, this.retryOptions.retryLimit,
this.retryOptions.interval, this.retryOptions.interval,

@ -1,6 +1,6 @@
import { Contract } from 'ethers'; import { Contract } from 'ethers';
import { types } from '@abacus-network/utils'; import type { types } from '@abacus-network/utils';
import { Connection } from './types'; import { Connection } from './types';

@ -1,7 +1,7 @@
import { ethers } from 'ethers'; import type { ethers } from 'ethers';
import { Router } from '@abacus-network/app'; import { Router } from '@abacus-network/app';
import { types } from '@abacus-network/utils'; import type { types } from '@abacus-network/utils';
import { AbacusContracts, AbacusFactories } from './contracts'; import { AbacusContracts, AbacusFactories } from './contracts';

@ -0,0 +1,5 @@
{
"rules": {
"@typescript-eslint/explicit-module-boundary-types": ["off"]
}
}

@ -1,6 +1,6 @@
import { FixedNumber, ethers } from 'ethers'; import { FixedNumber, ethers } from 'ethers';
import { ChainMap, ChainName } from '../src'; import { ChainMap, ChainName } from '../types';
const MOCK_NETWORK = { const MOCK_NETWORK = {
name: 'MockNetwork', name: 'MockNetwork',

@ -1,52 +1,12 @@
import { ethers } from 'ethers'; import type { ethers } from 'ethers';
import { chainMetadata } from './chain-metadata'; import type { Chains } from './consts/chains';
/**
* RPC Pagination information for Polygon
*/
export interface Pagination {
blocks: number;
from: number;
}
/**
* Enumeration of Abacus supported chains
*/
export enum Chains { // must be string type to be used with Object.keys
arbitrum = 'arbitrum',
alfajores = 'alfajores',
bsc = 'bsc',
mumbai = 'mumbai',
kovan = 'kovan',
goerli = 'goerli',
fuji = 'fuji',
celo = 'celo',
ethereum = 'ethereum',
avalanche = 'avalanche',
optimism = 'optimism',
polygon = 'polygon',
bsctestnet = 'bsctestnet',
arbitrumrinkeby = 'arbitrumrinkeby',
optimismkovan = 'optimismkovan',
auroratestnet = 'auroratestnet',
test1 = 'test1',
test2 = 'test2',
test3 = 'test3',
}
export type ChainName = keyof typeof Chains; export type ChainName = keyof typeof Chains;
export type CompleteChainMap<Value> = Record<ChainName, Value>; export type CompleteChainMap<Value> = Record<ChainName, Value>;
export type ChainMap<Chain extends ChainName, Value> = Record<Chain, Value>; export type ChainMap<Chain extends ChainName, Value> = Record<Chain, Value>;
export type TestChainNames = 'test1' | 'test2' | 'test3'; export type TestChainNames = 'test1' | 'test2' | 'test3';
export const AllChains = Object.keys(Chains) as ChainName[];
export const DomainIdToChainName = Object.fromEntries(
AllChains.map((chain) => [chainMetadata[chain].id, chain]),
);
export const ChainNameToDomainId = Object.fromEntries(
AllChains.map((chain) => [chain, chainMetadata[chain].id]),
) as CompleteChainMap<number>;
export type NameOrDomain = ChainName | number; export type NameOrDomain = ChainName | number;
export type Remotes< export type Remotes<
@ -60,14 +20,12 @@ export type RemoteChainMap<
Value, Value,
> = Record<Remotes<Chain, LocalChain>, Value>; > = Record<Remotes<Chain, LocalChain>, Value>;
/**
* A Domain (and its characteristics)
*/
export type ChainMetadata = {
id: number;
finalityBlocks: number;
nativeTokenDecimals?: number;
paginate?: Pagination;
};
export type Connection = ethers.providers.Provider | ethers.Signer; export type Connection = ethers.providers.Provider | ethers.Signer;
export interface IChainConnection {
provider: ethers.providers.Provider;
signer?: ethers.Signer;
overrides?: ethers.Overrides;
confirmations?: number;
blockExplorerUrl?: string;
}

@ -1,142 +0,0 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { BytesLike, arrayify, hexlify } from '@ethersproject/bytes';
import { ethers } from 'ethers';
import { ChainMap, ChainName, Remotes } from './types';
export type Address = string;
/**
* Converts a 20-byte (or other length) ID to a 32-byte ID.
* Ensures that a bytes-like is 32 long. left-padding with 0s if not.
*
* @param data A string or array of bytes to canonize
* @returns A Uint8Array of length 32
*/
export function canonizeId(data: BytesLike): Uint8Array {
if (!data) throw new Error('Bad input. Undefined');
const buf = ethers.utils.arrayify(data);
if (buf.length > 32) {
throw new Error('Too long');
}
if (buf.length !== 20 && buf.length != 32) {
throw new Error('bad input, expect address or bytes32');
}
return ethers.utils.zeroPad(buf, 32);
}
/**
* Converts an Abacus ID of 20 or 32 bytes to the corresponding EVM Address.
*
* For 32-byte IDs this enforces the EVM convention of using the LAST 20 bytes.
*
* @param data The data to truncate
* @returns A 20-byte, 0x-prepended hex string representing the EVM Address
* @throws if the data is not 20 or 32 bytes
*/
export function evmId(data: BytesLike): Address {
const u8a = arrayify(data);
if (u8a.length === 32) {
return hexlify(u8a.slice(12, 32));
} else if (u8a.length === 20) {
return hexlify(u8a);
} else {
throw new Error(`Invalid id length. expected 20 or 32. Got ${u8a.length}`);
}
}
/**
* Sleep async for some time.
*
* @param ms the number of milliseconds to sleep
* @returns A delay promise
*/
export function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export class MultiGeneric<Chain extends ChainName, Value> {
constructor(protected readonly chainMap: ChainMap<Chain, Value>) {}
protected get(chain: Chain) {
return this.chainMap[chain];
}
protected set(chain: Chain, value: Value) {
this.chainMap[chain] = value;
}
chains = () => Object.keys(this.chainMap) as Chain[];
apply(fn: (n: Chain, dc: Value) => void) {
for (const chain of this.chains()) {
fn(chain, this.chainMap[chain]);
}
}
map<Output>(fn: (n: Chain, dc: Value) => Output) {
const entries: [Chain, Output][] = [];
const chains = this.chains();
for (const chain of chains) {
entries.push([chain, fn(chain, this.chainMap[chain])]);
}
return Object.fromEntries(entries) as Record<Chain, Output>;
}
remoteChains = <LocalChain extends Chain>(name: LocalChain) =>
this.chains().filter((key) => key !== name) as Remotes<Chain, LocalChain>[];
extendWithChain = <New extends Remotes<ChainName, Chain>>(
chain: New,
value: Value,
) =>
new MultiGeneric<New & Chain, Value>({
...this.chainMap,
[chain]: value,
});
knownChain = (chain: ChainName) => chain in this.chainMap;
}
export function inferChainMap<M>(map: M) {
return map as M extends ChainMap<infer Chain, infer Value>
? Record<Chain, Value>
: never;
}
export function objMapEntries<K extends string, I = any, O = any>(
obj: Record<K, I>,
func: (k: K, _: I) => O,
): [K, O][] {
return Object.entries<I>(obj).map(([k, v]) => [k as K, func(k as K, v)]);
}
// Map over the values of the object
export function objMap<K extends string, I = any, O = any>(
obj: Record<K, I>,
func: (k: K, _: I) => O,
) {
return Object.fromEntries<O>(objMapEntries<K, I, O>(obj, func)) as Record<
K,
O
>;
}
// promiseObjectAll :: {k: Promise a} -> Promise {k: a}
export const promiseObjAll = <K extends string, V>(object: {
[key in K]: Promise<V>;
}): Promise<Record<K, V>> => {
const promiseList = Object.entries(object).map(([name, promise]) =>
(promise as Promise<V>).then((result) => [name, result]),
);
return Promise.all(promiseList).then(Object.fromEntries);
};
export const utils = {
objMap,
promiseObjAll,
canonizeId,
evmId,
delay,
};

@ -0,0 +1,5 @@
{
"rules": {
"@typescript-eslint/explicit-module-boundary-types": ["off"]
}
}

@ -0,0 +1,44 @@
import { ChainMap, ChainName, Remotes } from '../types';
export class MultiGeneric<Chain extends ChainName, Value> {
constructor(protected readonly chainMap: ChainMap<Chain, Value>) {}
protected get(chain: Chain) {
return this.chainMap[chain];
}
protected set(chain: Chain, value: Value) {
this.chainMap[chain] = value;
}
chains = () => Object.keys(this.chainMap) as Chain[];
apply(fn: (n: Chain, dc: Value) => void) {
for (const chain of this.chains()) {
fn(chain, this.chainMap[chain]);
}
}
map<Output>(fn: (n: Chain, dc: Value) => Output) {
const entries: [Chain, Output][] = [];
const chains = this.chains();
for (const chain of chains) {
entries.push([chain, fn(chain, this.chainMap[chain])]);
}
return Object.fromEntries(entries) as Record<Chain, Output>;
}
remoteChains = <LocalChain extends Chain>(name: LocalChain) =>
this.chains().filter((key) => key !== name) as Remotes<Chain, LocalChain>[];
extendWithChain = <New extends Remotes<ChainName, Chain>>(
chain: New,
value: Value,
) =>
new MultiGeneric<New & Chain, Value>({
...this.chainMap,
[chain]: value,
});
knownChain = (chain: ChainName) => chain in this.chainMap;
}

@ -0,0 +1,41 @@
import { BytesLike, ethers } from 'ethers';
/**
* Converts a 20-byte (or other length) ID to a 32-byte ID.
* Ensures that a bytes-like is 32 long. left-padding with 0s if not.
*
* @param data A string or array of bytes to canonize
* @returns A Uint8Array of length 32
*/
export function canonizeId(data: BytesLike): Uint8Array {
if (!data) throw new Error('Bad input. Undefined');
const buf = ethers.utils.arrayify(data);
if (buf.length > 32) {
throw new Error('Too long');
}
if (buf.length !== 20 && buf.length != 32) {
throw new Error('bad input, expect address or bytes32');
}
return ethers.utils.zeroPad(buf, 32);
}
/**
* Converts an Abacus ID of 20 or 32 bytes to the corresponding EVM Address.
*
* For 32-byte IDs this enforces the EVM convention of using the LAST 20 bytes.
*
* @param data The data to truncate
* @returns A 20-byte, 0x-prepended hex string representing the EVM Address
* @throws if the data is not 20 or 32 bytes
*/
export function evmId(data: BytesLike): string {
const u8a = ethers.utils.arrayify(data);
if (u8a.length === 32) {
return ethers.utils.hexlify(u8a.slice(12, 32));
} else if (u8a.length === 20) {
return ethers.utils.hexlify(u8a);
} else {
throw new Error(`Invalid id length. expected 20 or 32. Got ${u8a.length}`);
}
}

@ -0,0 +1,6 @@
export * from './ids';
export * from './MultiGeneric';
export * from './number';
export * from './objects';
export * from './sets';
export * from './time';

@ -0,0 +1,29 @@
// TODO move to utils package
export function objMapEntries<K extends string, I = any, O = any>(
obj: Record<K, I>,
func: (k: K, _: I) => O,
): [K, O][] {
return Object.entries<I>(obj).map(([k, v]) => [k as K, func(k as K, v)]);
}
// Map over the values of the object
export function objMap<K extends string, I = any, O = any>(
obj: Record<K, I>,
func: (k: K, _: I) => O,
) {
return Object.fromEntries<O>(objMapEntries<K, I, O>(obj, func)) as Record<
K,
O
>;
}
// promiseObjectAll :: {k: Promise a} -> Promise {k: a}
export const promiseObjAll = <K extends string, V>(object: {
[key in K]: Promise<V>;
}): Promise<Record<K, V>> => {
const promiseList = Object.entries(object).map(([name, promise]) =>
(promise as Promise<V>).then((result) => [name, result]),
);
return Promise.all(promiseList).then(Object.fromEntries);
};

@ -0,0 +1,11 @@
// TODO move to utils package
// Returns a \ b
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#implementing_basic_set_operations
export function setDifference<T>(a: Set<T>, b: Set<T>) {
const diff = new Set(a);
for (const element of b) {
diff.delete(element);
}
return diff;
}

@ -0,0 +1,11 @@
// TODO move to utils package
/**
* Sleep async for some time.
*
* @param ms the number of milliseconds to sleep
* @returns A delay promise
*/
export function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

@ -1,7 +1,7 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { BigNumber, FixedNumber } from 'ethers'; import { BigNumber, FixedNumber } from 'ethers';
import { bigToFixed, fixedToBig, mulBigAndFixed } from '../src/gas/utils'; import { bigToFixed, fixedToBig, mulBigAndFixed } from './number';
describe('utils', () => { describe('utils', () => {
describe('bigToFixed', () => { describe('bigToFixed', () => {

@ -4,6 +4,5 @@
"outDir": "./dist/", "outDir": "./dist/",
"rootDir": "./src/" "rootDir": "./src/"
}, },
"exclude": ["./node_modules/", "./dist/", "./test/"], "include": ["./src/**/*.ts", "./src/*.d.ts"],
"include": ["./src/*.ts", "./src/**/*.ts"]
} }

@ -1,12 +1,12 @@
{ {
"name": "@abacus-network/utils", "name": "@abacus-network/utils",
"description": "General utilities for the Abacus network", "description": "General utilities for the Abacus network",
"version": "0.2.3", "version": "0.3.1",
"dependencies": { "dependencies": {
"chai": "^4.3.0",
"ethers": "^5.6.8" "ethers": "^5.6.8"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.3.0",
"prettier": "^2.4.1", "prettier": "^2.4.1",
"typescript": "^4.7.2" "typescript": "^4.7.2"
}, },
@ -22,6 +22,7 @@
"repository": "https://github.com/abacus-network/abacus-monorepo", "repository": "https://github.com/abacus-network/abacus-monorepo",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"clean": "rm -rf ./dist",
"check": "tsc --noEmit", "check": "tsc --noEmit",
"prettier": "prettier --write ./src" "prettier": "prettier --write ./src"
}, },

@ -1,9 +1,17 @@
import { arrayify, hexlify } from '@ethersproject/bytes'; import { ethers, utils } from 'ethers';
import { assert } from 'chai';
import { ethers } from 'ethers';
import { Address, Domain, HexString, ParsedMessage } from './types'; import { Address, Domain, HexString, ParsedMessage } from './types';
export function assert(predicate: any, errorMessage?: string) {
if (!predicate) {
throw new Error(errorMessage ?? 'Error');
}
}
export function deepEquals(v1: any, v2: any) {
return JSON.stringify(v1) === JSON.stringify(v2);
}
/* /*
* Gets the byte length of a hex string * Gets the byte length of a hex string
* *
@ -73,12 +81,12 @@ export const formatMessage = (
* @returns * @returns
*/ */
export function parseMessage(message: string): ParsedMessage { export function parseMessage(message: string): ParsedMessage {
const buf = Buffer.from(arrayify(message)); const buf = Buffer.from(utils.arrayify(message));
const origin = buf.readUInt32BE(0); const origin = buf.readUInt32BE(0);
const sender = hexlify(buf.slice(4, 36)); const sender = utils.hexlify(buf.slice(4, 36));
const destination = buf.readUInt32BE(36); const destination = buf.readUInt32BE(36);
const recipient = hexlify(buf.slice(40, 72)); const recipient = utils.hexlify(buf.slice(40, 72));
const body = hexlify(buf.slice(72)); const body = utils.hexlify(buf.slice(72));
return { origin, sender, destination, recipient, body }; return { origin, sender, destination, recipient, body };
} }
@ -112,8 +120,8 @@ export function sleep(ms: number): Promise<void> {
return new Promise<void>((resolve) => setTimeout(resolve, ms)); return new Promise<void>((resolve) => setTimeout(resolve, ms));
} }
// Retries an async function when it raises an exeption // Retries an async function when it raises an exception
// if all the tries fail it raises the last thrown exeption // if all the tries fail it raises the last thrown exception
export async function retryAsync<T>( export async function retryAsync<T>(
runner: () => T, runner: () => T,
attempts = 3, attempts = 3,

@ -5,12 +5,12 @@ __metadata:
version: 6 version: 6
cacheKey: 8 cacheKey: 8
"@abacus-network/app@0.2.3, @abacus-network/app@^0.2.3, @abacus-network/app@workspace:solidity/app": "@abacus-network/app@0.3.1, @abacus-network/app@workspace:solidity/app":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@abacus-network/app@workspace:solidity/app" resolution: "@abacus-network/app@workspace:solidity/app"
dependencies: dependencies:
"@abacus-network/core": ^0.2.3 "@abacus-network/core": 0.3.1
"@abacus-network/utils": ^0.2.3 "@abacus-network/utils": 0.3.1
"@nomiclabs/hardhat-ethers": ^2.0.1 "@nomiclabs/hardhat-ethers": ^2.0.1
"@nomiclabs/hardhat-waffle": ^2.0.1 "@nomiclabs/hardhat-waffle": ^2.0.1
"@openzeppelin/contracts-upgradeable": ^4.5.0 "@openzeppelin/contracts-upgradeable": ^4.5.0
@ -42,11 +42,11 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@abacus-network/core@^0.2.3, @abacus-network/core@workspace:solidity/core": "@abacus-network/core@0.3.1, @abacus-network/core@workspace:solidity/core":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@abacus-network/core@workspace:solidity/core" resolution: "@abacus-network/core@workspace:solidity/core"
dependencies: dependencies:
"@abacus-network/utils": ^0.2.3 "@abacus-network/utils": 0.3.1
"@nomiclabs/hardhat-ethers": ^2.0.1 "@nomiclabs/hardhat-ethers": ^2.0.1
"@nomiclabs/hardhat-waffle": ^2.0.1 "@nomiclabs/hardhat-waffle": ^2.0.1
"@openzeppelin/contracts": ^4.6.0 "@openzeppelin/contracts": ^4.6.0
@ -70,55 +70,14 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@abacus-network/deploy@^0.2.3, @abacus-network/deploy@workspace:typescript/deploy": "@abacus-network/helloworld@npm:0.3.1-beta0":
version: 0.0.0-use.local version: 0.3.1-beta0
resolution: "@abacus-network/deploy@workspace:typescript/deploy" resolution: "@abacus-network/helloworld@npm:0.3.1-beta0"
dependencies:
"@abacus-network/app": ^0.2.3
"@abacus-network/core": ^0.2.3
"@abacus-network/sdk": ^0.2.3
"@abacus-network/utils": ^0.2.3
"@types/debug": ^4.1.7
"@types/node": ^16.9.1
"@types/yargs": ^17.0.10
axios: ^0.21.3
debug: ^4.3.4
ethers: ^5.6.8
prettier: ^2.4.1
ts-node: ^10.8.0
typescript: ^4.7.2
yargs: ^17.4.1
languageName: unknown
linkType: soft
"@abacus-network/hardhat@workspace:typescript/hardhat":
version: 0.0.0-use.local
resolution: "@abacus-network/hardhat@workspace:typescript/hardhat"
dependencies: dependencies:
"@abacus-network/core": ^0.2.3 "@abacus-network/sdk": ^0.3.1-beta0
"@abacus-network/deploy": ^0.2.3
"@abacus-network/sdk": ^0.2.3
"@abacus-network/utils": ^0.2.3
"@nomiclabs/hardhat-ethers": ^2.0.5
"@nomiclabs/hardhat-waffle": ^2.0.2
ethereum-waffle: ^3.2.2
ethers: ^5.6.8
hardhat: ^2.8.4
prettier: ^2.4.1
ts-node: ^10.8.0
typescript: ^4.7.2
languageName: unknown
linkType: soft
"@abacus-network/helloworld@npm:0.2.4":
version: 0.2.4
resolution: "@abacus-network/helloworld@npm:0.2.4"
dependencies:
"@abacus-network/app": 0.2.3
"@abacus-network/sdk": 0.2.3
"@openzeppelin/contracts-upgradeable": ^4.6.0 "@openzeppelin/contracts-upgradeable": ^4.6.0
ethers: ^5.4.7 ethers: ^5.4.7
checksum: 0cc987e6d0f762ac520571ebd431d48de0519e33ba1dee9d90c2d95357baf096827f7e74908ea5ec5445e867a41120a7fbb6fd20465f8aa42669a6b5e18dc253 checksum: 21114292ef0eb5408405f217942524ef54e379e189120edae94a4a2d2739a5c21edbfd98210831b309ca9a0118640c7ae231d13179f13c503ed94714a91c3055
languageName: node languageName: node
linkType: hard linkType: hard
@ -127,10 +86,8 @@ __metadata:
resolution: "@abacus-network/infra@workspace:typescript/infra" resolution: "@abacus-network/infra@workspace:typescript/infra"
dependencies: dependencies:
"@abacus-network/celo-ethers-provider": ^0.1.0 "@abacus-network/celo-ethers-provider": ^0.1.0
"@abacus-network/core": ^0.2.3 "@abacus-network/helloworld": 0.3.1-beta0
"@abacus-network/deploy": ^0.2.3 "@abacus-network/sdk": 0.3.1
"@abacus-network/helloworld": 0.2.4
"@abacus-network/sdk": ^0.2.3
"@aws-sdk/client-iam": ^3.74.0 "@aws-sdk/client-iam": ^3.74.0
"@aws-sdk/client-kms": 3.48.0 "@aws-sdk/client-kms": 3.48.0
"@aws-sdk/client-s3": ^3.74.0 "@aws-sdk/client-s3": ^3.74.0
@ -170,19 +127,26 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@abacus-network/sdk@0.2.3, @abacus-network/sdk@^0.2.3, @abacus-network/sdk@workspace:typescript/sdk": "@abacus-network/sdk@0.3.1, @abacus-network/sdk@^0.3.1-beta0, @abacus-network/sdk@workspace:typescript/sdk":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@abacus-network/sdk@workspace:typescript/sdk" resolution: "@abacus-network/sdk@workspace:typescript/sdk"
dependencies: dependencies:
"@abacus-network/app": ^0.2.3 "@abacus-network/app": 0.3.1
"@abacus-network/celo-ethers-provider": ^0.1.0 "@abacus-network/celo-ethers-provider": ^0.1.0
"@abacus-network/core": ^0.2.3 "@abacus-network/core": 0.3.1
"@abacus-network/utils": ^0.2.3 "@abacus-network/utils": 0.3.1
"@nomiclabs/hardhat-ethers": ^2.0.5
"@nomiclabs/hardhat-waffle": ^2.0.2
"@types/debug": ^4.1.7
"@types/node": ^16.9.1 "@types/node": ^16.9.1
chai: ^4.3.6 chai: ^4.3.6
cross-fetch: ^3.1.5
debug: ^4.3.4
dotenv: ^10.0.0 dotenv: ^10.0.0
ethereum-waffle: ^3.2.2
ethers: ^5.6.8 ethers: ^5.6.8
fs: 0.0.1-security fs: 0.0.1-security
hardhat: ^2.8.4
mocha: ^9.2.2 mocha: ^9.2.2
prettier: ^2.4.1 prettier: ^2.4.1
sinon: ^13.0.2 sinon: ^13.0.2
@ -190,7 +154,7 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@abacus-network/utils@^0.2.3, @abacus-network/utils@workspace:typescript/utils": "@abacus-network/utils@0.3.1, @abacus-network/utils@workspace:typescript/utils":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@abacus-network/utils@workspace:typescript/utils" resolution: "@abacus-network/utils@workspace:typescript/utils"
dependencies: dependencies:
@ -4497,15 +4461,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"axios@npm:^0.21.3":
version: 0.21.4
resolution: "axios@npm:0.21.4"
dependencies:
follow-redirects: ^1.14.0
checksum: 44245f24ac971e7458f3120c92f9d66d1fc695e8b97019139de5b0cc65d9b8104647db01e5f46917728edfc0cfd88eb30fc4c55e6053eef4ace76768ce95ff3c
languageName: node
linkType: hard
"babel-code-frame@npm:^6.26.0": "babel-code-frame@npm:^6.26.0":
version: 6.26.0 version: 6.26.0
resolution: "babel-code-frame@npm:6.26.0" resolution: "babel-code-frame@npm:6.26.0"
@ -6347,6 +6302,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cross-fetch@npm:^3.1.5":
version: 3.1.5
resolution: "cross-fetch@npm:3.1.5"
dependencies:
node-fetch: 2.6.7
checksum: f6b8c6ee3ef993ace6277fd789c71b6acf1b504fd5f5c7128df4ef2f125a429e29cd62dc8c127523f04a5f2fa4771ed80e3f3d9695617f441425045f505cf3bb
languageName: node
linkType: hard
"cross-spawn@npm:^6.0.5": "cross-spawn@npm:^6.0.5":
version: 6.0.5 version: 6.0.5
resolution: "cross-spawn@npm:6.0.5" resolution: "cross-spawn@npm:6.0.5"
@ -8373,7 +8337,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"follow-redirects@npm:^1.12.1, follow-redirects@npm:^1.14.0": "follow-redirects@npm:^1.12.1":
version: 1.15.1 version: 1.15.1
resolution: "follow-redirects@npm:1.15.1" resolution: "follow-redirects@npm:1.15.1"
peerDependenciesMeta: peerDependenciesMeta:
@ -11902,7 +11866,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": "node-fetch@npm:2.6.7, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7":
version: 2.6.7 version: 2.6.7
resolution: "node-fetch@npm:2.6.7" resolution: "node-fetch@npm:2.6.7"
dependencies: dependencies:

Loading…
Cancel
Save