diff --git a/.env_example b/.env_example index 050a556..69244e4 100644 --- a/.env_example +++ b/.env_example @@ -1,4 +1,4 @@ -# W3GW : mandatory +# W3GW: mandatory W3GW_PRIVATE_KEYS=[] W3GW_SEED_PHRASE="" diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 11a1219..427102a 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -18,7 +18,6 @@ jobs: registry-url: https://registry.npmjs.org/ - run: npm install --global yarn - run: yarn install - - run: yarn ci - run: yarn publish env: NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/package.json b/package.json index 213e75b..90a15cd 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,28 @@ { "name": "web3-jsonrpc-gateway", - "version": "1.7.0", + "version": "1.7.9", "description": "A Web3 provider that handles accounts on its own but delegates chain queries to a 3rd party service", "main": "dist/bin/w3gw.js", "files": [ - "dist", + "dist/**", "package.json", "LICENSE", "README.md" ], "bin": { - "w3gw": "dist/bin/w3gw.js" + "w3gw": "dist/bin/w3gw.js", + "w3gw-ethers": "dist/bin/ethers/index.js", + "w3gw-infura": "dist/bin/ethers/infura.js", + "w3gw-celo": "dist/bin/celo/index.js", + "w3gw-conflux": "dist/bin/conflux/index.js", + "w3gw-reef": "dist/bin/reef/index.js" }, "scripts": { "ci": "yarn build && yarn lint && yarn test", "test": "jest --passWithNoTests", "lint": "prettier-standard --format", "build": "tsc --build src", - "prepare": "npm run build", + "prepare": "yarn build", "arbitrum:goerli": "cross-env-shell ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_LIMIT_FACTOR=1.0 ETHERS_GAS_PRICE_FACTOR=1.1 ETHERS_MOCK_FILTERS=true node dist/bin/ethers https://goerli-rollup.arbitrum.io/rpc 8517", "arbitrum:one": "cross-env-shell ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_LIMIT=2000000000000 ETHERS_GAS_LIMIT_FACTOR=1.0 ETHERS_GAS_PRICE_FACTOR=1.0 ETHERS_MOCK_FILTERS=true node dist/bin/ethers https://arb1.arbitrum.io/rpc 9517", "avalanche:mainnet": "cross-env-shell ETHERS_ALWAYS_SYNCED=true ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_GAS_PRICE=150000000000 ETHERS_GAS_PRICE_FACTOR=1.25 ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_MOCK_FILTERS=true node dist/bin/ethers https://api.avax.network/ext/bc/C/rpc 9533", @@ -65,7 +70,7 @@ "okxchain:mainnet": "cross-env-shell EVM_CALL_INTERLEAVE_BLOCKS=0 ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_LIMIT=8000000 node dist/bin/ethers https://exchainrpc.okex.org 9528", "optimism:goerli": "cross-env-shell EVM_CALL_INTERLEAVE_BLOCKS=0 ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_LIMIT_FACTOR=1.5 ETHERS_MOCK_FILTERS=true node dist/bin/ethers https://optimism-goerli.infura.io/v3/$W3GW_PROVIDER_KEY 8520", "optimism:mainnet": "cross-env-shell EVM_CALL_INTERLEAVE_BLOCKS=0 ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_LIMIT_FACTOR=1.1 ETHERS_MOCK_FILTERS=true node dist/bin/ethers https://optimism-mainnet.infura.io/v3/$W3GW_PROVIDER_KEY 9520", - "polygon:goerli": "cross-env-shell EVM_CALL_INTERLEAVE_BLOCKS=0 ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_PRICE=100000000000 ETHERS_GAS_PRICE_FACTOR=1.1 ETHERS_GAS_LIMIT=25000000 node dist/bin/ethers https://polygon-mumbai-bor.publicnode.com 8535", + "polygon:goerli": "cross-env-shell EVM_CALL_INTERLEAVE_BLOCKS=0 ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_PRICE=100000000000 ETHERS_GAS_PRICE_FACTOR=1.1 ETHERS_GAS_LIMIT=25000000 node dist/bin/ethers https://endpoints.omniatech.io/v1/matic/mumbai/public 8535", "polygon:mainnet": "cross-env-shell EVM_CALL_INTERLEAVE_BLOCKS=0 ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_PRICE=300000000000 ETHERS_GAS_PRICE_FACTOR=1.1 ETHERS_GAS_LIMIT=25000000 node dist/bin/ethers https://polygon-mainnet.infura.io/v3/$W3GW_PROVIDER_KEY 9535", "polygon:zkevm:goerli": "cross-env-shell ETHERS_ESTIMATE_GAS_PRICE=true ETHERS_ESTIMATE_GAS_LIMIT=true ETHERS_GAS_LIMIT=15000000 ETHERS_GAS_LIMIT_FACTOR=1.1 ETHERS_GAS_PRICE=100000000000 ETHERS_GAS_PRICE_FACTOR=1.1 node dist/bin/ethers https://rpc.public.zkevm-test.net 8512", "reef:testnet": "cross-env-shell node dist/bin/reef wss://rpc-testnet.reefscan.com/ws https://squid.subsquid.io/reef-explorer-testnet/graphql 8532", @@ -97,22 +102,14 @@ }, "homepage": "https://github.com/witnet/web3-jsonrpc-gateway#readme", "devDependencies": { - "@types/cors": "^2.8.10", - "@types/express": "^4.17.11", - "@types/node": "^15.6.1", - "dotenv": "^10.0.0", - "jest": "^27.0.1", - "prettier-standard": "^16.4.1", - "tslint": "^6.1.3", - "typescript": "^4.3.2" - }, - "dependencies": { "@celo-tools/celo-ethers-wrapper": "~0.1.1", "@celo/contractkit": "~2.0.0", "@reef-defi/evm-provider": "~1.0.10", + "@types/cors": "^2.8.10", + "@types/express": "^4.17.11", + "@types/node": "^20.3.1", "body-parser": "1.19.0", "cors": "2.8.5", - "cross-env": "7.0.3", "ethers": "~5.2.0", "express": "~4.17.1", "graphql": "^14.6.0", @@ -120,6 +117,14 @@ "js-conflux-sdk": "2.0.0", "json-rpc-2.0": "~0.2.16", "winston": "~3.7.2", + "dotenv": "^10.0.0", + "jest": "^27.0.1", + "prettier-standard": "^16.4.1", + "tslint": "^6.1.3", + "typescript": "^5.1.3" + }, + "dependencies": { + "cross-env": "7.0.3", "yarn": "^1.22.19" } } diff --git a/src/bin/w3gw.js b/src/bin/w3gw.js index 9c4b45a..9df2013 100644 --- a/src/bin/w3gw.js +++ b/src/bin/w3gw.js @@ -3,62 +3,98 @@ require('dotenv').config() const execSync = require('child_process').execSync const scripts = require('../../package.json').scripts -if (process.argv.length >= 3 && (process.env.W3GW_SEED_PHRASE || process.env.W3GW_PRIVATE_KEYS)) { +if (process.argv.length >= 3) { + // search for network and launch gateway, if found + let ecosystem for (var key in scripts) { - if (key === process.argv[2]) { - if (process.argv.length >= 4) { - // a specific JSONRPC provider has been specified in the command line: + if (key.indexOf(":") > -1 && key === process.argv[2]) { + if (process.env.W3GW_SEED_PHRASE || process.env.W3GW_PRIVATE_KEYS) { var cmdline = scripts[key].split(' ') - cmdline[cmdline.length - 2] = process.argv[3] - if (process.env.W3GW_PORT) { + // substitute "node path/to/bin" to "npx w3gw-bin" + var index = cmdline.findIndex(item => item === "node") + if (index > -1) cmdline[index] = "npx" + index = cmdline.findIndex(item => item.startsWith("dist/bin")) + if (index > -1) cmdline[index] = `w3gw-${cmdline[index].split("/").slice(-1)}` + // replace all references to $W3GW_PROVIDER_URL + cmdline = cmdline.map(item => { + if (item.indexOf("$W3GW_PROVIDER_KEY") > -1) { + if (!process.env.W3GW_PROVIDER_KEY) { + console.info() + console.info("Cannot launch", key, "gateway: the W3GW_PROVIDER_KEY envar must be set!") + process.exit(0) + } + return item.replaceAll("$W3GW_PROVIDER_KEY", process.env.W3GW_PROVIDER_KEY) + } else { + return item + } + }) + if (process.argv.length >= 4) { + // a specific JSONRPC provider has been specified in the command line: + cmdline[cmdline.length - 2] = process.argv[3] + if (process.env.W3GW_PORT) { + cmdline[cmdline.length - 1] = process.env.W3GW_PORT + } + // invoke subprocess + execSync( + 'yarn ' + .concat(cmdline.join(' '), ' ') + .concat(process.argv.slice(4).join(' ')), + { stdio: 'inherit' } + ) + } else if (process.env.W3GW_PROVIDER_URL) { + // the W3GW_PROVIDER_URL variable is set + cmdline[cmdline.length - 2] = process.env.W3GW_PROVIDER_URL + if (process.env.W3GW_PORT) { + cmdline[cmdline.length - 1] = process.env.W3GW_PORT + } + execSync( + 'yarn ' + .concat(cmdline.join(' '), ' ') + .concat(process.argv.slice(3).join(' ')), + { stdio: 'inherit' } + ) + } else if (process.env.W3GW_PORT) { + // the W3GW_PORT variable is set while W3GW_PROVIDER_URL is not cmdline[cmdline.length - 1] = process.env.W3GW_PORT + execSync( + 'yarn ' + .concat(cmdline.join(' '), ' ') + .concat(process.argv.slice(3).join(' ')), + { stdio: 'inherit' } + ) + } else { + execSync( + 'yarn ' + .concat(cmdline.join(' '), ' ') + .concat(process.argv.slice(3).join(' ')), + { stdio: 'inherit' } + ) } - execSync( - 'yarn ' - .concat(cmdline.join(' '), ' ') - .concat(process.argv.slice(4).join(' ')), - { stdio: 'inherit' } - ) - } else if (process.env.W3GW_PROVIDER_URL) { - // the W3GW_PROVIDER_URL variable is set - var cmdline = scripts[key].split(' ') - cmdline[cmdline.length - 2] = process.env.W3GW_PROVIDER_URL - if (process.env.W3GW_PORT) { - cmdline[cmdline.length - 1] = process.env.W3GW_PORT - } - execSync( - 'yarn ' - .concat(cmdline.join(' '), ' ') - .concat(process.argv.slice(3).join(' ')), - { stdio: 'inherit' } - ) - } else if (process.env.W3GW_PORT) { - // the W3GW_PORT variable is set while W3GW_PROVIDER_URL is not - var cmdline = scripts[key].split(' ') - cmdline[cmdline.length - 1] = process.env.W3GW_PORT - execSync( - 'yarn ' - .concat(cmdline.join(' '), ' ') - .concat(process.argv.slice(3).join(' ')), - { stdio: 'inherit' } - ) + process.exit(0) } else { - execSync( - 'yarn ' - .concat( - scripts[key].replace( - '$W3GW_PROVIDER_KEY', - process.env.W3GW_PROVIDER_KEY - ), - ' ' - ) - .concat(process.argv.slice(3).join(' ')), - { stdio: 'inherit' } - ) + console.info() + console.info("Cannot launch", key, "gateway !!") + console.info("Please, setup the W3GW_SEED_PHRASE environment variable, or add it to the .env file!") + process.exit(1) } - process.exit(0) + } else if (key.indexOf(":") && key.split(":")[0].toLowerCase() === process.argv[2].toLowerCase()) { + ecosystem = process.argv[2].toLowerCase() + break } } + // if parameter matched a known ecosystem, list available network within it + if (ecosystem) { + const header = `AVAILABLE NETWORKS ON ${ecosystem.toUpperCase()}` + console.info() + console.info(header) + console.info("=".repeat(header.length)) + for (var key in scripts) { + if (key.split(":")[0].toLowerCase() === ecosystem) { + console.info(' ', key) + } + } + process.exit(0) + } } console.info('Usage:') console.info() @@ -66,10 +102,13 @@ console.info( ' ', '$ ' .concat(process.argv[0], ' ') - .concat(process.argv[1], ' :') + .concat(process.argv[1], ' [[:] [custom-rpc-provider-url]]') ) console.info() -console.info('Supported values:') + +const header = "AVAILABLE NETWORKS" +console.info(" ", header) +console.info(" ", "=".repeat(header.length)) console.info() for (var key in scripts) { if (key.indexOf(':') > -1) { @@ -89,7 +128,7 @@ console.info( ) console.info() console.info( - 'Optionally, you can specify an ETH/JSONRPC endpoint different from the default one by setting:' + 'Optionally, you can specify a custom ETH/JSONRPC endpoint by setting:' ) console.info() console.info( @@ -98,4 +137,4 @@ console.info( '\t=>', 'ETH/JSONRPC endpoint where to connect to.' ) -console.info() \ No newline at end of file +console.info() diff --git a/src/lib/conflux/wrapper.ts b/src/lib/conflux/wrapper.ts index de4f43b..2d7f105 100644 --- a/src/lib/conflux/wrapper.ts +++ b/src/lib/conflux/wrapper.ts @@ -254,8 +254,10 @@ export class WalletWrapper { ): Promise { let gasPrice: number | string if (this.estimateGasPrice) { - let gasPriceBI = await this.conflux.getGasPrice() - if (gasPriceBI > BigInt(this.conflux.defaultGasPrice)) { + let gasPriceBI: bigint = BigInt( + await (await this.conflux.getGasPrice()).toString(10) + ) + if (gasPriceBI > BigInt(this.conflux.defaultGasPrice.toString())) { let reason = `Estimated gas price exceeds threshold (${gasPriceBI} > ${this.conflux.defaultGasPrice})` throw { reason, diff --git a/src/lib/reef/server.ts b/src/lib/reef/server.ts index f9991fc..87260d1 100644 --- a/src/lib/reef/server.ts +++ b/src/lib/reef/server.ts @@ -118,7 +118,7 @@ export class WalletMiddlewareServer { body: { error: { code: -32015, - message: exception.toString(), + message: exception.toString() } } } @@ -135,12 +135,12 @@ export class WalletMiddlewareServer { : { error: { code: exception.code || -32099, - message: `"${message}"`, + message: `"${message}"` } }) body = typeof body !== 'string' ? JSON.stringify(body) : body try { - response = { ...header, error: JSON.parse(body).error } + response = { ...header, error: JSON.parse(body).error } } catch (e) { logger.log({ level: 'error', diff --git a/src/lib/reef/wrapper.ts b/src/lib/reef/wrapper.ts index f39d1aa..380fbbb 100644 --- a/src/lib/reef/wrapper.ts +++ b/src/lib/reef/wrapper.ts @@ -272,7 +272,7 @@ export class WalletWrapper { } ` let res = null - let data = await request(this.graphUrl, queryBlock) + let data: any = await request(this.graphUrl, queryBlock) const block = data?.blocks[0] if (block?.height) { const queryBlockExtrinsics = gql` @@ -369,7 +369,7 @@ export class WalletWrapper { socket, message: `=> querying data to ${this.graphUrl} ...` }) - const data = await request(this.graphUrl, query) + const data: any = await request(this.graphUrl, query) const extrinsic = data?.extrinsics[0] let res = null if (extrinsic && extrinsic.block.finalized) { @@ -446,8 +446,8 @@ export class WalletWrapper { } } ` - const data = await request(this.graphUrl, query) - const extrinsic = data?.extrinsics[0] + const data: any = await request(this.graphUrl, query) + const extrinsic: any = data?.extrinsics[0] let res = null if (extrinsic && extrinsic.block.finalized) { const logsQuery = gql` @@ -467,7 +467,7 @@ export class WalletWrapper { } } ` - const logsData = await request(this.graphUrl, logsQuery) + const logsData: any = await request(this.graphUrl, logsQuery) const events: any[] = logsData?.evmEvents try { const gas = BigNumber.from(extrinsic.signedData.fee.weight) diff --git a/src/tsconfig.json b/src/tsconfig.json index f2a1833..d055026 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -20,6 +20,7 @@ "noUnusedLocals": true /* Report errors on unused locals. */, "noUnusedParameters": true /* Report errors on unused parameters. */, "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "useUnknownInCatchVariables": true, "baseUrl": "../" /* Base directory to resolve non-absolute module names. */, "typeRoots": [ "./node_modules/@types", diff --git a/tsconfig.json b/tsconfig.json index 35156cc..b070a8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,8 @@ "rootDir": ".", "outDir": ".", "resolveJsonModule": true, - "composite": true + "composite": true, + "useUnknownInCatchVariables": true }, "files": ["package.json"] }