Skip to content

Commit 29dabca

Browse files
feat: add dapp address resolution from compass
1 parent 9fcb86e commit 29dabca

File tree

7 files changed

+132
-25
lines changed

7 files changed

+132
-25
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.13.2",
5454
"graphql-request": "^6.1.0",
55-
"iexec": "^8.16.1",
55+
"iexec": "^8.17.0",
5656
"kubo-rpc-client": "^4.1.1",
5757
"yup": "^1.1.1"
5858
},

src/config/config.ts

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

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

src/web3mail/IExecWeb3mail.ts

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

2122
type EthersCompatibleProvider =
2223
| AbstractProvider
@@ -126,16 +127,37 @@ export class IExecWeb3mail {
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;
139161
const defaultWorkerpool = chainDefaultConfig?.prodWorkerpoolAddress;
140162
const ipfsNode =
141163
this.options?.ipfsNode || chainDefaultConfig?.ipfsUploadUrl;
@@ -156,21 +178,6 @@ export class IExecWeb3mail {
156178
);
157179
}
158180

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-
174181
try {
175182
graphQLClient = new GraphQLClient(subgraphUrl);
176183
} catch (error: any) {

tests/e2e/constructor.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,27 @@ describe('IExecWeb3mail()', () => {
220220
});
221221
});
222222
});
223+
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
231+
232+
const web3mail = new IExecWeb3mail(
233+
getWeb3Provider(Wallet.createRandom().privateKey, {
234+
host: chainId,
235+
allowExperimentalNetworks: true,
236+
}),
237+
{ allowExperimentalNetworks: true }
238+
);
239+
await web3mail.init();
240+
241+
const dappAddressOrENS = web3mail['dappAddressOrENS'];
242+
expect(typeof dappAddressOrENS).toBe('string');
243+
expect(dappAddressOrENS).toMatch(/^0x[a-fA-F0-9]{40}$/);
244+
});
245+
});
223246
});
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)