Skip to content

Commit c55a36b

Browse files
Merge pull request #32 from iExecBlockchainComputing/feature/add-multichain-config
Feature/add multichain config
2 parents abe4896 + 9703458 commit c55a36b

File tree

12 files changed

+320
-175
lines changed

12 files changed

+320
-175
lines changed

src/config/config.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
1-
export const WEB3TELEGRAM_DAPP_ADDRESS = 'web3telegram.apps.iexec.eth';
2-
3-
export const WHITELIST_SMART_CONTRACT_ADDRESS =
4-
'0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F';
5-
6-
export const PROD_WORKERPOOL_ADDRESS = 'prod-v8-bellecour.main.pools.iexec.eth';
7-
8-
export const DATAPROTECTOR_SUBGRAPH_ENDPOINT =
9-
'https://thegraph.iex.ec/subgraphs/name/bellecour/dataprotector-v2';
10-
111
export const MAX_DESIRED_DATA_ORDER_PRICE = 0;
12-
132
export const MAX_DESIRED_APP_ORDER_PRICE = 0;
14-
153
export const MAX_DESIRED_WORKERPOOL_ORDER_PRICE = 0;
16-
17-
export const IPFS_UPLOAD_URL = '/dns4/ipfs-upload.v8-bellecour.iex.ec/https';
18-
19-
export const DEFAULT_IPFS_GATEWAY = 'https://ipfs-gateway.v8-bellecour.iex.ec';
20-
214
export const ANY_DATASET_ADDRESS = 'any';
5+
6+
export const CHAIN_IDS = {
7+
BELLECOUR: 134,
8+
AVALANCHE_FUJI: 43113,
9+
ARBITRUM_SEPOLIA: 421614,
10+
} as const;
11+
12+
export const DEFAULT_CHAIN_ID = CHAIN_IDS.BELLECOUR;
13+
14+
interface ChainConfig {
15+
name: string;
16+
dappAddress: string;
17+
prodWorkerpoolAddress: string;
18+
dataProtectorSubgraph: string;
19+
ipfsUploadUrl: string;
20+
ipfsGateway: string;
21+
whitelistSmartContract: string;
22+
}
23+
24+
export const CHAIN_CONFIG: Record<number, ChainConfig> = {
25+
[CHAIN_IDS.BELLECOUR]: {
26+
name: 'bellecour',
27+
dappAddress: 'web3telegram.apps.iexec.eth',
28+
prodWorkerpoolAddress: 'prod-v8-bellecour.main.pools.iexec.eth',
29+
dataProtectorSubgraph:
30+
'https://thegraph.iex.ec/subgraphs/name/bellecour/dataprotector-v2',
31+
ipfsUploadUrl: '/dns4/ipfs-upload.v8-bellecour.iex.ec/https',
32+
ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec',
33+
whitelistSmartContract: '0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F',
34+
},
35+
};
36+
37+
export function getChainConfig(chainId: number): ChainConfig {
38+
const config = CHAIN_CONFIG[chainId];
39+
if (!config) {
40+
throw new Error(`Unsupported chain ID: ${chainId}`);
41+
}
42+
return config;
43+
}

src/utils/getChainId.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {
2+
JsonRpcProvider,
3+
BrowserProvider,
4+
AbstractProvider,
5+
AbstractSigner,
6+
Eip1193Provider,
7+
} from 'ethers';
8+
import { DEFAULT_CHAIN_ID } from '../config/config.js';
9+
10+
type EthersCompatibleProvider =
11+
| string
12+
| AbstractProvider
13+
| AbstractSigner
14+
| Eip1193Provider;
15+
16+
export async function getChainIdFromProvider(
17+
ethProvider: EthersCompatibleProvider
18+
): Promise<number> {
19+
try {
20+
if (typeof ethProvider === 'string') {
21+
// Handle network string
22+
if (ethProvider === 'bellecour') return 134;
23+
// TODO add arbitrum & avalanche
24+
const provider = new JsonRpcProvider(ethProvider);
25+
const network = await provider.getNetwork();
26+
return Number(network.chainId);
27+
} else if (ethProvider instanceof AbstractProvider) {
28+
const network = await ethProvider.getNetwork();
29+
return Number(network.chainId);
30+
} else if (ethProvider instanceof AbstractSigner) {
31+
const { provider } = ethProvider;
32+
if (!provider) {
33+
throw Error('Signer is not connected to a provider');
34+
}
35+
const network = await provider.getNetwork();
36+
return Number(network.chainId);
37+
} else if ('request' in ethProvider) {
38+
const provider = new BrowserProvider(ethProvider as Eip1193Provider);
39+
const network = await provider.getNetwork();
40+
return Number(network.chainId);
41+
}
42+
} catch (e) {
43+
console.warn('Failed to detect chainId:', e);
44+
}
45+
return DEFAULT_CHAIN_ID;
46+
}

src/utils/ipfs-service.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { create } from 'kubo-rpc-client';
2-
import { IPFS_UPLOAD_URL, DEFAULT_IPFS_GATEWAY } from '../config/config.js';
32

43
interface GetOptions {
54
ipfsGateway?: string;
@@ -8,10 +7,7 @@ interface AddOptions extends GetOptions {
87
ipfsNode?: string;
98
}
109

11-
const get = async (
12-
cid,
13-
{ ipfsGateway = DEFAULT_IPFS_GATEWAY }: GetOptions = {}
14-
) => {
10+
const get = async (cid, { ipfsGateway }: GetOptions = {}) => {
1511
const multiaddr = `/ipfs/${cid.toString()}`;
1612
const publicUrl = `${ipfsGateway}${multiaddr}`;
1713
const res = await fetch(publicUrl);
@@ -22,13 +18,7 @@ const get = async (
2218
return new Uint8Array(arrayBuffer);
2319
};
2420

25-
const add = async (
26-
content,
27-
{
28-
ipfsNode = IPFS_UPLOAD_URL,
29-
ipfsGateway = DEFAULT_IPFS_GATEWAY,
30-
}: AddOptions = {}
31-
) => {
21+
const add = async (content, { ipfsNode, ipfsGateway }: AddOptions = {}) => {
3222
const ipfsClient = create(ipfsNode);
3323
const { cid } = await ipfsClient.add(content);
3424
await get(cid.toString(), { ipfsGateway });
Lines changed: 123 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,88 @@
11
import { AbstractProvider, AbstractSigner, Eip1193Provider } from 'ethers';
22
import { IExec } from 'iexec';
3+
import { GraphQLClient } from 'graphql-request';
34
import { fetchUserContacts } from './fetchUserContacts.js';
45
import { fetchMyContacts } from './fetchMyContacts.js';
56
import { sendTelegram } from './sendTelegram.js';
67
import {
78
Contact,
89
FetchUserContactsParams,
910
SendTelegramParams,
10-
Web3SignerProvider,
1111
AddressOrENS,
1212
Web3TelegramConfigOptions,
1313
SendTelegramResponse,
14+
Web3SignerProvider,
1415
FetchMyContactsParams,
1516
} from './types.js';
16-
import { GraphQLClient } from 'graphql-request';
17-
import {
18-
WEB3TELEGRAM_DAPP_ADDRESS,
19-
IPFS_UPLOAD_URL,
20-
DEFAULT_IPFS_GATEWAY,
21-
DATAPROTECTOR_SUBGRAPH_ENDPOINT,
22-
WHITELIST_SMART_CONTRACT_ADDRESS,
23-
} from '../config/config.js';
17+
import { CHAIN_CONFIG } from '../config/config.js';
2418
import { isValidProvider } from '../utils/validators.js';
19+
import { getChainIdFromProvider } from '../utils/getChainId.js';
20+
21+
type EthersCompatibleProvider =
22+
| AbstractProvider
23+
| AbstractSigner
24+
| Eip1193Provider
25+
| Web3SignerProvider
26+
| string;
27+
28+
interface Web3telegramResolvedConfig {
29+
dappAddressOrENS: AddressOrENS;
30+
dappWhitelistAddress: AddressOrENS;
31+
graphQLClient: GraphQLClient;
32+
ipfsNode: string;
33+
ipfsGateway: string;
34+
defaultWorkerpool: string;
35+
iexec: IExec;
36+
}
2537

2638
export class IExecWeb3telegram {
27-
private iexec: IExec;
39+
private dappAddressOrENS!: AddressOrENS;
40+
41+
private dappWhitelistAddress!: AddressOrENS;
2842

29-
private ipfsNode: string;
43+
private graphQLClient!: GraphQLClient;
3044

31-
private ipfsGateway: string;
45+
private ipfsNode!: string;
3246

33-
private dataProtectorSubgraph: string;
47+
private ipfsGateway!: string;
3448

35-
private dappAddressOrENS: AddressOrENS;
49+
private defaultWorkerpool!: string;
3650

37-
private dappWhitelistAddress: AddressOrENS;
51+
private iexec!: IExec;
3852

39-
private graphQLClient: GraphQLClient;
53+
private initPromise: Promise<void> | null = null;
54+
55+
private ethProvider: EthersCompatibleProvider;
56+
57+
private options: Web3TelegramConfigOptions;
4058

4159
constructor(
42-
ethProvider?:
43-
| Eip1193Provider
44-
| AbstractProvider
45-
| AbstractSigner
46-
| Web3SignerProvider
47-
| string,
60+
ethProvider?: EthersCompatibleProvider,
4861
options?: Web3TelegramConfigOptions
4962
) {
50-
try {
51-
this.iexec = new IExec(
52-
{ ethProvider: ethProvider || 'bellecour' },
53-
options?.iexecOptions
54-
);
55-
} catch (e) {
56-
throw Error('Unsupported ethProvider');
57-
}
63+
this.ethProvider = ethProvider || 'bellecour';
64+
this.options = options || {};
65+
}
5866

59-
try {
60-
this.dataProtectorSubgraph =
61-
options?.dataProtectorSubgraph || DATAPROTECTOR_SUBGRAPH_ENDPOINT;
62-
this.graphQLClient = new GraphQLClient(this.dataProtectorSubgraph);
63-
} catch (e) {
64-
throw Error('Impossible to create GraphQLClient');
67+
async init(): Promise<void> {
68+
if (!this.initPromise) {
69+
this.initPromise = this.resolveConfig().then((config) => {
70+
this.dappAddressOrENS = config.dappAddressOrENS;
71+
this.dappWhitelistAddress = config.dappWhitelistAddress;
72+
this.graphQLClient = config.graphQLClient;
73+
this.ipfsNode = config.ipfsNode;
74+
this.ipfsGateway = config.ipfsGateway;
75+
this.defaultWorkerpool = config.defaultWorkerpool;
76+
this.iexec = config.iexec;
77+
});
6578
}
66-
67-
this.dappAddressOrENS =
68-
options?.dappAddressOrENS || WEB3TELEGRAM_DAPP_ADDRESS;
69-
this.ipfsNode = options?.ipfsNode || IPFS_UPLOAD_URL;
70-
this.ipfsGateway = options?.ipfsGateway || DEFAULT_IPFS_GATEWAY;
71-
this.dappWhitelistAddress =
72-
options?.dappWhitelistAddress || WHITELIST_SMART_CONTRACT_ADDRESS;
79+
return this.initPromise;
7380
}
7481

7582
async fetchMyContacts(args?: FetchMyContactsParams): Promise<Contact[]> {
83+
await this.init();
7684
await isValidProvider(this.iexec);
85+
7786
return fetchMyContacts({
7887
...args,
7988
iexec: this.iexec,
@@ -83,7 +92,9 @@ export class IExecWeb3telegram {
8392
});
8493
}
8594

86-
fetchUserContacts(args: FetchUserContactsParams): Promise<Contact[]> {
95+
async fetchUserContacts(args: FetchUserContactsParams): Promise<Contact[]> {
96+
await this.init();
97+
8798
return fetchUserContacts({
8899
...args,
89100
iexec: this.iexec,
@@ -94,9 +105,12 @@ export class IExecWeb3telegram {
94105
}
95106

96107
async sendTelegram(args: SendTelegramParams): Promise<SendTelegramResponse> {
108+
await this.init();
97109
await isValidProvider(this.iexec);
98110
return sendTelegram({
99111
...args,
112+
workerpoolAddressOrEns:
113+
args.workerpoolAddressOrEns || this.defaultWorkerpool,
100114
iexec: this.iexec,
101115
ipfsNode: this.ipfsNode,
102116
ipfsGateway: this.ipfsGateway,
@@ -105,4 +119,69 @@ export class IExecWeb3telegram {
105119
graphQLClient: this.graphQLClient,
106120
});
107121
}
122+
123+
private async resolveConfig(): Promise<Web3telegramResolvedConfig> {
124+
const chainId = await getChainIdFromProvider(this.ethProvider);
125+
const chainDefaultConfig = CHAIN_CONFIG[chainId];
126+
127+
const subgraphUrl =
128+
this.options?.dataProtectorSubgraph ||
129+
chainDefaultConfig?.dataProtectorSubgraph;
130+
const dappAddressOrENS =
131+
this.options?.dappAddressOrENS || chainDefaultConfig?.dappAddress;
132+
const dappWhitelistAddress =
133+
this.options?.dappWhitelistAddress ||
134+
chainDefaultConfig?.whitelistSmartContract;
135+
const ipfsGateway =
136+
this.options?.ipfsGateway || chainDefaultConfig?.ipfsGateway;
137+
const defaultWorkerpool = chainDefaultConfig?.prodWorkerpoolAddress;
138+
const ipfsNode =
139+
this.options?.ipfsNode || chainDefaultConfig?.ipfsUploadUrl;
140+
141+
const missing = [];
142+
if (!subgraphUrl) missing.push('dataProtectorSubgraph');
143+
if (!dappAddressOrENS) missing.push('dappAddress');
144+
if (!dappWhitelistAddress) missing.push('whitelistSmartContract');
145+
if (!ipfsGateway) missing.push('ipfsGateway');
146+
if (!defaultWorkerpool) missing.push('prodWorkerpoolAddress');
147+
if (!ipfsNode) missing.push('ipfsUploadUrl');
148+
149+
if (missing.length > 0) {
150+
throw new Error(
151+
`Missing required configuration for chainId ${chainId}: ${missing.join(
152+
', '
153+
)}`
154+
);
155+
}
156+
157+
let iexec: IExec, graphQLClient: GraphQLClient;
158+
159+
try {
160+
iexec = new IExec(
161+
{ ethProvider: this.ethProvider },
162+
{
163+
ipfsGatewayURL: ipfsGateway,
164+
...this.options?.iexecOptions,
165+
}
166+
);
167+
} catch (e: any) {
168+
throw new Error(`Unsupported ethProvider: ${e.message}`);
169+
}
170+
171+
try {
172+
graphQLClient = new GraphQLClient(subgraphUrl);
173+
} catch (error: any) {
174+
throw new Error(`Failed to create GraphQLClient: ${error.message}`);
175+
}
176+
177+
return {
178+
dappAddressOrENS,
179+
dappWhitelistAddress: dappWhitelistAddress.toLowerCase(),
180+
defaultWorkerpool,
181+
graphQLClient,
182+
ipfsNode,
183+
ipfsGateway,
184+
iexec,
185+
};
186+
}
108187
}

src/web3telegram/sendTelegram.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
MAX_DESIRED_APP_ORDER_PRICE,
44
MAX_DESIRED_DATA_ORDER_PRICE,
55
MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
6-
PROD_WORKERPOOL_ADDRESS,
76
} from '../config/config.js';
87
import { handleIfProtocolError, WorkflowError } from '../utils/errors.js';
98
import { generateSecureUniqueId } from '../utils/generateUniqueId.js';
@@ -39,7 +38,7 @@ export type SendTelegram = typeof sendTelegram;
3938
export const sendTelegram = async ({
4039
graphQLClient = throwIfMissing(),
4140
iexec = throwIfMissing(),
42-
workerpoolAddressOrEns = PROD_WORKERPOOL_ADDRESS,
41+
workerpoolAddressOrEns = throwIfMissing(),
4342
dappAddressOrENS,
4443
dappWhitelistAddress,
4544
ipfsNode,
@@ -238,8 +237,8 @@ export const sendTelegram = async ({
238237
// Push telegram message to IPFS
239238
const cid = await ipfs
240239
.add(encryptedFile, {
241-
ipfsNode: ipfsNode,
242-
ipfsGateway: ipfsGateway,
240+
ipfsNode,
241+
ipfsGateway,
243242
})
244243
.catch((e) => {
245244
throw new WorkflowError({

0 commit comments

Comments
 (0)