diff --git a/.github/workflows/conventional-commits-check.yaml b/.github/workflows/conventional-commits-check.yaml new file mode 100644 index 00000000..555d5bcc --- /dev/null +++ b/.github/workflows/conventional-commits-check.yaml @@ -0,0 +1,13 @@ +name: Conventional Commits Check + +on: [pull_request] + +jobs: + check-conventional-commits: + name: Conventional Commits + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check Commit Conventions + uses: webiny/action-conventional-commits@v1.3.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 416db64e..a30210c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [8.16.0] 2025-06-19 + +### Added + +- added experimental networks + +### Changed + +- updated `iexec app init --tee` template to Scone v5.9 + ## [8.15.0] 2025-04-10 ### Added diff --git a/docs/classes/internal_.IExecContractsClient.md b/docs/classes/internal_.IExecContractsClient.md index f5b1da05..22cc0c53 100644 --- a/docs/classes/internal_.IExecContractsClient.md +++ b/docs/classes/internal_.IExecContractsClient.md @@ -44,9 +44,9 @@ Create a client for IExec contracts | Name | Type | Description | | :------ | :------ | :------ | | `args` | `Object` | - | -| `args.chainId` | `string` \| `number` | id of the chain to use (used to resolve IExec contract address) | +| `args.chainId` | `string` \| `number` | id of the chain | | `args.confirms?` | `number` | number of block to wait for transactions confirmation (default 1) | -| `args.hubAddress?` | `string` | override the IExec contract address to target a custom instance | +| `args.hubAddress` | `string` | IExec contract address | | `args.isNative?` | `boolean` | true if IExec contract use the chain native token | | `args.provider` | `Provider` | ethers Provider | | `args.signer?` | `Signer` | ethers Signer, required to sign transactions and messages | diff --git a/docs/interfaces/IExecConfigOptions.md b/docs/interfaces/IExecConfigOptions.md index 7a423f5f..cc07914c 100644 --- a/docs/interfaces/IExecConfigOptions.md +++ b/docs/interfaces/IExecConfigOptions.md @@ -6,6 +6,7 @@ ### Properties +- [allowExperimentalNetworks](IExecConfigOptions.md#allowexperimentalnetworks) - [bridgeAddress](IExecConfigOptions.md#bridgeaddress) - [bridgedNetworkConf](IExecConfigOptions.md#bridgednetworkconf) - [confirms](IExecConfigOptions.md#confirms) @@ -25,6 +26,16 @@ ## Properties +### allowExperimentalNetworks + +• `Optional` **allowExperimentalNetworks**: `boolean` + +if true allows using a provider connected to an experimental networks (default false) + +⚠️ experimental networks are networks on which the iExec's stack is partially deployed, experimental networks can be subject to instabilities or discontinuity. Access is provided without warranties. + +___ + ### bridgeAddress • `Optional` **bridgeAddress**: `string` diff --git a/docs/modules/utils.md b/docs/modules/utils.md index 8256fe17..3cd6ac77 100644 --- a/docs/modules/utils.md +++ b/docs/modules/utils.md @@ -183,6 +183,7 @@ const iexec = new IExec({ ethProvider }); | `host` | `string` | node RPC url | | `privateKey` | `string` | wallet private key | | `options?` | `Object` | - | +| `options.allowExperimentalNetworks?` | `boolean` | if true allows using a provider connected to an experimental networks (default false) ⚠️ experimental networks are networks on which the iExec's stack is partially deployed, experimental networks can be subject to instabilities or discontinuity. Access is provided without warranties. | | `options.gasPrice?` | `string` \| `number` \| `bigint` | gas price override | | `options.getTransactionCount?` | (`blockTag?`: `BlockTag`) => `Promise`<`number`\> | nonce override | | `options.providers` | [`ProviderOptions`](../interfaces/ProviderOptions.md) | providers options | diff --git a/package-lock.json b/package-lock.json index 53376405..9c3380b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "iexec", - "version": "8.15.0", + "version": "8.16.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "iexec", - "version": "8.15.0", + "version": "8.16.0", "license": "Apache-2.0", "dependencies": { "@ensdomains/ens-contracts": "^1.2.5", diff --git a/package.json b/package.json index 36d6bccd..39bf7638 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iexec", - "version": "8.15.0", + "version": "8.16.0", "description": "iExec SDK", "bin": { "iexec": "./dist/esm/cli/cmd/iexec.js" diff --git a/src/cli/cmd/iexec.js b/src/cli/cmd/iexec.js index e2c0d4a1..41226536 100755 --- a/src/cli/cmd/iexec.js +++ b/src/cli/cmd/iexec.js @@ -123,9 +123,7 @@ infoCmd const chain = await loadChain(opts.chain, { spinner }); const host = - chain.host === getChainDefaults({ id: chain.id }).host - ? 'default' - : chain.host; + chain.host === getChainDefaults(chain.id).host ? 'default' : chain.host; spinner.info(`Ethereum host: ${host}`); spinner.start(info.checking('iExec contracts info')); diff --git a/src/cli/utils/chains.js b/src/cli/utils/chains.js index 94c4a5dd..189ebed7 100644 --- a/src/cli/utils/chains.js +++ b/src/cli/utils/chains.js @@ -1,5 +1,5 @@ import Debug from 'debug'; -import { getChainDefaults } from '../../common/utils/config.js'; +import { getId, getChainDefaults } from '../../common/utils/config.js'; import IExecContractsClient from '../../common/utils/IExecContractsClient.js'; import { EnhancedWallet } from '../../common/utils/signers.js'; import { loadChainConf } from './fs.js'; @@ -8,27 +8,21 @@ import { getReadOnlyProvider } from '../../common/utils/providers.js'; const debug = Debug('iexec:chains'); -const CHAIN_ALIASES_MAP = { - 1: 'mainnet', - 134: 'bellecour', -}; - -const CHAIN_NAME_MAP = { - 1: { id: '1' }, - mainnet: { id: '1' }, - 134: { id: '134' }, - bellecour: { id: '134' }, -}; - const createChainFromConf = ( chainName, chainConf, - { bridgeConf, providerOptions, txOptions = {} } = {}, + { + bridgeConf, + providerOptions, + txOptions = {}, + allowExperimentalNetworks = false, + } = {}, ) => { try { const chain = { ...chainConf }; const provider = getReadOnlyProvider(chainConf.host, { providers: providerOptions, + allowExperimentalNetworks, }); chain.name = chainName; @@ -45,6 +39,7 @@ const createChainFromConf = ( chain.bridgedNetwork = { ...bridgeConf }; const bridgeProvider = getReadOnlyProvider(bridgeConf.host, { providers: providerOptions, + allowExperimentalNetworks, }); chain.bridgedNetwork.contracts = new IExecContractsClient({ provider: bridgeProvider, @@ -63,26 +58,34 @@ const createChainFromConf = ( }; export const loadChain = async ( - chainName, + chainNameOrId, { txOptions, spinner = Spinner() } = {}, ) => { try { const chainsConf = await loadChainConf(); - debug('chainsConf', chainsConf); + const { allowExperimentalNetworks } = chainsConf; const providerOptions = chainsConf.providers; let name; let loadedConf; - if (chainName) { - if (chainsConf.chains[chainName]) { - loadedConf = chainsConf.chains[chainName]; - name = chainName; + if (chainNameOrId) { + if (chainsConf.chains[chainNameOrId]) { + loadedConf = chainsConf.chains[chainNameOrId]; + name = chainNameOrId; } else { - const alias = CHAIN_ALIASES_MAP[chainName]; + const { name: alias } = getChainDefaults( + getId(chainNameOrId, { + allowExperimentalNetworks, + }), + { + allowExperimentalNetworks, + }, + ); if (alias && chainsConf.chains[alias]) { loadedConf = chainsConf.chains[alias]; name = alias; } - if (!name) throw Error(`Missing "${chainName}" chain in "chain.json"`); + if (!name) + throw Error(`Missing "${chainNameOrId}" chain in "chain.json"`); } } else if (chainsConf.default) { if (chainsConf.chains[chainsConf.default]) { @@ -96,11 +99,16 @@ export const loadChain = async ( throw Error('Missing chain parameter. Check your "chain.json" file'); const idConf = { - ...CHAIN_NAME_MAP[name], - ...(loadedConf.id && { id: loadedConf.id }), + id: + loadedConf.id || + getId(name, { + allowExperimentalNetworks, + }), }; - const defaultConf = getChainDefaults(idConf); + const defaultConf = getChainDefaults(idConf.id, { + allowExperimentalNetworks, + }); debug('loading chain', name); debug('loadedConf', loadedConf); @@ -120,7 +128,14 @@ export const loadChain = async ( if (chainsConf.chains[bridgedChainNameOrId]) { bridgeLoadedConf = chainsConf.chains[bridgedChainNameOrId]; } else { - const alias = CHAIN_ALIASES_MAP[bridgedChainNameOrId]; + const { name: alias } = getChainDefaults( + getId(bridgedChainNameOrId, { + allowExperimentalNetworks, + }), + { + allowExperimentalNetworks, + }, + ); if (alias && chainsConf.chains[alias]) { bridgeLoadedConf = chainsConf.chains[alias]; } @@ -128,10 +143,15 @@ export const loadChain = async ( throw Error(`Missing "${name}" chain in "chain.json"`); } const bridgeIdConf = { - ...CHAIN_NAME_MAP[bridgedChainNameOrId], - ...(bridgeLoadedConf.id && { id: bridgeLoadedConf.id }), + id: + bridgeLoadedConf.id || + getId(bridgedChainNameOrId, { + allowExperimentalNetworks, + }), }; - const bridgeDefaultConf = getChainDefaults(bridgeIdConf); + const bridgeDefaultConf = getChainDefaults(bridgeIdConf.id, { + allowExperimentalNetworks, + }); debug('bridgeLoadedConf', bridgeLoadedConf); debug('bridgeDefaultConf', defaultConf); bridgeConf = { @@ -150,6 +170,7 @@ export const loadChain = async ( bridgeConf, providerOptions, txOptions, + allowExperimentalNetworks, }); spinner.info(`Using chain ${name} [chainId: ${chain.id}]`); return chain; diff --git a/src/cli/utils/fs.js b/src/cli/utils/fs.js index 705acaaf..cfabb020 100644 --- a/src/cli/utils/fs.js +++ b/src/cli/utils/fs.js @@ -51,6 +51,7 @@ const chainConfSchema = () => const chainsConfSchema = () => object({ default: string(), + allowExperimentalNetworks: boolean().default(false), chains: object() .test(async (chainsOjb) => { await Promise.all( diff --git a/src/cli/utils/templates.js b/src/cli/utils/templates.js index 7f0ad015..48750eb9 100644 --- a/src/cli/utils/templates.js +++ b/src/cli/utils/templates.js @@ -25,16 +25,17 @@ export const sconeTeeApp = { owner: '0x0000000000000000000000000000000000000000', name: 'hello-world-scone', type: 'DOCKER', - multiaddr: 'iexechub/python-hello-world:8.0.0-sconify-5.7.5-v12-production', + multiaddr: + 'docker.io/iexechub/python-hello-world:8.0.0-sconify-5.9.1-v15-production', checksum: - '0xc9d25041956bfc6961d47294b528887879c26ad4110de17cf4b985ba51f93bd2', + '0x15de77fd7ac448028884256b3ab376e7d4560e9ef6acf0594ea0b3c031d5d395', mrenclave: { framework: 'SCONE', - version: 'v5', + version: 'v5.9', entrypoint: 'python /app/app.py', heapSize: 1073741824, fingerprint: - 'a5b171bd7b8ecd9724b07d901c21f2d0c02d64339a818562a8554c7f60dec2cb', + '2d4b9efd066d0bb058b8da79bf8551be7d244779bc41d03a12201a4004779609', }, }; diff --git a/src/common/generated/sdk/package.js b/src/common/generated/sdk/package.js index 9f3aa74f..7a2b0dd8 100644 --- a/src/common/generated/sdk/package.js +++ b/src/common/generated/sdk/package.js @@ -1,5 +1,5 @@ // this file is auto generated do not edit it export const name = "iexec"; -export const version = "8.15.0"; +export const version = "8.16.0"; export const description = "iExec SDK"; export default { name, version, description }; \ No newline at end of file diff --git a/src/common/market/order.js b/src/common/market/order.js index ba70a088..b71a41d1 100644 --- a/src/common/market/order.js +++ b/src/common/market/order.js @@ -783,7 +783,7 @@ const getMatchableVolume = async ( export const estimateMatchOrders = async ({ contracts = throwIfMissing(), - voucherHubAddress = throwIfMissing(), + voucherHubAddress, apporder, datasetorder = NULL_DATASETORDER, workerpoolorder, diff --git a/src/common/utils/IExecContractsClient.d.ts b/src/common/utils/IExecContractsClient.d.ts index c351ea9f..e44b05ea 100644 --- a/src/common/utils/IExecContractsClient.d.ts +++ b/src/common/utils/IExecContractsClient.d.ts @@ -14,13 +14,13 @@ export default class IExecContractsClient { */ signer?: Signer; /** - * id of the chain to use (used to resolve IExec contract address) + * id of the chain */ chainId: number | string; /** - * override the IExec contract address to target a custom instance + * IExec contract address */ - hubAddress?: string; + hubAddress: string; /** * if false set the gasPrice to 0 (default true) */ diff --git a/src/common/utils/IExecContractsClient.js b/src/common/utils/IExecContractsClient.js index 23a3e4ec..dd6bbe6a 100644 --- a/src/common/utils/IExecContractsClient.js +++ b/src/common/utils/IExecContractsClient.js @@ -1,7 +1,6 @@ import Debug from 'debug'; import { Contract } from 'ethers'; import { version as pocoVersion } from '../generated/@iexec/poco/package.js'; -import { networks as iexecProxyNetworks } from '../generated/@iexec/poco/ERC1538Proxy.js'; import iexecTokenDesc from '../generated/@iexec/poco/IexecInterfaceToken.js'; import iexecNativeDesc from '../generated/@iexec/poco/IexecInterfaceNative.js'; import appRegistryDesc from '../generated/@iexec/poco/AppRegistry.js'; @@ -20,17 +19,6 @@ const gasPriceByNetwork = { 134: 0n, }; -const getHubAddress = (chainId) => { - if ( - iexecProxyNetworks && - iexecProxyNetworks[chainId] && - iexecProxyNetworks[chainId].address - ) { - return iexecProxyNetworks[chainId].address; - } - throw Error(`Missing iExec contract default address for chain ${chainId}`); -}; - const getIsNative = (chainId) => nativeNetworks.includes(chainId); const getGasPriceOverride = (chainId) => gasPriceByNetwork[chainId]; @@ -76,19 +64,12 @@ const getContractsDescMap = (isNative) => ({ }, }); -const createClient = ({ - ethSigner, - ethProvider, - chainId, - globalHubAddress, - isNative, -}) => { +const createClient = ({ ethSigner, ethProvider, hubAddress, isNative }) => { const cachedAddresses = {}; + if (!hubAddress) throw Error('Missing iExec contract address'); const contractsDescMap = getContractsDescMap(isNative); - const hubAddress = globalHubAddress || getHubAddress(chainId); - const getContract = (objName, address) => { try { const { contractDesc } = contractsDescMap[objName]; @@ -184,6 +165,7 @@ class IExecContractsClient { } = {}) { const stringChainId = `${chainId}`; if (!provider) throw Error('missing provider key'); + if (!hubAddress) throw Error('missing hubAddress key'); if (!stringChainId) throw Error('missing chainId key'); if (!Number.isInteger(confirms) || confirms <= 0) throw Error('invalid confirms'); @@ -206,8 +188,7 @@ class IExecContractsClient { const client = createClient({ ethSigner: signer, ethProvider: provider, - chainId: stringChainId, - globalHubAddress: hubAddress, + hubAddress, isNative: native, }); diff --git a/src/common/utils/config.js b/src/common/utils/config.js index af39ea65..ad8e13b8 100644 --- a/src/common/utils/config.js +++ b/src/common/utils/config.js @@ -1,101 +1,139 @@ import { Network, EnsPlugin } from 'ethers'; import { TEE_FRAMEWORKS } from './constant.js'; import { address as voucherHubBellecourAddress } from '../generated/@iexec/voucher-contracts/deployments/bellecour/VoucherHubERC1967Proxy.js'; +import { networks as iexecProxyNetworks } from '../generated/@iexec/poco/ERC1538Proxy.js'; -const hostMap = { - 1: 'mainnet', - 134: 'https://bellecour.iex.ec', -}; - -const ensMap = { - 1: { - // registry: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - publicResolver: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - }, - 134: { - registry: '0x5f5B93fca68c9C79318d1F3868A354EE67D8c006', - publicResolver: '0x1347d8a1840A810B990d0B774A6b7Bb8A1bd62BB', - }, -}; - -const voucherHubMap = { - 134: voucherHubBellecourAddress, -}; - -const networkMap = { - 134: { +const networkConfigs = [ + { + id: 134, name: 'bellecour', - chainId: 134, - ensAddress: ensMap[134].registry, + hub: iexecProxyNetworks[134].address, + host: 'https://bellecour.iex.ec', + ensRegistry: '0x5f5B93fca68c9C79318d1F3868A354EE67D8c006', + ensPublicResolver: '0x1347d8a1840A810B990d0B774A6b7Bb8A1bd62BB', + sms: { + [TEE_FRAMEWORKS.SCONE]: 'https://sms.iex.ec', + [TEE_FRAMEWORKS.GRAMINE]: 'https://sms.gramine.v8-bellecour.iex.ec', + }, + resultProxy: 'https://result.v8-bellecour.iex.ec', + ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', + iexecGateway: 'https://api.market.v8-bellecour.iex.ec', + pocoSubgraph: 'https://thegraph.iex.ec/subgraphs/name/bellecour/poco-v5', + voucherHub: voucherHubBellecourAddress, + voucherSubgraph: + 'https://thegraph.iex.ec/subgraphs/name/bellecour/iexec-voucher', + bridge: { + contract: '0x188A4376a1D818bF2434972Eb34eFd57102a19b7', + bridgedChainId: '1', + }, + shouldRegisterNetwork: true, + isExperimental: false, }, -}; - -const hubMap = {}; - -const smsMap = { - 134: { - [TEE_FRAMEWORKS.SCONE]: 'https://sms.iex.ec', - [TEE_FRAMEWORKS.GRAMINE]: 'https://sms.gramine.v8-bellecour.iex.ec', - }, -}; - -const resultProxyMap = { - 134: 'https://result.v8-bellecour.iex.ec', -}; - -const bridgeMap = { - 1: { - contract: '0x4e55c9B8953AB1957ad0A59D413631A66798c6a2', - bridgedChainId: '134', + { + id: 1, + name: 'mainnet', + hub: iexecProxyNetworks[1].address, + host: 'mainnet', + ensRegistry: undefined, // use ethers default + ensPublicResolver: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + sms: undefined, // no protocol running + resultProxy: undefined, // no protocol running + ipfsGateway: undefined, // no protocol running + iexecGateway: undefined, // no protocol running + pocoSubgraph: undefined, // no protocol running + voucherHub: undefined, // no voucher + voucherSubgraph: undefined, // no voucher + bridge: { + contract: '0x4e55c9B8953AB1957ad0A59D413631A66798c6a2', + bridgedChainId: '134', + }, + shouldRegisterNetwork: false, + isExperimental: false, }, - 134: { - contract: '0x188A4376a1D818bF2434972Eb34eFd57102a19b7', - bridgedChainId: '1', + { + id: 421614, + name: 'arbitrum-sepolia-testnet', + hub: '0x14B465079537655E1662F012e99EBa3863c8B9E0', + host: 'https://sepolia-rollup.arbitrum.io/rpc', + ensRegistry: undefined, // TODO: not supported + ensPublicResolver: undefined, // TODO: not supported + sms: { + [TEE_FRAMEWORKS.SCONE]: 'https://sms.arbitrum-sepolia-testnet.iex.ec', + }, + resultProxy: undefined, // not exposed + ipfsGateway: 'https://ipfs-gateway.arbitrum-sepolia-testnet.iex.ec', + iexecGateway: 'https://api-market.arbitrum-sepolia-testnet.iex.ec', + pocoSubgraph: + 'https://thegraph.arbitrum-sepolia-testnet.iex.ec/api/subgraphs/id/2GCj8gzLCihsiEDq8cYvC5nUgK6VfwZ6hm3Wj8A3kcxz', + voucherHub: undefined, // no voucher + voucherSubgraph: undefined, // no voucher + bridge: {}, // no bridge + shouldRegisterNetwork: false, + isExperimental: true, }, +]; + +export const getId = (idOrName, { allowExperimentalNetworks = false } = {}) => + networkConfigs + .filter( + ({ isExperimental }) => allowExperimentalNetworks || !isExperimental, + ) + .find(({ id, name }) => idOrName === name || `${idOrName}` === `${id}`)?.id; + +export const getChainDefaults = ( + id, + { allowExperimentalNetworks = false } = {}, +) => { + const { + name, + host, + ensRegistry, + ensPublicResolver, + hub, + sms, + resultProxy, + iexecGateway, + ipfsGateway, + pocoSubgraph, + voucherHub, + voucherSubgraph, + bridge, + } = + networkConfigs + .filter( + ({ isExperimental }) => allowExperimentalNetworks || !isExperimental, + ) + .find((networkConfig) => `${id}` === `${networkConfig.id}`) || {}; + + return { + name, + host, + ensRegistry, + ensPublicResolver, + hub, + sms, + resultProxy, + iexecGateway, + ipfsGateway, + pocoSubgraph, + voucherHub, + voucherSubgraph, + bridge, + }; }; -const ipfsGatewayMap = { - 134: 'https://ipfs-gateway.v8-bellecour.iex.ec', -}; - -const iexecGatewayMap = { - 134: 'https://api.market.v8-bellecour.iex.ec', -}; - -const pocoSubgraphMap = { - 134: 'https://thegraph.iex.ec/subgraphs/name/bellecour/poco-v5', -}; - -const voucherSubgraphMap = { - 134: 'https://thegraph.iex.ec/subgraphs/name/bellecour/iexec-voucher', -}; - -const idMap = { - mainnet: 1, - bellecour: 134, -}; - -export const getId = (idOrName) => idMap[idOrName] || idOrName; - -export const getChainDefaults = ({ id }) => ({ - host: hostMap[id], - hub: hubMap[id], - sms: smsMap[id], - ensPublicResolver: ensMap[id] && ensMap[id].publicResolver, - voucherHub: voucherHubMap[id], - resultProxy: resultProxyMap[id], - ipfsGateway: ipfsGatewayMap[id], - iexecGateway: iexecGatewayMap[id], - pocoSubgraph: pocoSubgraphMap[id], - voucherSubgraph: voucherSubgraphMap[id], - bridge: bridgeMap[id], +// Register unknown networks and their ENS settings for the ethers library +networkConfigs.forEach((networkConfig) => { + if ( + networkConfig.shouldRegisterNetwork && + Network.from(networkConfig.id).name === 'unknown' && + networkConfig.ensRegistry + ) { + const network = new Network( + networkConfig.name, + networkConfig.id, + ).attachPlugin(new EnsPlugin(networkConfig.ensRegistry, networkConfig.id)); + Network.register(network.chainId, () => network); + Network.register(network.name, () => network); + } }); - -// register ethers unknown networks -if (Network.from(134).name === 'unknown') { - const bellecourNetwork = new Network(networkMap[134].name, 134).attachPlugin( - new EnsPlugin(ensMap[134].registry, 134), - ); - Network.register(bellecourNetwork.chainId, () => bellecourNetwork); - Network.register(bellecourNetwork.name, () => bellecourNetwork); -} diff --git a/src/common/utils/providers.js b/src/common/utils/providers.js index c3b61c1f..71c66096 100644 --- a/src/common/utils/providers.js +++ b/src/common/utils/providers.js @@ -1,11 +1,18 @@ import { getDefaultProvider, JsonRpcProvider } from 'ethers'; import { getChainDefaults, getId } from './config.js'; -export const getReadOnlyProvider = (host, options = {}) => { - const providerOptions = options.providers || {}; +export const getReadOnlyProvider = ( + host, + { providers = {}, allowExperimentalNetworks = false } = {}, +) => { let resolvedHost = host; - const defaults = getChainDefaults({ id: getId(host) }); + const defaults = getChainDefaults( + getId(host, { allowExperimentalNetworks }), + { + allowExperimentalNetworks, + }, + ); if (defaults && defaults.host) { resolvedHost = defaults.host; @@ -24,7 +31,7 @@ export const getReadOnlyProvider = (host, options = {}) => { }); } // API provider - const { quorum, ...providersOptionsRest } = providerOptions; + const { quorum, ...providersOptionsRest } = providers; // disable non configured providers when at least 1 is configured const apiProvidersList = [ 'alchemy', diff --git a/src/common/utils/signers.js b/src/common/utils/signers.js index 368401e3..588d19b8 100644 --- a/src/common/utils/signers.js +++ b/src/common/utils/signers.js @@ -100,9 +100,18 @@ export class BrowserProviderSignerAdapter extends AbstractSigner { export const getSignerFromPrivateKey = ( host, privateKey, - { gasPrice, getTransactionCount, providers } = {}, -) => - new EnhancedWallet(privateKey, getReadOnlyProvider(host, { providers }), { + { gasPrice, getTransactionCount, - }); + providers, + allowExperimentalNetworks = false, + } = {}, +) => + new EnhancedWallet( + privateKey, + getReadOnlyProvider(host, { providers, allowExperimentalNetworks }), + { + gasPrice, + getTransactionCount, + }, + ); diff --git a/src/common/voucher/voucher.js b/src/common/voucher/voucher.js index c90d7d6f..6befc0fb 100644 --- a/src/common/voucher/voucher.js +++ b/src/common/voucher/voucher.js @@ -15,7 +15,7 @@ const debug = Debug('iexec:voucher:voucher'); export const fetchVoucherContract = async ( contracts = throwIfMissing(), - voucherHubAddress = throwIfMissing(), + voucherHubAddress, userAddress, { voucherAddress } = {}, ) => { @@ -135,7 +135,7 @@ export const showUserVoucher = async ( export const authorizeRequester = async ( contracts = throwIfMissing(), - voucherHubAddress = throwIfMissing(), + voucherHubAddress, requester, ) => { try { @@ -176,7 +176,7 @@ export const authorizeRequester = async ( export const revokeRequesterAuthorization = async ( contracts = throwIfMissing(), - voucherHubAddress = throwIfMissing(), + voucherHubAddress, requester, ) => { try { diff --git a/src/common/voucher/voucherHub.js b/src/common/voucher/voucherHub.js index 7fdfbe98..61f11bdd 100644 --- a/src/common/voucher/voucherHub.js +++ b/src/common/voucher/voucherHub.js @@ -9,7 +9,7 @@ const debug = Debug('iexec:voucher:voucherHub'); export const fetchVoucherAddress = async ( contracts = throwIfMissing(), - voucherHubAddress = throwIfMissing(), + voucherHubAddress, owner, ) => { try { @@ -31,7 +31,7 @@ export const fetchVoucherAddress = async ( export const isVoucherAddress = async ( contracts = throwIfMissing(), - voucherHubAddress = throwIfMissing(), + voucherHubAddress, voucherAddress, ) => { try { diff --git a/src/lib/IExecConfig.d.ts b/src/lib/IExecConfig.d.ts index 04c4a045..d00e3abd 100644 --- a/src/lib/IExecConfig.d.ts +++ b/src/lib/IExecConfig.d.ts @@ -118,6 +118,12 @@ export interface IExecConfigOptions { * [ethers default provider](https://docs.ethers.io/v5/api/providers/#providers-getDefaultProvider) options */ providerOptions?: ProviderOptions | AnyRecord; + /** + * if true allows using a provider connected to an experimental networks (default false) + * + * ⚠️ experimental networks are networks on which the iExec's stack is partially deployed, experimental networks can be subject to instabilities or discontinuity. Access is provided without warranties. + */ + allowExperimentalNetworks?: boolean; } /** diff --git a/src/lib/IExecConfig.js b/src/lib/IExecConfig.js index c5a8252d..7e109d7a 100644 --- a/src/lib/IExecConfig.js +++ b/src/lib/IExecConfig.js @@ -33,6 +33,7 @@ export default class IExecConfig { voucherSubgraphURL, defaultTeeFramework, providerOptions, + allowExperimentalNetworks = false, } = {}, ) { if ( @@ -76,6 +77,7 @@ export default class IExecConfig { if (isRpcUrlProvider) { provider = getReadOnlyProvider(ethProvider, { providers: providerOptions, + allowExperimentalNetworks, }); } else if (isEthersAbstractSignerWithProvider) { provider = ethProvider.provider; @@ -130,7 +132,9 @@ export default class IExecConfig { const chainConfDefaultsPromise = (async () => { const { chainId } = await networkPromise; - return getChainDefaults({ id: chainId }); + return getChainDefaults(chainId, { + allowExperimentalNetworks, + }); })(); chainConfDefaultsPromise.catch((err) => { @@ -139,12 +143,19 @@ export default class IExecConfig { const contractsPromise = (async () => { const { chainId } = await networkPromise; + const chainConfDefaults = await chainConfDefaultsPromise; + const resolvedHubAddress = hubAddress || chainConfDefaults.hub; + if (!resolvedHubAddress) { + throw new ConfigurationError( + `hubAddress option not set and no default value for your chain ${chainId}`, + ); + } try { return new IExecContractsClient({ chainId, provider, signer, - hubAddress, + hubAddress: resolvedHubAddress, useGas, confirms, isNative, @@ -172,42 +183,38 @@ export default class IExecConfig { ); } const bridgedChainId = - bridgedNetworkConf.chainId !== undefined - ? bridgedNetworkConf.chainId - : chainConfDefaults.bridge && chainConfDefaults.bridge.bridgedChainId; + bridgedNetworkConf.chainId ?? chainConfDefaults.bridge?.bridgedChainId; if (!bridgedChainId) { throw new ConfigurationError( `Missing chainId in bridgedNetworkConf and no default value for your chain ${chainId}`, ); } - const bridgedChainConfDefaults = getChainDefaults({ - id: bridgedChainId, + const bridgedChainConfDefaults = getChainDefaults(bridgedChainId, { + allowExperimentalNetworks, }); const bridgedRpcUrl = - bridgedNetworkConf.rpcURL !== undefined - ? bridgedNetworkConf.rpcURL - : bridgedChainConfDefaults.host; + bridgedNetworkConf.rpcURL ?? bridgedChainConfDefaults.host; if (!bridgedRpcUrl) { throw new ConfigurationError( `Missing rpcURL in bridgedNetworkConf and no default value for bridged chain ${bridgedChainId}`, ); } const bridgedBridgeAddress = - bridgedNetworkConf.bridgeAddress !== undefined - ? bridgedNetworkConf.bridgeAddress - : bridgedChainConfDefaults.bridge && - bridgedChainConfDefaults.bridge.contract; + bridgedNetworkConf.bridgeAddress ?? + bridgedChainConfDefaults.bridge?.contract; if (!bridgedBridgeAddress) { throw new ConfigurationError( `Missing bridgeAddress in bridgedNetworkConf and no default value for bridged chain ${bridgedChainId}`, ); } + const bridgedHubAddress = + bridgedNetworkConf.hubAddress ?? bridgedChainConfDefaults.hub; const contracts = await contractsPromise; return { chainId: bridgedChainId, rpcURL: bridgedRpcUrl, isNative: !contracts.isNative, - hubAddress: bridgedNetworkConf.hubAddress, + hubAddress: bridgedHubAddress, bridgeAddress: bridgedBridgeAddress, }; })(); @@ -218,15 +225,22 @@ export default class IExecConfig { const bridgedContractsPromise = (async () => { const bridgedConf = await bridgedConfPromise; + const { hubAddress, chainId, isNative, rpcURL } = bridgedConf; + if (!hubAddress) { + throw new ConfigurationError( + `Missing hubAddress in bridgedNetworkConf and no default value for bridged chain ${chainId}`, + ); + } try { return new IExecContractsClient({ - chainId: bridgedConf.chainId, - provider: getReadOnlyProvider(bridgedConf.rpcURL, { + chainId, + provider: getReadOnlyProvider(rpcURL, { providers: providerOptions, + allowExperimentalNetworks, }), - hubAddress: bridgedConf.hubAddress, + hubAddress, confirms, - isNative: bridgedConf.isNative, + isNative, }); } catch (err) { throw new ConfigurationError( diff --git a/src/lib/utils.d.ts b/src/lib/utils.d.ts index b3f98aad..d4ab254c 100644 --- a/src/lib/utils.d.ts +++ b/src/lib/utils.d.ts @@ -42,6 +42,12 @@ export const getSignerFromPrivateKey: ( * providers options */ providers: ProviderOptions; + /** + * if true allows using a provider connected to an experimental networks (default false) + * + * ⚠️ experimental networks are networks on which the iExec's stack is partially deployed, experimental networks can be subject to instabilities or discontinuity. Access is provided without warranties. + */ + allowExperimentalNetworks?: boolean; }, ) => EnhancedWallet; diff --git a/test/lib/e2e/IExecAccountModule.test.js b/test/lib/e2e/IExecAccountModule.test.js index 610939bc..38216cb3 100644 --- a/test/lib/e2e/IExecAccountModule.test.js +++ b/test/lib/e2e/IExecAccountModule.test.js @@ -4,7 +4,7 @@ import { describe, test, expect } from '@jest/globals'; import { BN } from 'bn.js'; import { ONE_ETH, ONE_RLC, getTestConfig } from '../lib-test-utils.js'; import { - INFURA_PROJECT_ID, + DEFAULT_PROVIDER_OPTIONS, TEST_CHAINS, getRandomAddress, setBalance, @@ -47,7 +47,9 @@ describe('account', () => { test('expose bridged balances (mainnet) on bellecour', async () => { const iexec = new IExec( { ethProvider: 'bellecour' }, - { providerOptions: { infura: INFURA_PROJECT_ID } }, + { + providerOptions: DEFAULT_PROVIDER_OPTIONS, + }, ); const res = await iexec.account.checkBridgedBalance(getRandomAddress()); expect(res.stake).toBeInstanceOf(BN); @@ -56,7 +58,12 @@ describe('account', () => { describe('token chain', () => { test('expose bridged balances (bellecour) on mainnet', async () => { - const iexec = new IExec({ ethProvider: 'mainnet' }); + const iexec = new IExec( + { ethProvider: 'mainnet' }, + { + providerOptions: DEFAULT_PROVIDER_OPTIONS, + }, + ); const res = await iexec.account.checkBridgedBalance(getRandomAddress()); expect(res.stake).toBeInstanceOf(BN); diff --git a/test/lib/e2e/IExecConfig.test.js b/test/lib/e2e/IExecConfig.test.js index 955ad576..79f23fbf 100644 --- a/test/lib/e2e/IExecConfig.test.js +++ b/test/lib/e2e/IExecConfig.test.js @@ -21,11 +21,13 @@ import { TEE_FRAMEWORKS, getRandomAddress, getRandomWallet, + DEFAULT_PROVIDER_OPTIONS, } from '../../test-utils.js'; import '../../jest-setup.js'; import { utils, IExecConfig, errors } from '../../../src/lib/index.js'; import IExecContractsClient from '../../../src/common/utils/IExecContractsClient.js'; +import { getChainDefaults } from '../../../src/common/utils/config.js'; const iexecTestChain = TEST_CHAINS['bellecour-fork']; const unknownTestChain = TEST_CHAINS['custom-token-chain']; @@ -90,12 +92,7 @@ describe('[IExecConfig]', () => { const config = new IExecConfig( { ethProvider: 'mainnet' }, { - providerOptions: { - cloudflare: true, - alchemy: ALCHEMY_API_KEY, - etherscan: ETHERSCAN_API_KEY, - infura: INFURA_PROJECT_ID, - }, + providerOptions: DEFAULT_PROVIDER_OPTIONS, }, ); const { provider, signer, chainId } = @@ -133,6 +130,31 @@ describe('[IExecConfig]', () => { ); expect(createConfig).toThrow(errors.ConfigurationError); }); + describe('allowExperimentalNetworks', () => { + test('throw with experimental chains when allowExperimentalNetworks is not enabled', () => { + const createConfig = () => + new IExecConfig({ ethProvider: 'arbitrum-sepolia-testnet' }); + expect(createConfig).toThrow( + Error('Invalid ethProvider: Invalid provider host name or url'), + ); + expect(createConfig).toThrow(errors.ConfigurationError); + }); + test('allows experimental chains when allowExperimentalNetworks is enabled', async () => { + const config = new IExecConfig( + { ethProvider: 'arbitrum-sepolia-testnet' }, + { allowExperimentalNetworks: true }, + ); + const { provider, signer, chainId } = + await config.resolveContractsClient(); + expect(signer).toBeUndefined(); + expect(provider).toBeDefined(); + expect(provider).toBeInstanceOf(JsonRpcProvider); + expect(chainId).toBe('421614'); + const network = await provider.getNetwork(); + expect(network.chainId).toBe(421614n); + expect(network.name).toBe('arbitrum-sepolia'); + }); + }); }); describe('read-only ethProvider from network chainId', () => { @@ -140,12 +162,7 @@ describe('[IExecConfig]', () => { const config = new IExecConfig( { ethProvider: '1' }, { - providerOptions: { - cloudflare: true, - alchemy: ALCHEMY_API_KEY, - etherscan: ETHERSCAN_API_KEY, - infura: INFURA_PROJECT_ID, - }, + providerOptions: DEFAULT_PROVIDER_OPTIONS, }, ); const { provider, signer, chainId } = @@ -180,12 +197,7 @@ describe('[IExecConfig]', () => { const config = new IExecConfig( { ethProvider: 1 }, { - providerOptions: { - cloudflare: true, - alchemy: ALCHEMY_API_KEY, - etherscan: ETHERSCAN_API_KEY, - infura: INFURA_PROJECT_ID, - }, + providerOptions: DEFAULT_PROVIDER_OPTIONS, }, ); const { provider, signer, chainId } = @@ -230,6 +242,30 @@ describe('[IExecConfig]', () => { ); expect(createConfig).toThrow(errors.ConfigurationError); }); + describe('allowExperimentalNetworks', () => { + test('throw with experimental chains when allowExperimentalNetworks is not enabled', () => { + const createConfig = () => new IExecConfig({ ethProvider: 421614 }); + expect(createConfig).toThrow( + Error('Invalid ethProvider: Invalid provider host name or url'), + ); + expect(createConfig).toThrow(errors.ConfigurationError); + }); + test('allows experimental chains when allowExperimentalNetworks is enabled', async () => { + const config = new IExecConfig( + { ethProvider: 421614 }, + { allowExperimentalNetworks: true }, + ); + const { provider, signer, chainId } = + await config.resolveContractsClient(); + expect(signer).toBeUndefined(); + expect(provider).toBeDefined(); + expect(provider).toBeInstanceOf(JsonRpcProvider); + expect(chainId).toBe('421614'); + const network = await provider.getNetwork(); + expect(network.chainId).toBe(421614n); + expect(network.name).toBe('arbitrum-sepolia'); + }); + }); }); describe('read-only ethProvider with API keys', () => { @@ -385,6 +421,38 @@ describe('[IExecConfig]', () => { network.getPlugin('org.ethers.plugins.network.Ens').address, ).toBe('0x5f5B93fca68c9C79318d1F3868A354EE67D8c006'); }); + describe('allowExperimentalNetworks', () => { + const experimentalNetworkRpcUrl = getChainDefaults(421614, { + allowExperimentalNetworks: true, + }).host; + + test('fail resolving config with experimental chains when allowExperimentalNetworks is not enabled', async () => { + const config = new IExecConfig({ + ethProvider: experimentalNetworkRpcUrl, + }); + + await expect(config.resolveContractsClient()).rejects.toThrow( + Error( + 'hubAddress option not set and no default value for your chain 421614', + ), + ); + }); + test('allows experimental chains when allowExperimentalNetworks is enabled', async () => { + const config = new IExecConfig( + { ethProvider: experimentalNetworkRpcUrl }, + { allowExperimentalNetworks: true }, + ); + const { provider, signer, chainId } = + await config.resolveContractsClient(); + expect(signer).toBeUndefined(); + expect(provider).toBeDefined(); + expect(provider).toBeInstanceOf(JsonRpcProvider); + expect(chainId).toBe('421614'); + const network = await provider.getNetwork(); + expect(network.chainId).toBe(421614n); + expect(network.name).toBe('arbitrum-sepolia'); + }); + }); }); describe('signer provider from private key', () => { @@ -417,12 +485,7 @@ describe('[IExecConfig]', () => { 'mainnet', wallet.privateKey, { - providers: { - cloudflare: true, - infura: INFURA_PROJECT_ID, - alchemy: ALCHEMY_API_KEY, - etherscan: ETHERSCAN_API_KEY, - }, + providers: DEFAULT_PROVIDER_OPTIONS, }, ), }); @@ -463,6 +526,46 @@ describe('[IExecConfig]', () => { network.getPlugin('org.ethers.plugins.network.Ens').address, ).toBe(iexecTestChain.defaults.ensRegistryAddress); }); + describe('allowExperimentalNetworks', () => { + const experimentalNetworkRpcUrl = getChainDefaults(421614, { + allowExperimentalNetworks: true, + }).host; + + test('fail resolving config with experimental chains when allowExperimentalNetworks is not enabled', async () => { + const injectedProvider = new InjectedProvider( + experimentalNetworkRpcUrl, + getRandomWallet().privateKey, + ); + const config = new IExecConfig({ + ethProvider: injectedProvider, + }); + + await expect(config.resolveContractsClient()).rejects.toThrow( + Error( + 'hubAddress option not set and no default value for your chain 421614', + ), + ); + }); + test('allows experimental chains when allowExperimentalNetworks is enabled', async () => { + const injectedProvider = new InjectedProvider( + experimentalNetworkRpcUrl, + getRandomWallet().privateKey, + ); + const config = new IExecConfig( + { ethProvider: injectedProvider }, + { allowExperimentalNetworks: true }, + ); + const { provider, signer, chainId } = + await config.resolveContractsClient(); + expect(signer).toBeDefined(); + expect(provider).toBeDefined(); + expect(provider).toBeInstanceOf(BrowserProvider); + expect(chainId).toBe('421614'); + const network = await provider.getNetwork(); + expect(network.chainId).toBe(421614n); + expect(network.name).toBe('arbitrum-sepolia'); + }); + }); }); describe('ethers AbstractProvider', () => { @@ -551,7 +654,12 @@ describe('[IExecConfig]', () => { describe('bridged chain provider', () => { test('IExecConfig({ ethProvider: "bellecour" })', async () => { - const config = new IExecConfig({ ethProvider: 'bellecour' }); + const config = new IExecConfig( + { ethProvider: 'bellecour' }, + { + providerOptions: DEFAULT_PROVIDER_OPTIONS, + }, + ); const { provider, signer, chainId } = await config.resolveBridgedContractsClient(); expect(signer).toBeUndefined(); @@ -599,12 +707,7 @@ describe('[IExecConfig]', () => { const config = new IExecConfig( { ethProvider: 'mainnet' }, { - providerOptions: { - cloudflare: true, - alchemy: ALCHEMY_API_KEY, - etherscan: ETHERSCAN_API_KEY, - infura: INFURA_PROJECT_ID, - }, + providerOptions: DEFAULT_PROVIDER_OPTIONS, }, ); const { provider, signer, chainId } = @@ -697,7 +800,7 @@ describe('[IExecConfig]', () => { const promise = config.resolveContractsClient(); await expect(promise).rejects.toThrow( Error( - `Failed to create contracts client: Missing iExec contract default address for chain ${unknownTestChain.chainId}`, + `hubAddress option not set and no default value for your chain ${unknownTestChain.chainId}`, ), ); await expect(promise).rejects.toThrow(errors.ConfigurationError); diff --git a/test/lib/e2e/IExecEnsModule.test.js b/test/lib/e2e/IExecEnsModule.test.js index c1852b9d..795b76d0 100644 --- a/test/lib/e2e/IExecEnsModule.test.js +++ b/test/lib/e2e/IExecEnsModule.test.js @@ -14,7 +14,7 @@ import { NULL_ADDRESS, getId, getRandomAddress, - INFURA_PROJECT_ID, + DEFAULT_PROVIDER_OPTIONS, } from '../../test-utils.js'; import '../../jest-setup.js'; import { IExec } from '../../../src/lib/index.js'; @@ -34,11 +34,7 @@ describe('ens', () => { const iexec = new IExec( { ethProvider: 'mainnet' }, { - providerOptions: { - cloudflare: 1, - infura: INFURA_PROJECT_ID, - quorum: 1, - }, + providerOptions: DEFAULT_PROVIDER_OPTIONS, }, ); const balance = await iexec.wallet.checkBalances('core.v5.iexec.eth'); diff --git a/test/lib/e2e/IExecWalletModule.test.js b/test/lib/e2e/IExecWalletModule.test.js index 8a1cdb7b..078b3a6e 100644 --- a/test/lib/e2e/IExecWalletModule.test.js +++ b/test/lib/e2e/IExecWalletModule.test.js @@ -4,7 +4,10 @@ import { describe, test, expect } from '@jest/globals'; import { BN } from 'bn.js'; import { ONE_ETH, ONE_RLC, getTestConfig } from '../lib-test-utils.js'; import { + ALCHEMY_API_KEY, + ETHERSCAN_API_KEY, INFURA_PROJECT_ID, + DEFAULT_PROVIDER_OPTIONS, TEST_CHAINS, getRandomAddress, getRandomWallet, @@ -67,7 +70,9 @@ describe('wallet', () => { test('expose bridged balances (mainnet) on bellecour', async () => { const iexec = new IExec( { ethProvider: 'bellecour' }, - { providerOptions: { infura: INFURA_PROJECT_ID } }, + { + providerOptions: DEFAULT_PROVIDER_OPTIONS, + }, ); const address = getRandomAddress(); const balance = await iexec.wallet.checkBridgedBalances(address); @@ -79,7 +84,12 @@ describe('wallet', () => { }); describe('token chain', () => { test('expose bridged balances (bellecour) on mainnet', async () => { - const iexec = new IExec({ ethProvider: 'mainnet' }); + const iexec = new IExec( + { ethProvider: 'mainnet' }, + { + providerOptions: DEFAULT_PROVIDER_OPTIONS, + }, + ); const address = getRandomAddress(); const balance = await iexec.wallet.checkBridgedBalances(address); expect(balance.nRLC).toStrictEqual(new BN(0)); diff --git a/test/lib/e2e/utils.test.js b/test/lib/e2e/utils.test.js index d0fa8571..8638ac56 100644 --- a/test/lib/e2e/utils.test.js +++ b/test/lib/e2e/utils.test.js @@ -601,5 +601,21 @@ describe('utils', () => { ).wallet.checkBalances(NULL_ADDRESS), ).resolves.toBeDefined(); }); + test('allowExperimentalNetworks option allow creating signer connected to an experimental network', async () => { + expect(() => + utils.getSignerFromPrivateKey( + 'arbitrum-sepolia-testnet', + getRandomWallet().privateKey, + ), + ).toThrowError('Invalid provider host name or url'); + + const signer = utils.getSignerFromPrivateKey( + 'arbitrum-sepolia-testnet', + getRandomWallet().privateKey, + { allowExperimentalNetworks: true }, + ); + const nonce = await signer.getNonce(); + expect(nonce).toBe(0); + }); }); }); diff --git a/test/lib/unit/config.test.js b/test/lib/unit/config.test.js new file mode 100644 index 00000000..a0859b50 --- /dev/null +++ b/test/lib/unit/config.test.js @@ -0,0 +1,63 @@ +import { describe, test, expect } from '@jest/globals'; +import { Network } from 'ethers'; +import { getChainDefaults, getId } from '../../../src/common/utils/config.js'; + +describe('getId()', () => { + test('chain id as number returns id', () => { + expect(getId(134)).toBe(134); + }); + test('chain id as string returns id', () => { + expect(getId('134')).toBe(134); + }); + test('chain name returns id', () => { + expect(getId('bellecour')).toBe(134); + }); +}); + +describe('getChainDefaults', () => { + test('id 134 returns bellecour config', () => { + expect(getChainDefaults(134)).toEqual({ + bridge: { + bridgedChainId: '1', + contract: '0x188A4376a1D818bF2434972Eb34eFd57102a19b7', + }, + ensPublicResolver: '0x1347d8a1840A810B990d0B774A6b7Bb8A1bd62BB', + ensRegistry: '0x5f5B93fca68c9C79318d1F3868A354EE67D8c006', + host: 'https://bellecour.iex.ec', + hub: '0x3eca1B216A7DF1C7689aEb259fFB83ADFB894E7f', + iexecGateway: 'https://api.market.v8-bellecour.iex.ec', + ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', + name: 'bellecour', + pocoSubgraph: 'https://thegraph.iex.ec/subgraphs/name/bellecour/poco-v5', + resultProxy: 'https://result.v8-bellecour.iex.ec', + sms: { + gramine: 'https://sms.gramine.v8-bellecour.iex.ec', + scone: 'https://sms.iex.ec', + }, + voucherHub: '0x3137B6DF4f36D338b82260eDBB2E7bab034AFEda', + voucherSubgraph: + 'https://thegraph.iex.ec/subgraphs/name/bellecour/iexec-voucher', + }); + }); + test('unknown id returns empty object', () => { + expect(getChainDefaults(0)).toEqual({}); + }); + test('experimental networks are accessible with `allowExperimentalNetworks:true` hidden by default', () => { + expect(getChainDefaults(421614)).toEqual({}); + expect( + getChainDefaults(421614, { allowExperimentalNetworks: true }).host, + ).toBeDefined(); + }); +}); + +describe('Networks', () => { + test('ethers Networks is populated with bellecour', () => { + const networkFromId = Network.from(134); + expect(networkFromId.chainId).toBe(134n); + expect(networkFromId.name).toBe('bellecour'); + + const networkFromName = Network.from('bellecour'); + expect(networkFromName.chainId).toBe(134n); + expect(networkFromName.name).toBe('bellecour'); + }); +}); diff --git a/test/lib/unit/validator.test.js b/test/lib/unit/validator.test.js index a3a8a68e..ab07e2c2 100644 --- a/test/lib/unit/validator.test.js +++ b/test/lib/unit/validator.test.js @@ -3,7 +3,12 @@ import { BN } from 'bn.js'; import { getDefaultProvider } from 'ethers'; import fsExtra from 'fs-extra'; import { join } from 'path'; -import { INFURA_PROJECT_ID, TEE_FRAMEWORKS } from '../../test-utils.js'; +import { + ALCHEMY_API_KEY, + ETHERSCAN_API_KEY, + INFURA_PROJECT_ID, + TEE_FRAMEWORKS, +} from '../../test-utils.js'; import { uint256Schema, weiAmountSchema, @@ -33,9 +38,12 @@ const { ValidationError } = errors; const { readFile } = fsExtra; -const mainnetHost = INFURA_PROJECT_ID - ? `https://mainnet.infura.io/v3/${INFURA_PROJECT_ID}` - : 'mainnet'; +const mainnetDefaultProvider = getDefaultProvider('mainnet', { + cloudflare: true, + alchemy: ALCHEMY_API_KEY || '-', + etherscan: ETHERSCAN_API_KEY || '-', + infura: INFURA_PROJECT_ID || '-', +}); describe('[positiveIntSchema]', () => { test('int', async () => { @@ -878,7 +886,7 @@ describe('[addressSchema]', () => { test('undefined', async () => { await expect( addressSchema({ - ethProvider: getDefaultProvider(mainnetHost), + ethProvider: mainnetDefaultProvider, }).validate(undefined), ).resolves.toBe(undefined); }); @@ -894,7 +902,7 @@ describe('[addressSchema]', () => { }); test('address (with ethProvider)', async () => { await expect( - addressSchema({ ethProvider: getDefaultProvider(mainnetHost) }).validate( + addressSchema({ ethProvider: mainnetDefaultProvider }).validate( '0x607F4C5BB672230e8672085532f7e901544a7375', ), ).resolves.toBe('0x607F4C5BB672230e8672085532f7e901544a7375'); @@ -910,14 +918,14 @@ describe('[addressSchema]', () => { }); test('ens (resolve ENS with ethProvider)', async () => { await expect( - addressSchema({ ethProvider: getDefaultProvider(mainnetHost) }).validate( + addressSchema({ ethProvider: mainnetDefaultProvider }).validate( 'rlc.iexec.eth', ), ).resolves.toBe('0x607F4C5BB672230e8672085532f7e901544a7375'); }, 10000); test('invalid ens (throw when ens is missing)', async () => { await expect( - addressSchema({ ethProvider: getDefaultProvider(mainnetHost) }).validate( + addressSchema({ ethProvider: mainnetDefaultProvider }).validate( 'pierre.iexec.eth', ), ).rejects.toThrow( @@ -935,7 +943,7 @@ describe('[addressOrAnySchema]', () => { test('undefined', async () => { await expect( addressOrAnySchema({ - ethProvider: getDefaultProvider(mainnetHost), + ethProvider: mainnetDefaultProvider, }).validate(undefined), ).resolves.toBe(undefined); }); @@ -957,7 +965,7 @@ describe('[addressOrAnySchema]', () => { test('address (with ethProvider)', async () => { await expect( addressOrAnySchema({ - ethProvider: getDefaultProvider(mainnetHost), + ethProvider: mainnetDefaultProvider, }).validate('0x607F4C5BB672230e8672085532f7e901544a7375'), ).resolves.toBe('0x607F4C5BB672230e8672085532f7e901544a7375'); }); @@ -975,14 +983,14 @@ describe('[addressOrAnySchema]', () => { test('ens (resolve ENS with ethProvider)', async () => { await expect( addressOrAnySchema({ - ethProvider: getDefaultProvider(mainnetHost), + ethProvider: mainnetDefaultProvider, }).validate('rlc.iexec.eth'), ).resolves.toBe('0x607F4C5BB672230e8672085532f7e901544a7375'); }, 10000); test('invalid ens (throw when ens is missing)', async () => { await expect( addressOrAnySchema({ - ethProvider: getDefaultProvider(mainnetHost), + ethProvider: mainnetDefaultProvider, }).validate('pierre.iexec.eth'), ).rejects.toThrow( new ValidationError('Unable to resolve ENS pierre.iexec.eth'), diff --git a/test/test-utils.js b/test/test-utils.js index f467cafb..b6b437e8 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -47,6 +47,14 @@ console.log('using env INFURA_PROJECT_ID', !!INFURA_PROJECT_ID); console.log('using env ETHERSCAN_API_KEY', !!ETHERSCAN_API_KEY); console.log('using env ALCHEMY_API_KEY', !!ALCHEMY_API_KEY); +export const DEFAULT_PROVIDER_OPTIONS = { + cloudflare: true, + alchemy: ALCHEMY_API_KEY, + etherscan: ETHERSCAN_API_KEY, + infura: INFURA_PROJECT_ID, + quorum: 1, +}; + export const SERVICE_HTTP_500_URL = DRONE ? 'http://service-internal-error:80' : 'http://localhost:5500';