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 publishpull/670/head v0.3.1
parent
0644e4b22d
commit
86f280e826
@ -1,3 +0,0 @@ |
|||||||
node_modules/ |
|
||||||
dist/ |
|
||||||
*.swo |
|
@ -1,2 +0,0 @@ |
|||||||
**/helm |
|
||||||
|
|
@ -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"] |
|
||||||
} |
|
@ -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,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,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,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; |
@ -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,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,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,2 +0,0 @@ |
|||||||
export { InterchainGasCalculator } from './calculator'; |
|
||||||
export { DefaultTokenPriceGetter, TokenPriceGetter } from './token-prices'; |
|
@ -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));
|
||||||
|
// }
|
||||||
|
} |
@ -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,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', () => { |
Loading…
Reference in new issue