Skip to content

Commit 887f97a

Browse files
feat: add support for experimental networks (#196)
* chore: update dependencies - iexec to 8.16.1 and whitelist-smart-contract to 0.2.1 * feat: add support for experimental networks * refactor: remove unused CHAIN_IDS export and simplify chain config * refactor: rename getChainConfig to getChainDefaultConfig and improve resolveConfig behavior * test: add test to verify arbitrum-sepolia config
1 parent 3231966 commit 887f97a

File tree

12 files changed

+179
-58
lines changed

12 files changed

+179
-58
lines changed

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"buffer": "^6.0.3",
5353
"ethers": "^6.13.2",
5454
"graphql-request": "^6.1.0",
55-
"iexec": "^8.16.0",
55+
"iexec": "^8.16.1",
5656
"kubo-rpc-client": "^4.1.1",
5757
"yup": "^1.1.1"
5858
},

src/config/config.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ export const MAX_DESIRED_WORKERPOOL_ORDER_PRICE = 0;
44
export const DEFAULT_CONTENT_TYPE = 'text/plain';
55
export const ANY_DATASET_ADDRESS = 'any';
66

7-
export const CHAIN_IDS = {
8-
BELLECOUR: 134,
9-
AVALANCHE_FUJI: 43113,
10-
ARBITRUM_SEPOLIA: 421614,
11-
} as const;
12-
13-
export const DEFAULT_CHAIN_ID = CHAIN_IDS.BELLECOUR;
7+
export const DEFAULT_CHAIN_ID = 134;
148

159
interface ChainConfig {
1610
name: string;
@@ -20,10 +14,11 @@ interface ChainConfig {
2014
ipfsUploadUrl: string;
2115
ipfsGateway: string;
2216
whitelistSmartContract: string;
17+
isExperimental?: boolean;
2318
}
2419

25-
export const CHAIN_CONFIG: Record<number, ChainConfig> = {
26-
[CHAIN_IDS.BELLECOUR]: {
20+
const CHAIN_CONFIG: Record<number, ChainConfig> = {
21+
134: {
2722
name: 'bellecour',
2823
dappAddress: 'web3mail.apps.iexec.eth',
2924
prodWorkerpoolAddress: 'prod-v8-bellecour.main.pools.iexec.eth',
@@ -33,12 +28,32 @@ export const CHAIN_CONFIG: Record<number, ChainConfig> = {
3328
ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec',
3429
whitelistSmartContract: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2',
3530
},
31+
421614: {
32+
name: 'arbitrum-sepolia-testnet',
33+
dappAddress: 'web3mail.apps.iexec.eth',
34+
prodWorkerpoolAddress: '0x39c3cdd91a7f1c4ed59108a9da4e79de9a1c1b59',
35+
dataProtectorSubgraph:
36+
'https://thegraph.iex.ec/subgraphs/name/bellecour/dataprotector-v2',
37+
ipfsGateway: 'https://ipfs-gateway.arbitrum-sepolia-testnet.iex.ec',
38+
ipfsUploadUrl: 'https://ipfs-upload.arbitrum-sepolia-testnet.iex.ec',
39+
whitelistSmartContract: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2', // TODO: add the correct address
40+
isExperimental: true,
41+
},
3642
};
3743

38-
export function getChainConfig(chainId: number): ChainConfig {
44+
export const getChainDefaultConfig = (
45+
chainId: number,
46+
options?: { allowExperimentalNetworks?: boolean }
47+
): ChainConfig | null => {
3948
const config = CHAIN_CONFIG[chainId];
49+
4050
if (!config) {
41-
throw new Error(`Unsupported chain ID: ${chainId}`);
51+
return null;
4252
}
53+
54+
if (config.isExperimental && !options?.allowExperimentalNetworks) {
55+
return null;
56+
}
57+
4358
return config;
44-
}
59+
};

src/utils/getWeb3Provider.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { getSignerFromPrivateKey } from 'iexec/utils';
22
import { Web3SignerProvider } from '../web3mail/types.js';
33

4-
export const getWeb3Provider = (privateKey: string): Web3SignerProvider =>
5-
getSignerFromPrivateKey('bellecour', privateKey);
4+
export const getWeb3Provider = (
5+
privateKey: string,
6+
options: { allowExperimentalNetworks?: boolean; host?: number | string } = {}
7+
): Web3SignerProvider => {
8+
const chainHost = options?.host ? `${options.host}` : 'bellecour';
9+
return getSignerFromPrivateKey(chainHost, privateKey, {
10+
allowExperimentalNetworks: options?.allowExperimentalNetworks,
11+
providers: {},
12+
});
13+
};

src/web3mail/IExecWeb3mail.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import {
1414
Web3SignerProvider,
1515
FetchMyContactsParams,
1616
} from './types.js';
17-
import { CHAIN_CONFIG } from '../config/config.js';
1817
import { isValidProvider } from '../utils/validators.js';
1918
import { getChainIdFromProvider } from '../utils/getChainId.js';
19+
import { getChainDefaultConfig } from '../config/config.js';
2020

2121
type EthersCompatibleProvider =
2222
| AbstractProvider
@@ -122,7 +122,9 @@ export class IExecWeb3mail {
122122

123123
private async resolveConfig(): Promise<Web3mailResolvedConfig> {
124124
const chainId = await getChainIdFromProvider(this.ethProvider);
125-
const chainDefaultConfig = CHAIN_CONFIG[chainId];
125+
const chainDefaultConfig = getChainDefaultConfig(chainId, {
126+
allowExperimentalNetworks: this.options.allowExperimentalNetworks,
127+
});
126128

127129
const subgraphUrl =
128130
this.options?.dataProtectorSubgraph ||
@@ -162,6 +164,7 @@ export class IExecWeb3mail {
162164
{
163165
ipfsGatewayURL: ipfsGateway,
164166
...this.options?.iexecOptions,
167+
allowExperimentalNetworks: this.options.allowExperimentalNetworks,
165168
}
166169
);
167170
} catch (e: any) {

src/web3mail/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ export type Web3MailConfigOptions = {
108108
* If not provided, the default IPFS gateway URL will be used.
109109
*/
110110
ipfsGateway?: string;
111+
112+
/**
113+
* if true allows using a provider connected to an experimental networks (default false)
114+
*
115+
* ⚠️ 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.
116+
*/
117+
allowExperimentalNetworks?: boolean;
111118
};
112119

113120
export type DappAddressConsumer = {

tests/e2e/constructor.test.ts

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// needed to access and assert IExecDataProtector's private properties
33
import { describe, expect, it } from '@jest/globals';
44
import { Wallet } from 'ethers';
5-
import { IExecWeb3mail } from '../../src/index.js';
5+
import { getWeb3Provider, IExecWeb3mail } from '../../src/index.js';
66
import {
77
getTestWeb3SignerProvider,
88
MAX_EXPECTED_WEB2_SERVICES_TIME,
99
} from '../test-utils.js';
10-
import { DEFAULT_CHAIN_ID, getChainConfig } from '../../src/config/config.js';
10+
import { DEFAULT_CHAIN_ID, getChainDefaultConfig } from '../../src/config/config.js';
1111

1212
describe('IExecWeb3mail()', () => {
1313
it('instantiates with a valid ethProvider', async () => {
@@ -26,7 +26,9 @@ describe('IExecWeb3mail()', () => {
2626
);
2727
await web3mail.init();
2828
const ipfsGateway = web3mail['ipfsGateway'];
29-
expect(ipfsGateway).toStrictEqual(getChainConfig(DEFAULT_CHAIN_ID).ipfsGateway);
29+
const defaultConfig = getChainDefaultConfig(DEFAULT_CHAIN_ID);
30+
expect(defaultConfig).not.toBeNull();
31+
expect(ipfsGateway).toStrictEqual(defaultConfig!.ipfsGateway);
3032
});
3133

3234
it('should use provided ipfs gateway url when ipfsGateway is provided', async () => {
@@ -49,9 +51,11 @@ describe('IExecWeb3mail()', () => {
4951
getTestWeb3SignerProvider(wallet.privateKey)
5052
);
5153
await web3mail.init();
52-
const graphQLClientUrl = web3mail['graphQLClient'];
53-
expect(graphQLClientUrl['url']).toBe(
54-
getChainConfig(DEFAULT_CHAIN_ID).dataProtectorSubgraph);
54+
const graphQLClient = web3mail['graphQLClient'];
55+
const defaultConfig = getChainDefaultConfig(DEFAULT_CHAIN_ID);
56+
expect(defaultConfig).not.toBeNull();
57+
expect(graphQLClient['url']).toBe(
58+
defaultConfig!.dataProtectorSubgraph);
5559
});
5660

5761
it('should use provided data Protector Subgraph URL when subgraphUrl is provided', async () => {
@@ -124,4 +128,77 @@ describe('IExecWeb3mail()', () => {
124128
},
125129
MAX_EXPECTED_WEB2_SERVICES_TIME
126130
);
131+
132+
describe('When instantiating SDK with an experimental network', () => {
133+
const experimentalNetworkSigner = getWeb3Provider(
134+
Wallet.createRandom().privateKey,
135+
{
136+
host: 421614,
137+
allowExperimentalNetworks: true,
138+
}
139+
);
140+
141+
describe('Without allowExperimentalNetworks', () => {
142+
it('should throw a configuration error', async () => {
143+
const web3mail = new IExecWeb3mail(experimentalNetworkSigner);
144+
await expect(web3mail.init()).rejects.toThrow(
145+
'Missing required configuration for chainId 421614: dataProtectorSubgraph, dappAddress, whitelistSmartContract, ipfsGateway, prodWorkerpoolAddress, ipfsUploadUrl'
146+
);
147+
});
148+
});
149+
150+
describe('With allowExperimentalNetworks: true', () => {
151+
it('should resolve the configuration', async () => {
152+
const web3mail = new IExecWeb3mail(
153+
experimentalNetworkSigner,
154+
{ allowExperimentalNetworks: true }
155+
);
156+
await expect(web3mail.init()).resolves.toBeUndefined();
157+
expect(web3mail).toBeInstanceOf(IExecWeb3mail);
158+
});
159+
160+
it('should use Arbitrum Sepolia default configuration', async () => {
161+
const web3mail = new IExecWeb3mail(
162+
experimentalNetworkSigner,
163+
{ allowExperimentalNetworks: true }
164+
);
165+
await web3mail.init();
166+
167+
const arbitrumSepoliaConfig = getChainDefaultConfig(421614, { allowExperimentalNetworks: true });
168+
expect(arbitrumSepoliaConfig).not.toBeNull();
169+
170+
expect(web3mail['ipfsGateway']).toBe(arbitrumSepoliaConfig!.ipfsGateway);
171+
expect(web3mail['ipfsNode']).toBe(arbitrumSepoliaConfig!.ipfsUploadUrl);
172+
expect(web3mail['dappAddressOrENS']).toBe(arbitrumSepoliaConfig!.dappAddress);
173+
expect(web3mail['dappWhitelistAddress']).toBe(arbitrumSepoliaConfig!.whitelistSmartContract.toLowerCase());
174+
expect(web3mail['defaultWorkerpool']).toBe(arbitrumSepoliaConfig!.prodWorkerpoolAddress);
175+
expect(web3mail['graphQLClient']['url']).toBe(arbitrumSepoliaConfig!.dataProtectorSubgraph);
176+
});
177+
178+
it('should allow custom configuration override for Arbitrum Sepolia', async () => {
179+
const customIpfsGateway = 'https://custom-arbitrum-ipfs.com';
180+
const customDappAddress = 'custom.arbitrum.app.eth';
181+
182+
const web3mail = new IExecWeb3mail(
183+
experimentalNetworkSigner,
184+
{
185+
allowExperimentalNetworks: true,
186+
ipfsGateway: customIpfsGateway,
187+
dappAddressOrENS: customDappAddress
188+
}
189+
);
190+
await web3mail.init();
191+
192+
expect(web3mail['ipfsGateway']).toBe(customIpfsGateway);
193+
expect(web3mail['dappAddressOrENS']).toBe(customDappAddress);
194+
195+
const arbitrumSepoliaConfig = getChainDefaultConfig(421614, { allowExperimentalNetworks: true });
196+
expect(arbitrumSepoliaConfig).not.toBeNull();
197+
expect(web3mail['ipfsNode']).toBe(arbitrumSepoliaConfig!.ipfsUploadUrl);
198+
expect(web3mail['dappWhitelistAddress']).toBe(arbitrumSepoliaConfig!.whitelistSmartContract.toLowerCase());
199+
expect(web3mail['defaultWorkerpool']).toBe(arbitrumSepoliaConfig!.prodWorkerpoolAddress);
200+
expect(web3mail['graphQLClient']['url']).toBe(arbitrumSepoliaConfig!.dataProtectorSubgraph);
201+
});
202+
});
203+
});
127204
});

tests/e2e/fetchMyContacts.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
waitSubgraphIndexing,
1717
} from '../test-utils.js';
1818
import IExec from 'iexec/IExec';
19-
import { DEFAULT_CHAIN_ID, getChainConfig } from '../../src/config/config.js';
19+
import { DEFAULT_CHAIN_ID, getChainDefaultConfig } from '../../src/config/config.js';
2020

2121
describe('web3mail.fetchMyContacts()', () => {
2222
let wallet: HDNodeWallet;
@@ -41,7 +41,7 @@ describe('web3mail.fetchMyContacts()', () => {
4141
'pass with a granted access for a specific requester',
4242
async () => {
4343
await dataProtector.grantAccess({
44-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
44+
authorizedApp: getChainDefaultConfig(DEFAULT_CHAIN_ID).dappAddress,
4545
protectedData: protectedData.address,
4646
authorizedUser: wallet.address,
4747
});
@@ -65,7 +65,7 @@ describe('web3mail.fetchMyContacts()', () => {
6565
'pass with a granted access for any requester',
6666
async () => {
6767
const grantedAccessForAnyRequester = await dataProtector.grantAccess({
68-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
68+
authorizedApp: getChainDefaultConfig(DEFAULT_CHAIN_ID).dappAddress,
6969
protectedData: protectedData.address,
7070
authorizedUser: NULL_ADDRESS,
7171
});
@@ -104,7 +104,7 @@ describe('web3mail.fetchMyContacts()', () => {
104104
const encryptionKey = await iexec.dataset.generateEncryptionKey();
105105
await iexec.dataset.pushDatasetSecret(dataset.address, encryptionKey);
106106
await dataProtector.grantAccess({
107-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
107+
authorizedApp: getChainDefaultConfig(DEFAULT_CHAIN_ID).dappAddress,
108108
protectedData: dataset.address,
109109
authorizedUser: wallet.address,
110110
});
@@ -126,7 +126,7 @@ describe('web3mail.fetchMyContacts()', () => {
126126
await waitSubgraphIndexing();
127127

128128
await dataProtector.grantAccess({
129-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
129+
authorizedApp: getChainDefaultConfig(DEFAULT_CHAIN_ID).dappAddress,
130130
protectedData: notValidProtectedData.address,
131131
authorizedUser: wallet.address,
132132
});

tests/e2e/fetchUserContacts.test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from '@iexec/dataprotector';
55
import { beforeAll, describe, expect, it } from '@jest/globals';
66
import { HDNodeWallet, Wallet } from 'ethers';
7-
import { DEFAULT_CHAIN_ID, getChainConfig } from '../../src/config/config.js';
7+
import { DEFAULT_CHAIN_ID, getChainDefaultConfig } from '../../src/config/config.js';
88
import { IExecWeb3mail, WorkflowError } from '../../src/index.js';
99
import {
1010
MAX_EXPECTED_BLOCKTIME,
@@ -44,14 +44,18 @@ describe('web3mail.fetchMyContacts()', () => {
4444
async () => {
4545
const user1 = Wallet.createRandom().address;
4646
const user2 = Wallet.createRandom().address;
47+
const defaultConfig = getChainDefaultConfig(DEFAULT_CHAIN_ID);
48+
expect(defaultConfig).not.toBeNull();
49+
const authorizedApp = defaultConfig!.dappAddress;
50+
4751
await dataProtector.grantAccess({
48-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
52+
authorizedApp: authorizedApp,
4953
protectedData: protectedData1.address,
5054
authorizedUser: user1,
5155
});
5256

5357
await dataProtector.grantAccess({
54-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
58+
authorizedApp: authorizedApp,
5559
protectedData: protectedData2.address,
5660
authorizedUser: user2,
5761
});
@@ -71,8 +75,12 @@ describe('web3mail.fetchMyContacts()', () => {
7175
'Test that the protected data can be accessed by authorized user',
7276
async () => {
7377
const userWithAccess = Wallet.createRandom().address;
78+
const defaultConfig = getChainDefaultConfig(DEFAULT_CHAIN_ID);
79+
expect(defaultConfig).not.toBeNull();
80+
const authorizedApp = defaultConfig!.dappAddress;
81+
7482
await dataProtector.grantAccess({
75-
authorizedApp: getChainConfig(DEFAULT_CHAIN_ID).dappAddress,
83+
authorizedApp: authorizedApp,
7684
protectedData: protectedData1.address,
7785
authorizedUser: userWithAccess,
7886
});

0 commit comments

Comments
 (0)