Skip to content

Commit 9895a48

Browse files
feat: dapp address resolution from compass (#53)
1 parent 19a9a78 commit 9895a48

File tree

7 files changed

+153
-42
lines changed

7 files changed

+153
-42
lines changed

package-lock.json

Lines changed: 4 additions & 4 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.8.1",
5454
"graphql-request": "^6.1.0",
55-
"iexec": "^8.16.1",
55+
"iexec": "^8.17.0",
5656
"kubo-rpc-client": "^4.1.3",
5757
"yup": "^1.1.1"
5858
},

src/config/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const DEFAULT_CHAIN_ID = 134;
77

88
interface ChainConfig {
99
name: string;
10-
dappAddress: string;
10+
dappAddress?: string;
1111
prodWorkerpoolAddress: string;
1212
dataProtectorSubgraph: string;
1313
ipfsUploadUrl: string;
@@ -29,7 +29,7 @@ export const CHAIN_CONFIG: Record<number, ChainConfig> = {
2929
},
3030
421614: {
3131
name: 'arbitrum-sepolia-testnet',
32-
dappAddress: 'web3telegram.apps.iexec.eth',
32+
dappAddress: undefined, // ENS not supported on this network, address will be resolved from Compass
3333
prodWorkerpoolAddress: '0x39c3cdd91a7f1c4ed59108a9da4e79de9a1c1b59',
3434
dataProtectorSubgraph:
3535
'https://thegraph.arbitrum-sepolia-testnet.iex.ec/api/subgraphs/id/5YjRPLtjS6GH6bB4yY55Qg4HzwtRGQ8TaHtGf9UBWWd',
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { CompassCallError } from 'iexec/errors';
2+
import { AddressOrENS } from '../web3telegram/types.js';
3+
4+
export async function resolveDappAddressFromCompass(
5+
compassUrl: string,
6+
chainId: number
7+
): Promise<AddressOrENS | undefined> {
8+
if (!compassUrl) {
9+
return undefined;
10+
}
11+
12+
return (
13+
fetch(`${compassUrl}/${chainId}/iapps/web3telegram`)
14+
// Handle network errors
15+
.catch((error) => {
16+
throw new CompassCallError(
17+
`Connection to ${compassUrl} failed with a network error`,
18+
error
19+
);
20+
})
21+
// Handle server errors
22+
.then((response) => {
23+
if (response.status >= 500 && response.status <= 599) {
24+
throw new CompassCallError(
25+
`Server at ${compassUrl} encountered an internal error`,
26+
Error(
27+
`Server internal error: ${response.status} ${response.statusText}`
28+
)
29+
);
30+
}
31+
return response;
32+
})
33+
// Handle unexpected response formats
34+
.then((response) => {
35+
if (response.status !== 200) {
36+
throw new Error(
37+
`Failed to fetch dapp address from compass: ${response.statusText}`
38+
);
39+
}
40+
const contentType = response.headers.get('Content-Type');
41+
if (!contentType || contentType.indexOf('application/json') === -1) {
42+
throw new Error(
43+
'Failed to fetch dapp address from compass: response is not JSON'
44+
);
45+
}
46+
return response.json();
47+
})
48+
.then((data) => {
49+
if (!data || !data.address) {
50+
throw new Error(`No dapp address found in compass response`);
51+
}
52+
return data.address;
53+
})
54+
);
55+
}

src/web3telegram/IExecWeb3telegram.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import { getChainDefaultConfig } from '../config/config.js';
1818
import { isValidProvider } from '../utils/validators.js';
1919
import { getChainIdFromProvider } from '../utils/getChainId.js';
20+
import { resolveDappAddressFromCompass } from '../utils/resolveDappAddressFromCompass.js';
2021

2122
type EthersCompatibleProvider =
2223
| AbstractProvider
@@ -126,16 +127,38 @@ export class IExecWeb3telegram {
126127
allowExperimentalNetworks: this.options.allowExperimentalNetworks,
127128
});
128129

130+
const ipfsGateway =
131+
this.options?.ipfsGateway || chainDefaultConfig?.ipfsGateway;
132+
133+
let iexec: IExec, graphQLClient: GraphQLClient;
134+
135+
try {
136+
iexec = new IExec(
137+
{ ethProvider: this.ethProvider },
138+
{
139+
ipfsGatewayURL: ipfsGateway,
140+
...this.options?.iexecOptions,
141+
allowExperimentalNetworks: this.options.allowExperimentalNetworks,
142+
}
143+
);
144+
} catch (e: any) {
145+
throw new Error(`Unsupported ethProvider: ${e.message}`);
146+
}
147+
129148
const subgraphUrl =
130149
this.options?.dataProtectorSubgraph ||
131150
chainDefaultConfig?.dataProtectorSubgraph;
132151
const dappAddressOrENS =
133-
this.options?.dappAddressOrENS || chainDefaultConfig?.dappAddress;
152+
this.options?.dappAddressOrENS ||
153+
chainDefaultConfig?.dappAddress ||
154+
(await resolveDappAddressFromCompass(
155+
await iexec.config.resolveCompassURL(),
156+
chainId
157+
));
134158
const dappWhitelistAddress =
135159
this.options?.dappWhitelistAddress ||
136160
chainDefaultConfig?.whitelistSmartContract;
137-
const ipfsGateway =
138-
this.options?.ipfsGateway || chainDefaultConfig?.ipfsGateway;
161+
139162
const defaultWorkerpool = chainDefaultConfig?.prodWorkerpoolAddress;
140163
const ipfsNode =
141164
this.options?.ipfsNode || chainDefaultConfig?.ipfsUploadUrl;
@@ -156,21 +179,6 @@ export class IExecWeb3telegram {
156179
);
157180
}
158181

159-
let iexec: IExec, graphQLClient: GraphQLClient;
160-
161-
try {
162-
iexec = new IExec(
163-
{ ethProvider: this.ethProvider },
164-
{
165-
ipfsGatewayURL: ipfsGateway,
166-
...this.options?.iexecOptions,
167-
allowExperimentalNetworks: this.options.allowExperimentalNetworks,
168-
}
169-
);
170-
} catch (e: any) {
171-
throw new Error(`Unsupported ethProvider: ${e.message}`);
172-
}
173-
174182
try {
175183
graphQLClient = new GraphQLClient(subgraphUrl);
176184
} catch (error: any) {

tests/e2e/constructor.test.ts

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ describe('IExecWeb3telegram()', () => {
116116
expect(await iexec.config.resolveIexecGatewayURL()).toBe(iexecGatewayURL);
117117
});
118118

119+
it(
120+
'When calling a read method should work as expected',
121+
async () => {
122+
// --- GIVEN
123+
const web3telegram = new IExecWeb3telegram();
124+
const wallet = Wallet.createRandom();
125+
126+
// --- WHEN/THEN
127+
await expect(
128+
web3telegram.fetchUserContacts({ userAddress: wallet.address })
129+
).resolves.not.toThrow();
130+
},
131+
MAX_EXPECTED_WEB2_SERVICES_TIME
132+
);
133+
119134
describe('When instantiating SDK with an experimental network', () => {
120135
const experimentalNetworkSigner = getWeb3Provider(
121136
Wallet.createRandom().privateKey,
@@ -157,10 +172,10 @@ describe('IExecWeb3telegram()', () => {
157172
expect(web3telegram['ipfsGateway']).toBe(
158173
arbitrumSepoliaConfig!.ipfsGateway
159174
);
160-
expect(web3telegram['ipfsNode']).toBe(arbitrumSepoliaConfig!.ipfsUploadUrl);
161-
expect(web3telegram['dappAddressOrENS']).toBe(
162-
arbitrumSepoliaConfig!.dappAddress
175+
expect(web3telegram['ipfsNode']).toBe(
176+
arbitrumSepoliaConfig!.ipfsUploadUrl
163177
);
178+
expect(web3telegram['dappAddressOrENS']).toMatch(/^0x[a-fA-F0-9]{40}$/); // resolved from Compass
164179
expect(web3telegram['dappWhitelistAddress']).toBe(
165180
arbitrumSepoliaConfig!.whitelistSmartContract.toLowerCase()
166181
);
@@ -190,7 +205,9 @@ describe('IExecWeb3telegram()', () => {
190205
allowExperimentalNetworks: true,
191206
});
192207
expect(arbitrumSepoliaConfig).not.toBeNull();
193-
expect(web3telegram['ipfsNode']).toBe(arbitrumSepoliaConfig!.ipfsUploadUrl);
208+
expect(web3telegram['ipfsNode']).toBe(
209+
arbitrumSepoliaConfig!.ipfsUploadUrl
210+
);
194211
expect(web3telegram['dappWhitelistAddress']).toBe(
195212
arbitrumSepoliaConfig!.whitelistSmartContract.toLowerCase()
196213
);
@@ -204,18 +221,26 @@ describe('IExecWeb3telegram()', () => {
204221
});
205222
});
206223

207-
it(
208-
'When calling a read method should work as expected',
209-
async () => {
210-
// --- GIVEN
211-
const web3telegram = new IExecWeb3telegram();
212-
const wallet = Wallet.createRandom();
224+
describe('When instantiating SDK with on a network backed by Compass', () => {
225+
it('should resolve dapp address from Compass', async () => {
226+
const chainId = 421614; // Arbitrum Sepolia Testnet ENS not supported
227+
const chainConfig = getChainDefaultConfig(chainId, {
228+
allowExperimentalNetworks: true,
229+
});
230+
expect(chainConfig.dappAddress).toBeUndefined(); // ENS not supported on this network
213231

214-
// --- WHEN/THEN
215-
await expect(
216-
web3telegram.fetchUserContacts({ userAddress: wallet.address })
217-
).resolves.not.toThrow();
218-
},
219-
MAX_EXPECTED_WEB2_SERVICES_TIME
220-
);
232+
const web3telegram = new IExecWeb3telegram(
233+
getWeb3Provider(Wallet.createRandom().privateKey, {
234+
host: chainId,
235+
allowExperimentalNetworks: true,
236+
}),
237+
{ allowExperimentalNetworks: true }
238+
);
239+
await web3telegram.init();
240+
241+
const dappAddressOrENS = web3telegram['dappAddressOrENS'];
242+
expect(typeof dappAddressOrENS).toBe('string');
243+
expect(dappAddressOrENS).toMatch(/^0x[a-fA-F0-9]{40}$/);
244+
});
245+
});
221246
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { describe, it, expect } from '@jest/globals';
2+
import { resolveDappAddressFromCompass } from '../../../src/utils/resolveDappAddressFromCompass.js';
3+
4+
describe('resolveDappAddressFromCompass', () => {
5+
it('should return undefined if compassUrl is not provided', async () => {
6+
const result = await resolveDappAddressFromCompass('', 1);
7+
expect(result).toBeUndefined();
8+
});
9+
10+
it('should resolve dapp address from a valid compass instance', async () => {
11+
const compassUrl = 'https://compass.arbitrum-sepolia-testnet.iex.ec';
12+
const chainId = 421614;
13+
const address = await resolveDappAddressFromCompass(compassUrl, chainId);
14+
expect(address).toBeDefined();
15+
});
16+
17+
it('should throw CompassCallError on network error', async () => {
18+
const compassUrl = 'https://invalid-url.iex.ec';
19+
await expect(resolveDappAddressFromCompass(compassUrl, 1)).rejects.toThrow(
20+
'Compass API error: Connection to https://invalid-url.iex.ec failed with a network error'
21+
);
22+
});
23+
});

0 commit comments

Comments
 (0)