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 { AbacusApp } from '../app'; |
||||
import { AbacusApp } from '../AbacusApp'; |
||||
import { environments } from '../consts/environments'; |
||||
import { buildContracts } from '../contracts'; |
||||
import { MultiProvider } from '../provider'; |
||||
import { MultiProvider } from '../providers/MultiProvider'; |
||||
import { ConnectionClientConfig } from '../router'; |
||||
import { ChainMap, ChainName, Remotes } from '../types'; |
||||
import { objMap } from '../utils'; |
||||
|
||||
import { CoreContracts, coreFactories } from './contracts'; |
||||
import { environments } from './environments'; |
||||
|
||||
export type CoreEnvironment = keyof typeof environments; |
||||
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 { TestCoreApp } from '../src/TestCoreApp'; |
||||
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 '@nomiclabs/hardhat-ethers'; |
||||
import '@nomiclabs/hardhat-waffle'; |
||||
import { expect } from 'chai'; |
||||
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 localDomain = chainMetadata[localChain].id; |
||||
const remoteChain = 'test2'; |
||||
const remoteDomain = chainMetadata[remoteChain].id; |
||||
const message = '0xdeadbeef'; |
||||
|
||||
describe('TestCoreDeploy', async () => { |
||||
describe('TestCoreDeployer', async () => { |
||||
let abacus: TestCoreApp, localOutbox: TestOutbox, remoteOutbox: TestOutbox; |
||||
|
||||
beforeEach(async () => { |
||||
const [signer] = await ethers.getSigners(); |
||||
const multiProvider = hardhatMultiProvider(ethers.provider, signer); |
||||
const deployer = new TestCoreDeploy(multiProvider); |
||||
abacus = await deployer.deployCore(); |
||||
|
||||
const config = { |
||||
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(); |
||||
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 { BeaconProxyAddresses, ChainName } from '@abacus-network/sdk'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import type { 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 { |
||||
type: BeaconProxyAddresses['kind']; |
@ -1,5 +1,6 @@ |
||||
import { ConnectionClientConfig } from '@abacus-network/sdk'; |
||||
import { types } from '@abacus-network/utils'; |
||||
import type { types } from '@abacus-network/utils'; |
||||
|
||||
import type { ConnectionClientConfig } from '../../router'; |
||||
|
||||
export type OwnableConfig = { |
||||
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 { |
||||
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 } from 'ethers'; |
||||
import { ethers, utils } from 'ethers'; |
||||
|
||||
import { ContractVerificationInput } from './types'; |
||||
|
||||
export function formatFunctionArguments(fragment: Fragment, args: any[]) { |
||||
export function formatFunctionArguments( |
||||
fragment: utils.Fragment, |
||||
args: any[], |
||||
): any { |
||||
const params = Object.fromEntries( |
||||
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 { ChainMap, ChainName } from '../src'; |
||||
import { ChainMap, ChainName } from '../types'; |
||||
|
||||
const MOCK_NETWORK = { |
||||
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 { BigNumber, FixedNumber } from 'ethers'; |
||||
|
||||
import { bigToFixed, fixedToBig, mulBigAndFixed } from '../src/gas/utils'; |
||||
import { bigToFixed, fixedToBig, mulBigAndFixed } from './number'; |
||||
|
||||
describe('utils', () => { |
||||
describe('bigToFixed', () => { |
Loading…
Reference in new issue