diff --git a/.changeset/brave-spies-own.md b/.changeset/brave-spies-own.md new file mode 100644 index 000000000..36d7f4a36 --- /dev/null +++ b/.changeset/brave-spies-own.md @@ -0,0 +1,5 @@ +--- +'@celo/dev-utils': patch +--- + +Bump web3 to 1.10.4 diff --git a/.changeset/friendly-penguins-happen.md b/.changeset/friendly-penguins-happen.md new file mode 100644 index 000000000..18af2ee23 --- /dev/null +++ b/.changeset/friendly-penguins-happen.md @@ -0,0 +1,8 @@ +--- +'@celo/network-utils': patch +'@celo/contractkit': patch +'@celo/explorer': patch +'@celo/celocli': patch +--- + +Bump Cross Fetch to fix security vulnerability diff --git a/.changeset/lemon-doors-rescue.md b/.changeset/lemon-doors-rescue.md new file mode 100644 index 000000000..76e89b111 --- /dev/null +++ b/.changeset/lemon-doors-rescue.md @@ -0,0 +1,19 @@ +--- +'@celo/wallet-hsm-azure': patch +'@celo/wallet-hsm-aws': patch +'@celo/wallet-hsm-gcp': patch +'@celo/wallet-ledger': patch +'@celo/wallet-remote': patch +'@celo/wallet-local': patch +'@celo/wallet-base': patch +'@celo/wallet-hsm': patch +'@celo/wallet-rpc': patch +'@celo/transactions-uri': patch +'@celo/network-utils': patch +'@celo/contractkit': patch +'@celo/connect': patch +'@celo/utils': patch +'@celo/celocli': patch +--- + +Bump web3-\* to 1.10.4 -- Some consumers may be forced to upgrade their web3 instance to the same version diff --git a/.gitignore b/.gitignore index 9b32745dc..11d572ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -71,7 +71,6 @@ packages/sdk/identity/* packages/sdk/encrypted-backup/* packages/protocol/* packages/celotool/* -packages/dev-utils/* packages/metadata-crawler/* packages/helm-charts/* # temp json file for deploy-sdks script diff --git a/.vscode/launch.json b/.vscode/launch.json index 79c6a7e4a..2dcffcacd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,38 +1,6 @@ { "version": "0.2.0", "configurations": [ - { - "name": "Debug PhoneNumberPrivacy Combiner Tests", - "type": "node", - "request": "launch", - "runtimeArgs": [ - "--inspect-brk", - "${workspaceRoot}/node_modules/.bin/jest", - "--rootDir", - "${workspaceFolder}/packages/phone-number-privacy", - "--runInBand", - "${workspaceFolder}/packages/phone-number-privacy/combiner/test/**", - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "port": 9229 - }, - { - "name": "Debug PhoneNumberPrivacy Signer Tests", - "type": "node", - "request": "launch", - "runtimeArgs": [ - "--inspect-brk", - "${workspaceRoot}/node_modules/.bin/jest", - "--rootDir", - "${workspaceFolder}/packages/phone-number-privacy/signer", - "--runInBand", - "${workspaceFolder}/packages/phone-number-privacy/signer/test/**", - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "port": 9229 - }, { "name": "Debug ContractKit Tests", "type": "node", @@ -41,9 +9,9 @@ "--inspect-brk", "${workspaceRoot}/node_modules/.bin/jest", "--rootDir", - "${workspaceFolder}/packages/contractkit", + "${workspaceFolder}/packages/sdk/contractkit", "--runInBand", - "${workspaceFolder}/packages/contractkit/src/**/*.test.ts", + "${workspaceFolder}/packages/sdk/contractkit/src/**/*.test.ts", ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", diff --git a/packages/cli/package.json b/packages/cli/package.json index 3cb3392bd..8e86066f7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -22,9 +22,9 @@ "node": ">=16" }, "scripts": { - "clean": "tsc -b . --clean", + "clean": "yarn run --top-level tsc -b . --clean", "dev": "yarn build && ts-node ./bin/dev.js", - "build": "tsc -b .", + "build": "yarn run --top-level tsc -b .", "docs": "./generate_docs.sh", "lint": "yarn run --top-level eslint -c .eslintrc.js ", "prepublish": "", @@ -61,7 +61,7 @@ "bip32": "3.1.0", "chalk": "^2.4.2", "command-exists": "^1.2.9", - "cross-fetch": "3.0.6", + "cross-fetch": "3.1.5", "debug": "^4.1.1", "ethers": "5", "fs-extra": "^8.1.0", @@ -69,12 +69,12 @@ "path": "^0.12.7", "prompts": "^2.0.1", "randombytes": "^2.0.1", - "web3": "1.10.0", - "web3-utils": "^1.10.0" + "web3": "1.10.4", + "web3-utils": "^1.10.4" }, "devDependencies": { "@celo/celo-devchain": "^7.0.0", - "@celo/dev-utils": "0.0.1-beta.1", + "@celo/dev-utils": "0.0.2", "@celo/typescript": "workspace:^", "@types/debug": "^4.1.4", "@types/fs-extra": "^8.0.0", @@ -83,7 +83,6 @@ "@types/ledgerhq__hw-transport-node-hid": "^4.22.2", "@types/node": "^18.7.16", "@types/prompts": "^1.1.1", - "@types/web3": "^1.0.18", "jest": "^29.0.2", "oclif": "^4.3.4", "prettier": "1.19.1", diff --git a/packages/cli/src/commands/account/authorize.test.ts b/packages/cli/src/commands/account/authorize.test.ts index d7a419f06..3c5bb899b 100644 --- a/packages/cli/src/commands/account/authorize.test.ts +++ b/packages/cli/src/commands/account/authorize.test.ts @@ -8,7 +8,6 @@ import Authorize from './authorize' import Register from './register' process.env.NO_SYNCCHECK = 'true' - testWithGanache('account:authorize cmd', (web3: Web3) => { test('can authorize vote signer', async () => { const accounts = await web3.eth.getAccounts() diff --git a/packages/cli/src/commands/account/claim-domain.ts b/packages/cli/src/commands/account/claim-domain.ts index 206fd850c..78098db38 100644 --- a/packages/cli/src/commands/account/claim-domain.ts +++ b/packages/cli/src/commands/account/claim-domain.ts @@ -12,7 +12,7 @@ export default class ClaimDomain extends ClaimCommand { } static args = ClaimCommand.args static examples = [ - 'claim-domain ~/metadata.json --domain test.com --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95', + 'claim-domain ~/metadata.json --domain example.com --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95', ] self = ClaimDomain async run() { diff --git a/packages/cli/src/commands/account/claim-storage.ts b/packages/cli/src/commands/account/claim-storage.ts index 7425eb96c..32fcffcf0 100644 --- a/packages/cli/src/commands/account/claim-storage.ts +++ b/packages/cli/src/commands/account/claim-storage.ts @@ -12,7 +12,7 @@ export default class ClaimStorage extends ClaimCommand { } static args = ClaimCommand.args static examples = [ - 'claim-storage ~/metadata.json --url http://test.com/myurl --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95', + 'claim-storage ~/metadata.json --url http://example.com/myurl --from 0x47e172F6CfB6c7D01C1574fa3E2Be7CC73269D95', ] self = ClaimStorage diff --git a/packages/cli/src/commands/account/claims.test.ts b/packages/cli/src/commands/account/claims.test.ts index 721b8c7ee..efe7ba8c8 100644 --- a/packages/cli/src/commands/account/claims.test.ts +++ b/packages/cli/src/commands/account/claims.test.ts @@ -52,7 +52,7 @@ testWithGanache('account metadata cmds', (web3: Web3) => { test('account:claim-domain cmd', async () => { generateEmptyMetadataFile() - const domain = 'test.com' + const domain = 'example.com' await testLocally(ClaimDomain, ['--from', account, '--domain', domain, emptyFilePath]) const metadata = await readFile() const claim = metadata.findClaim(ClaimTypes.DOMAIN) @@ -84,7 +84,7 @@ testWithGanache('account metadata cmds', (web3: Web3) => { '--from', account, '--url', - 'https://test.com', + 'https://example.com', ]) }) @@ -97,7 +97,13 @@ testWithGanache('account metadata cmds', (web3: Web3) => { it('cannot register metadata', async () => { await expect( - testLocally(RegisterMetadata, ['--force', '--from', account, '--url', 'https://test.com']) + testLocally(RegisterMetadata, [ + '--force', + '--from', + account, + '--url', + 'https://example.com', + ]) ).rejects.toThrow("Some checks didn't pass!") }) }) diff --git a/packages/cli/src/commands/releasecelo/set-account.ts b/packages/cli/src/commands/releasecelo/set-account.ts index a02f7579b..17c450f38 100644 --- a/packages/cli/src/commands/releasecelo/set-account.ts +++ b/packages/cli/src/commands/releasecelo/set-account.ts @@ -26,7 +26,7 @@ export default class SetAccount extends ReleaseGoldBaseCommand { static examples = [ 'set-account --contract 0x5719118266779B58D0f9519383A4A27aA7b829E5 --property name --value mywallet', 'set-account --contract 0x5719118266779B58D0f9519383A4A27aA7b829E5 --property dataEncryptionKey --value 0x041bb96e35f9f4b71ca8de561fff55a249ddf9d13ab582bdd09a09e75da68ae4cd0ab7038030f41b237498b4d76387ae878dc8d98fd6f6db2c15362d1a3bf11216', - 'set-account --contract 0x5719118266779B58D0f9519383A4A27aA7b829E5 --property metaURL --value www.test.com', + 'set-account --contract 0x5719118266779B58D0f9519383A4A27aA7b829E5 --property metaURL --value www.example.com', ] async run() { diff --git a/packages/dev-utils/.eslintrc.js b/packages/dev-utils/.eslintrc.js new file mode 100644 index 000000000..bfd2057be --- /dev/null +++ b/packages/dev-utils/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: '../../.eslintrc.js', +} diff --git a/packages/dev-utils/.gitignore b/packages/dev-utils/.gitignore new file mode 100644 index 000000000..7951405f8 --- /dev/null +++ b/packages/dev-utils/.gitignore @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md new file mode 100644 index 000000000..e61ba036a --- /dev/null +++ b/packages/dev-utils/README.md @@ -0,0 +1,3 @@ +# packages/dev-utils + +This is a `utils` package that is meant to be used as a devDependency. It's primary use case is to reuse the ganache setup currently present in `cli` and `contractkit`. Due to the way jest uses globalSetup, depending packages will still need to define their own setup/teardown files. diff --git a/packages/dev-utils/eslint.tsconfig.json b/packages/dev-utils/eslint.tsconfig.json new file mode 100644 index 000000000..fc8520e73 --- /dev/null +++ b/packages/dev-utils/eslint.tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json new file mode 100644 index 000000000..49c3903eb --- /dev/null +++ b/packages/dev-utils/package.json @@ -0,0 +1,39 @@ +{ + "name": "@celo/dev-utils", + "version": "0.0.2", + "description": "util package for celo packages that should only be a devDependency", + "main": "./lib/index.js", + "types": "./lib/index.d.ts", + "author": "Celo", + "license": "Apache-2.0", + "homepage": "https://github.com/celo-org/celo-monorepo/tree/master/packages/dev-utils", + "repository": "https://github.com/celo-org/celo-monorepo/tree/master/packages/dev-utils", + "keywords": [ + "celo" + ], + "files": [ + "lib/*" + ], + "scripts": { + "build": "yarn run --top-level tsc -b .", + "lint": "yarn run --top-level eslint -c .eslintrc.js ", + "prepack": "yarn build" + }, + "dependencies": { + "bignumber.js": "^9.0.0", + "fs-extra": "^8.1.0", + "ganache": "npm:@celo/ganache@7.8.0-unofficial.0", + "targz": "^1.0.1", + "tmp": "^0.1.0", + "web3": "1.10.4", + "web3-core-helpers": "1.10.4" + }, + "devDependencies": { + "@tsconfig/recommended": "^1.0.3", + "@types/fs-extra": "^8.1.0", + "@types/targz": "1.0.0" + }, + "engines": { + "node": ">=18.14.2" + } +} diff --git a/packages/dev-utils/src/describeEach.ts b/packages/dev-utils/src/describeEach.ts new file mode 100644 index 000000000..301ce4d18 --- /dev/null +++ b/packages/dev-utils/src/describeEach.ts @@ -0,0 +1,9 @@ +export interface TestCase { + label: string +} + +export function describeEach(testCases: T[], fn: (testCase: T) => void) { + for (const testCase of testCases) { + describe(testCase.label, () => fn(testCase)) + } +} diff --git a/packages/dev-utils/src/ganache-setup.ts b/packages/dev-utils/src/ganache-setup.ts new file mode 100644 index 000000000..c97afaf04 --- /dev/null +++ b/packages/dev-utils/src/ganache-setup.ts @@ -0,0 +1,131 @@ +import * as fs from 'fs-extra' +import * as ganache from 'ganache' +import * as path from 'path' +import * as targz from 'targz' + +/* eslint no-console: 0 */ // --> OFF +const MNEMONIC = 'concert load couple harbor equip island argue ramp clarify fence smart topic' +export const ACCOUNT_PRIVATE_KEYS = [ + '0xf2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d', + '0x5d862464fe9303452126c8bc94274b8c5f9874cbd219789b3eb2128075a76f72', + '0xdf02719c4df8b9b8ac7f551fcb5d9ef48fa27eef7a66453879f4d8fdc6e78fb1', + '0xff12e391b79415e941a94de3bf3a9aee577aed0731e297d5cfa0b8a1e02fa1d0', + '0x752dd9cf65e68cfaba7d60225cbdbc1f4729dd5e5507def72815ed0d8abc6249', + '0xefb595a0178eb79a8df953f87c5148402a224cdf725e88c0146727c6aceadccd', + '0x83c6d2cc5ddcf9711a6d59b417dc20eb48afd58d45290099e5987e3d768f328f', + '0xbb2d3f7c9583780a7d3904a2f55d792707c345f21de1bacb2d389934d82796b2', + '0xb2fd4d29c1390b71b8795ae81196bfd60293adf99f9d32a0aff06288fcdac55f', + '0x23cb7121166b9a2f93ae0b7c05bde02eae50d64449b2cbb42bc84e9d38d6cc89', +] +export const ACCOUNT_ADDRESSES = [ + '0x5409ED021D9299bf6814279A6A1411A7e866A631', + '0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb', + '0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84', + '0xE834EC434DABA538cd1b9Fe1582052B880BD7e63', + '0x78dc5D2D739606d31509C31d654056A45185ECb6', + '0xA8dDa8d7F5310E4A9E24F8eBA77E091Ac264f872', + '0x06cEf8E666768cC40Cc78CF93d9611019dDcB628', + '0x4404ac8bd8F9618D27Ad2f1485AA1B2cFD82482D', + '0x7457d5E02197480Db681D3fdF256c7acA21bDc12', + '0x91c987bf62D25945dB517BDAa840A6c661374402', +] + +export async function startGanache( + filePath: string, + datafile: string, + opts: { verbose?: boolean; from_targz?: boolean } = {} +) { + const chainCopyBase = process.env.GANACHE_CHAIN_DATA_PATH || path.resolve(filePath) + const chainCopy: string = path.resolve(path.join(chainCopyBase, 'tmp/copychain')) + console.info(chainCopy) + console.info(filePath, datafile) + const filenameWithPath: string = path.resolve(path.join(filePath, datafile)) + + // erases tmp chain + if (fs.existsSync(chainCopy)) { + console.info(`Removing old chain tmp folder: ${chainCopy}`) + fs.removeSync(chainCopy) + } + console.info(`Creating chain tmp folder: ${chainCopy}`) + fs.mkdirsSync(chainCopy) + + if (opts.from_targz) { + await decompressChain(filenameWithPath, chainCopy) + } else { + fs.copySync(filenameWithPath, chainCopy) + } + + return launchServer(opts, chainCopy) +} + +function launchServer(opts: { verbose?: boolean; from_targz?: boolean }, chain?: string) { + const logFn = opts.verbose + ? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + (...args: any[]) => console.info(...args) + : () => { + /* nothing */ + } + + const server = ganache.server({ + wallet: { mnemonic: MNEMONIC, defaultBalance: 1000000 }, + logging: { logger: { log: logFn } }, + database: { dbPath: chain }, + miner: { blockGasLimit: 20000000, defaultGasPrice: 0 }, + chain: { networkId: 1101, chainId: 1, allowUnlimitedContractSize: true, hardfork: 'istanbul' }, + }) + + server.listen(8545, (err: any) => { + if (err) { + throw err + } + }) + + async function stopGanache() { + return server.close() + } + + return { stopGanache } +} + +function decompressChain(tarPath: string, copyChainPath: string): Promise { + console.info('Decompressing chain') + return new Promise((resolve, reject) => { + targz.decompress({ src: tarPath, dest: copyChainPath }, (err) => { + if (err) { + console.error(err) + reject(err) + } else { + console.info('Chain decompressed') + resolve() + } + }) + }) +} + +export default function setup( + filePath: string, + datafile: string, + opts: { verbose?: boolean; from_targz?: boolean } = {} +) { + return startGanache(filePath, datafile, opts) + .then(({ stopGanache }) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ;(global as any).stopGanache = stopGanache + }) + .catch((err) => { + console.error('Error starting ganache') + console.error(err) + process.exit(1) + }) +} + +export function emptySetup(opts: { verbose?: boolean; from_targz?: boolean } = {}) { + try { + const { stopGanache } = launchServer(opts) + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ;(global as any).stopGanache = stopGanache + } catch (err) { + console.error(err) + process.exit(1) + } +} diff --git a/packages/dev-utils/src/ganache-teardown.ts b/packages/dev-utils/src/ganache-teardown.ts new file mode 100644 index 000000000..c0660ea14 --- /dev/null +++ b/packages/dev-utils/src/ganache-teardown.ts @@ -0,0 +1,10 @@ +export default function tearDown() { + try { + console.info('Stopping ganache') + // eslint-disable-next-line + return (global as any).stopGanache() + } catch (err) { + console.error('error stopping ganache') + console.error(err) + } +} diff --git a/packages/dev-utils/src/ganache-test.ts b/packages/dev-utils/src/ganache-test.ts new file mode 100644 index 000000000..673c02357 --- /dev/null +++ b/packages/dev-utils/src/ganache-test.ts @@ -0,0 +1,114 @@ +import Web3 from 'web3' +import { JsonRpcResponse } from 'web3-core-helpers' +import migrationOverride from './migration-override.json' + +export const NetworkConfig = migrationOverride + +export function jsonRpcCall(web3: Web3, method: string, params: any[]): Promise { + return new Promise((resolve, reject) => { + if (web3.currentProvider && typeof web3.currentProvider !== 'string') { + web3.currentProvider.send( + { + id: new Date().getTime(), + jsonrpc: '2.0', + method, + params, + }, + (err: Error | null, res?: JsonRpcResponse) => { + if (err) { + reject(err) + } else if (!res) { + reject(new Error('no response')) + } else if (res.error) { + reject( + new Error( + `Failed JsonRpcResponse: method: ${method} params: ${JSON.stringify( + params + )} error: ${JSON.stringify(res.error)}` + ) + ) + } else { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + resolve(res.result) + } + } + ) + } else { + reject(new Error('Invalid provider')) + } + }) +} + +export async function timeTravel(seconds: number, web3: Web3) { + await jsonRpcCall(web3, 'evm_increaseTime', [seconds]) + await jsonRpcCall(web3, 'evm_mine', []) +} + +export async function mineBlocks(blocks: number, web3: Web3) { + for (let i = 0; i < blocks; i++) { + await jsonRpcCall(web3, 'evm_mine', []) + } +} + +export function evmRevert(web3: Web3, snapId: string): Promise { + return jsonRpcCall(web3, 'evm_revert', [snapId]) +} + +export function evmSnapshot(web3: Web3) { + return jsonRpcCall(web3, 'evm_snapshot', []) +} + +export function testWithGanache(name: string, fn: (web3: Web3) => void) { + const web3 = new Web3('http://localhost:8545') + + describe(name, () => { + let snapId: string | null = null + + beforeEach(async () => { + if (snapId != null) { + await evmRevert(web3, snapId) + } + snapId = await evmSnapshot(web3) + }) + + afterAll(async () => { + if (snapId != null) { + await evmRevert(web3, snapId) + } + }) + + fn(web3) + }) +} + +/** + * Gets a contract address by parsing blocks and matching event signatures against the given event. + */ +export async function getContractFromEvent( + eventSignature: string, + web3: Web3, + filter?: { + expectedData?: string + index?: number + } +): Promise { + const logs = await web3.eth.getPastLogs({ + topics: [web3.utils.sha3(eventSignature)], + fromBlock: 'earliest', + toBlock: 'latest', + }) + if (logs.length === 0) { + throw Error(`Error: contract could not be found matching signature ${eventSignature}`) + } + const logIndex = filter?.index ?? 0 + if (!filter?.expectedData) { + return logs[logIndex].address + } + const filteredLogs = logs.filter((log) => log.data === filter.expectedData) + if (filteredLogs.length === 0) { + throw Error( + `Error: contract could not be found matching signature ${eventSignature} with data ${filter.expectedData}` + ) + } + return filteredLogs[logIndex ?? 0].address +} diff --git a/packages/dev-utils/src/matchers.ts b/packages/dev-utils/src/matchers.ts new file mode 100644 index 000000000..066e6a538 --- /dev/null +++ b/packages/dev-utils/src/matchers.ts @@ -0,0 +1,45 @@ +import BigNumber from 'bignumber.js' + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace jest { + interface Matchers { + toBeBigNumber(): R + toEqBigNumber(expected: BigNumber | string | number): R + } + } +} +jest.setTimeout(10000) + +expect.extend({ + toBeBigNumber(received: any) { + const pass = BigNumber.isBigNumber(received) + if (pass) { + return { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + message: () => `expected ${received.toJSON()} not to be BigNumber`, + pass: true, + } + } else { + return { + message: () => `expected ${received} to be BigNumber`, + pass: false, + } + } + }, + toEqBigNumber(received: BigNumber, _expected: BigNumber | string | number) { + const expected = new BigNumber(_expected) + const pass = expected.eq(received) + if (pass) { + return { + message: () => `expected ${received.toString()} not to equal ${expected.toString()}`, + pass: true, + } + } else { + return { + message: () => `expected ${received.toString()} to equal ${expected.toString()}`, + pass: false, + } + } + }, +}) diff --git a/packages/dev-utils/src/migration-override.json b/packages/dev-utils/src/migration-override.json new file mode 100644 index 000000000..213756a69 --- /dev/null +++ b/packages/dev-utils/src/migration-override.json @@ -0,0 +1,127 @@ +{ + "downtimeSlasher": { + "slashableDowntime": 60 + }, + "epochRewards": { + "frozen": false + }, + "exchange": { + "frozen": false, + "minimumReports": 1 + }, + "exchangeEUR": { + "frozen": false, + "minimumReports": 1 + }, + "goldToken": { + "frozen": false + }, + "governance": { + "dequeueFrequency": 30, + "queueExpiry": 1000, + "approvalStageDuration": 100, + "referendumStageDuration": 100, + "executionStageDuration": 100, + "minDeposit": 1, + "concurrentProposals": 5, + "skipTransferOwnership": false + }, + "governanceApproverMultiSig": { + "signatories": [ + "0x5409ed021d9299bf6814279a6a1411a7e866a631" + ], + "numRequiredConfirmations": 1 + }, + "grandaMento": { + "approver": "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "spread": 0.01, + "maxApprovalExchangeRateChange": 0.5, + "vetoPeriodSeconds": 10800 + }, + "oracles": { + "reportExpiry": 300 + }, + "reserve": { + "initialBalance": 100000000, + "otherAddresses": ["0x91c987bf62D25945dB517BDAa840A6c661374402"] + }, + "reserveSpenderMultiSig": { + "signatories": ["0x5409ed021d9299bf6814279a6a1411a7e866a631", "0x4404ac8bd8F9618D27Ad2f1485AA1B2cFD82482D"], + "numRequiredConfirmations": 2 + }, + "stableToken": { + "goldPrice": 1, + "initialBalances": { + "addresses": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0xE834EC434DABA538cd1b9Fe1582052B880BD7e63" + ], + "values": [ + "50000000000000000000000", + "50000000000000000000000", + "50000000000000000000000", + "50000000000000000000000" + ] + }, + "oracles": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0x06cEf8E666768cC40Cc78CF93d9611019dDcB628", + "0x7457d5E02197480Db681D3fdF256c7acA21bDc12" + ], + "frozen": false + }, + "stableTokenEUR": { + "goldPrice": 1, + "initialBalances": { + "addresses": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0xE834EC434DABA538cd1b9Fe1582052B880BD7e63" + ], + "values": [ + "50000000000000000000000", + "50000000000000000000000", + "50000000000000000000000", + "50000000000000000000000" + ] + }, + "oracles": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0x06cEf8E666768cC40Cc78CF93d9611019dDcB628", + "0x7457d5E02197480Db681D3fdF256c7acA21bDc12" + ], + "frozen": false + }, + "stableTokenBRL": { + "goldPrice": 1, + "initialBalances": { + "addresses": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0xE834EC434DABA538cd1b9Fe1582052B880BD7e63" + ], + "values": [ + "50000000000000000000000", + "50000000000000000000000", + "50000000000000000000000", + "50000000000000000000000" + ] + }, + "oracles": [ + "0x5409ED021D9299bf6814279A6A1411A7e866A631", + "0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84", + "0x06cEf8E666768cC40Cc78CF93d9611019dDcB628", + "0x7457d5E02197480Db681D3fdF256c7acA21bDc12" + ], + "frozen": false + }, + "validators": { + "commissionUpdateDelay": 3 + } +} diff --git a/packages/dev-utils/src/network.ts b/packages/dev-utils/src/network.ts new file mode 100644 index 000000000..480b875cd --- /dev/null +++ b/packages/dev-utils/src/network.ts @@ -0,0 +1,32 @@ +import { spawn, SpawnOptions } from 'child_process' + +export async function waitForPortOpen(host: string, port: number, seconds: number) { + const deadline = Date.now() + seconds * 1000 + do { + if (await isPortOpen(host, port)) { + return true + } + } while (Date.now() < deadline) + return false +} + +export async function isPortOpen(host: string, port: number) { + return (await execCmd('nc', ['-z', host, port.toString()], { silent: true })) === 0 +} + +async function execCmd(cmd: string, args: string[], options?: SpawnOptions & { silent?: boolean }) { + return new Promise((resolve, reject) => { + const { silent, ...spawnOptions } = options || { silent: false } + if (!silent) { + console.debug('$ ' + [cmd].concat(args).join(' ')) + } + const process = spawn(cmd, args, { ...spawnOptions, stdio: silent ? 'ignore' : 'inherit' }) + process.on('close', (code) => { + try { + resolve(code) + } catch (error) { + reject(error) + } + }) + }) +} diff --git a/packages/dev-utils/tsconfig.json b/packages/dev-utils/tsconfig.json new file mode 100644 index 000000000..a11e6de79 --- /dev/null +++ b/packages/dev-utils/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../typescript/tsconfig.library.json", + "compilerOptions": { + "resolveJsonModule": true, + "moduleResolution": "node16", + "declarationMap": true, + "module": "Node16", + "rootDir": "src", + "outDir": "lib", + "esModuleInterop": true, + "target": "es2020", + "strict": false, + "declaration": true, + }, + "include": ["src/**/*", "src/migration-override.json"] +} diff --git a/packages/docs/command-line-interface/exchange.md b/packages/docs/command-line-interface/exchange.md index 0cc2233f2..f44b64b73 100644 --- a/packages/docs/command-line-interface/exchange.md +++ b/packages/docs/command-line-interface/exchange.md @@ -6,7 +6,6 @@ Exchange Celo Dollars and CELO via Mento * [`celocli exchange:celo`](#celocli-exchangecelo) * [`celocli exchange:dollars`](#celocli-exchangedollars) * [`celocli exchange:euros`](#celocli-exchangeeuros) -* [`celocli exchange:gold`](#celocli-exchangegold) * [`celocli exchange:reals`](#celocli-exchangereals) * [`celocli exchange:show`](#celocli-exchangeshow) * [`celocli exchange:stable`](#celocli-exchangestable) @@ -104,39 +103,6 @@ EXAMPLES _See code: [src/commands/exchange/euros.ts](https://github.com/celo-org/developer-tooling/tree/master/packages/cli/src/commands/exchange/euros.ts)_ -## `celocli exchange:gold` - -Exchange CELO for StableTokens via Mento. *DEPRECATION WARNING* Use the "exchange:celo" command instead - -``` -USAGE - $ celocli exchange:gold --from --value [--globalHelp] - [--forAtLeast ] [--stableToken cUSD|cusd|cEUR|ceur|cREAL|creal] - -FLAGS - --forAtLeast=10000000000000000000000 [default: 0] Optional, the minimum - value of StableTokens to receive in - return - --from=0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d (required) The address with CELO to - exchange - --globalHelp View all available global flags - --stableToken=