@iexec-nox/nox-protocol-contracts 0.1.0-beta.5 → 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/contracts/interfaces/INoxCompute.sol +61 -3
- package/contracts/sdk/Nox.sol +141 -24
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -55,6 +55,14 @@ The default network is a local EDR simulation. For external networks, configure
|
|
|
55
55
|
pnpm run deploy
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Verify
|
|
59
|
+
|
|
60
|
+
Verify deployed contracts on Etherscan. Requires `ETHERSCAN_API_KEY`
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pnpm run verify arbitrumSepolia --network arbitrumSepolia
|
|
64
|
+
```
|
|
65
|
+
|
|
58
66
|
## Configuration notes
|
|
59
67
|
|
|
60
68
|
- Create2 salt is defined in [config/config.ts](config/config.ts).
|
|
@@ -19,6 +19,7 @@ interface INoxCompute {
|
|
|
19
19
|
error InvalidProof(bytes proof, string reason);
|
|
20
20
|
error UnsupportedType();
|
|
21
21
|
error IncompatibleTypes();
|
|
22
|
+
error NotPubliclyDecryptable(bytes32 handle);
|
|
22
23
|
|
|
23
24
|
/// Emitted when admin role is granted
|
|
24
25
|
event Allowed(address indexed sender, address indexed account, bytes32 indexed handle);
|
|
@@ -110,6 +111,20 @@ interface INoxCompute {
|
|
|
110
111
|
bytes32 success,
|
|
111
112
|
bytes32 result
|
|
112
113
|
);
|
|
114
|
+
event SafeMul(
|
|
115
|
+
address indexed caller,
|
|
116
|
+
bytes32 leftHandOperand,
|
|
117
|
+
bytes32 rightHandOperand,
|
|
118
|
+
bytes32 success,
|
|
119
|
+
bytes32 result
|
|
120
|
+
);
|
|
121
|
+
event SafeDiv(
|
|
122
|
+
address indexed caller,
|
|
123
|
+
bytes32 numerator,
|
|
124
|
+
bytes32 denominator,
|
|
125
|
+
bytes32 success,
|
|
126
|
+
bytes32 result
|
|
127
|
+
);
|
|
113
128
|
event Select(
|
|
114
129
|
address indexed caller,
|
|
115
130
|
bytes32 condition,
|
|
@@ -153,6 +168,8 @@ interface INoxCompute {
|
|
|
153
168
|
Div,
|
|
154
169
|
SafeAdd,
|
|
155
170
|
SafeSub,
|
|
171
|
+
SafeMul,
|
|
172
|
+
SafeDiv,
|
|
156
173
|
Select,
|
|
157
174
|
Eq,
|
|
158
175
|
Ne,
|
|
@@ -252,19 +269,30 @@ interface INoxCompute {
|
|
|
252
269
|
function plaintextToEncrypted(bytes32 value, TEEType teeType) external returns (bytes32);
|
|
253
270
|
|
|
254
271
|
/**
|
|
255
|
-
* @notice Validates
|
|
272
|
+
* @notice Validates an input handle proof for a given owner and type.
|
|
256
273
|
* @param handle handle to validate
|
|
257
274
|
* @param owner owner of the provided handle
|
|
258
275
|
* @param proof proof data
|
|
259
276
|
* @param teeType expected handle type
|
|
260
277
|
*/
|
|
261
|
-
function
|
|
278
|
+
function validateInputProof(
|
|
262
279
|
bytes32 handle,
|
|
263
280
|
address owner,
|
|
264
281
|
bytes calldata proof,
|
|
265
282
|
TEEType teeType
|
|
266
283
|
) external;
|
|
267
284
|
|
|
285
|
+
/**
|
|
286
|
+
* @notice Validates a decryption proof issued by the gateway for a publicly decryptable handle.
|
|
287
|
+
* @param handle Handle to decrypt
|
|
288
|
+
* @param decryptionProof Serialized decryption proof (signature + decrypted value)
|
|
289
|
+
* @return The decrypted value (variable length)
|
|
290
|
+
*/
|
|
291
|
+
function validateDecryptionProof(
|
|
292
|
+
bytes32 handle,
|
|
293
|
+
bytes calldata decryptionProof
|
|
294
|
+
) external view returns (bytes memory);
|
|
295
|
+
|
|
268
296
|
/**
|
|
269
297
|
* @notice Performs an addition between two encrypted values without overflow check.
|
|
270
298
|
* @param leftHandOperand Left-hand side operand handle
|
|
@@ -341,7 +369,37 @@ interface INoxCompute {
|
|
|
341
369
|
bytes32 rightHandOperand
|
|
342
370
|
) external returns (bytes32 success, bytes32 result);
|
|
343
371
|
|
|
344
|
-
|
|
372
|
+
/**
|
|
373
|
+
* @notice Performs a multiplication between two encrypted values with overflow check.
|
|
374
|
+
* If the operation succeeds, the value of the success handle will be an encrypted
|
|
375
|
+
* `true` and the result handle's value will be the encrypted product.
|
|
376
|
+
* If the operation fails (e.g., due to overflow), the success handle will contain
|
|
377
|
+
* an encrypted `false` and the result handle will contain an encrypted `0`.
|
|
378
|
+
* @param leftHandOperand Left-hand side operand handle
|
|
379
|
+
* @param rightHandOperand Right-hand side operand handle
|
|
380
|
+
* @return success Whether the operation was successful
|
|
381
|
+
* @return result Result handle
|
|
382
|
+
*/
|
|
383
|
+
function safeMul(
|
|
384
|
+
bytes32 leftHandOperand,
|
|
385
|
+
bytes32 rightHandOperand
|
|
386
|
+
) external returns (bytes32 success, bytes32 result);
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @notice Performs a division between two encrypted values with division-by-zero check.
|
|
390
|
+
* If the operation succeeds, the value of the success handle will be an encrypted
|
|
391
|
+
* `true` and the result handle's value will be the encrypted quotient.
|
|
392
|
+
* If the operation fails (e.g., due to division by zero), the success handle will contain
|
|
393
|
+
* an encrypted `false` and the result handle will contain an encrypted `0`.
|
|
394
|
+
* @param numerator Value to be divided
|
|
395
|
+
* @param denominator Value to divide by
|
|
396
|
+
* @return success Whether the operation was successful
|
|
397
|
+
* @return result Result handle
|
|
398
|
+
*/
|
|
399
|
+
function safeDiv(
|
|
400
|
+
bytes32 numerator,
|
|
401
|
+
bytes32 denominator
|
|
402
|
+
) external returns (bytes32 success, bytes32 result);
|
|
345
403
|
|
|
346
404
|
/**
|
|
347
405
|
* @notice Selects between two encrypted values based on a condition
|
package/contracts/sdk/Nox.sol
CHANGED
|
@@ -21,7 +21,7 @@ library Nox {
|
|
|
21
21
|
* Supports Arbitrum Mainnet (42161), Arbitrum Sepolia (421614), and local dev chains (31337),
|
|
22
22
|
* including local forks of each network.
|
|
23
23
|
*/
|
|
24
|
-
function noxComputeContract()
|
|
24
|
+
function noxComputeContract() internal view returns (address) {
|
|
25
25
|
// Arbitrum mainnet or its fork
|
|
26
26
|
if (block.chainid == 42161) {
|
|
27
27
|
// TODO: Update after mainnet deployment.
|
|
@@ -29,16 +29,16 @@ library Nox {
|
|
|
29
29
|
}
|
|
30
30
|
// Arbitrum Sepolia or its fork
|
|
31
31
|
if (block.chainid == 421614) {
|
|
32
|
-
return
|
|
32
|
+
return 0xE4622fbFCd0bDd482775bBf5b3e72382C0D99208;
|
|
33
33
|
}
|
|
34
34
|
// Local development chain
|
|
35
35
|
if (block.chainid == 31337) {
|
|
36
|
-
return
|
|
36
|
+
return 0x9bdef3F9fEc61eE7cDfE84BDE8398595c6E0b22d;
|
|
37
37
|
}
|
|
38
38
|
revert("Nox: Unsupported chain");
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function _noxComputeContract()
|
|
41
|
+
function _noxComputeContract() private view returns (INoxCompute) {
|
|
42
42
|
return INoxCompute(noxComputeContract());
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -119,19 +119,6 @@ library Nox {
|
|
|
119
119
|
);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/**
|
|
123
|
-
* @dev Convert a plaintext address to an encrypted address.
|
|
124
|
-
*/
|
|
125
|
-
function toEaddress(address value) internal returns (eaddress) {
|
|
126
|
-
return
|
|
127
|
-
eaddress.wrap(
|
|
128
|
-
_noxComputeContract().plaintextToEncrypted(
|
|
129
|
-
bytes32(uint256(uint160(value))),
|
|
130
|
-
TEEType.Address
|
|
131
|
-
)
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
122
|
/**
|
|
136
123
|
* @dev Convert a plaintext value to an encrypted euint16 integer.
|
|
137
124
|
*/
|
|
@@ -182,7 +169,7 @@ library Nox {
|
|
|
182
169
|
bytes calldata handleProof
|
|
183
170
|
) internal returns (ebool) {
|
|
184
171
|
bytes32 handle = externalEbool.unwrap(externalHandle);
|
|
185
|
-
_noxComputeContract().
|
|
172
|
+
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Bool);
|
|
186
173
|
return ebool.wrap(handle);
|
|
187
174
|
}
|
|
188
175
|
|
|
@@ -191,7 +178,7 @@ library Nox {
|
|
|
191
178
|
bytes calldata handleProof
|
|
192
179
|
) internal returns (eaddress) {
|
|
193
180
|
bytes32 handle = externalEaddress.unwrap(externalHandle);
|
|
194
|
-
_noxComputeContract().
|
|
181
|
+
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Address);
|
|
195
182
|
return eaddress.wrap(handle);
|
|
196
183
|
}
|
|
197
184
|
|
|
@@ -200,7 +187,7 @@ library Nox {
|
|
|
200
187
|
bytes calldata handleProof
|
|
201
188
|
) internal returns (euint16) {
|
|
202
189
|
bytes32 handle = externalEuint16.unwrap(externalHandle);
|
|
203
|
-
_noxComputeContract().
|
|
190
|
+
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Uint16);
|
|
204
191
|
return euint16.wrap(handle);
|
|
205
192
|
}
|
|
206
193
|
|
|
@@ -209,7 +196,7 @@ library Nox {
|
|
|
209
196
|
bytes calldata handleProof
|
|
210
197
|
) internal returns (euint256) {
|
|
211
198
|
bytes32 handle = externalEuint256.unwrap(externalHandle);
|
|
212
|
-
_noxComputeContract().
|
|
199
|
+
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Uint256);
|
|
213
200
|
return euint256.wrap(handle);
|
|
214
201
|
}
|
|
215
202
|
|
|
@@ -218,7 +205,7 @@ library Nox {
|
|
|
218
205
|
bytes calldata handleProof
|
|
219
206
|
) internal returns (eint16) {
|
|
220
207
|
bytes32 handle = externalEint16.unwrap(externalHandle);
|
|
221
|
-
_noxComputeContract().
|
|
208
|
+
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Int16);
|
|
222
209
|
return eint16.wrap(handle);
|
|
223
210
|
}
|
|
224
211
|
|
|
@@ -227,7 +214,7 @@ library Nox {
|
|
|
227
214
|
bytes calldata handleProof
|
|
228
215
|
) internal returns (eint256) {
|
|
229
216
|
bytes32 handle = externalEint256.unwrap(externalHandle);
|
|
230
|
-
_noxComputeContract().
|
|
217
|
+
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Int256);
|
|
231
218
|
return eint256.wrap(handle);
|
|
232
219
|
}
|
|
233
220
|
|
|
@@ -337,7 +324,45 @@ library Nox {
|
|
|
337
324
|
return (ebool.wrap(success), eint256.wrap(result));
|
|
338
325
|
}
|
|
339
326
|
|
|
340
|
-
|
|
327
|
+
function safeMul(euint16 a, euint16 b) internal returns (ebool, euint16) {
|
|
328
|
+
(bytes32 success, bytes32 result) = _safeMul(euint16.unwrap(a), euint16.unwrap(b));
|
|
329
|
+
return (ebool.wrap(success), euint16.wrap(result));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function safeMul(euint256 a, euint256 b) internal returns (ebool, euint256) {
|
|
333
|
+
(bytes32 success, bytes32 result) = _safeMul(euint256.unwrap(a), euint256.unwrap(b));
|
|
334
|
+
return (ebool.wrap(success), euint256.wrap(result));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function safeMul(eint16 a, eint16 b) internal returns (ebool, eint16) {
|
|
338
|
+
(bytes32 success, bytes32 result) = _safeMul(eint16.unwrap(a), eint16.unwrap(b));
|
|
339
|
+
return (ebool.wrap(success), eint16.wrap(result));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function safeMul(eint256 a, eint256 b) internal returns (ebool, eint256) {
|
|
343
|
+
(bytes32 success, bytes32 result) = _safeMul(eint256.unwrap(a), eint256.unwrap(b));
|
|
344
|
+
return (ebool.wrap(success), eint256.wrap(result));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function safeDiv(euint16 a, euint16 b) internal returns (ebool, euint16) {
|
|
348
|
+
(bytes32 success, bytes32 result) = _safeDiv(euint16.unwrap(a), euint16.unwrap(b));
|
|
349
|
+
return (ebool.wrap(success), euint16.wrap(result));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function safeDiv(euint256 a, euint256 b) internal returns (ebool, euint256) {
|
|
353
|
+
(bytes32 success, bytes32 result) = _safeDiv(euint256.unwrap(a), euint256.unwrap(b));
|
|
354
|
+
return (ebool.wrap(success), euint256.wrap(result));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function safeDiv(eint16 a, eint16 b) internal returns (ebool, eint16) {
|
|
358
|
+
(bytes32 success, bytes32 result) = _safeDiv(eint16.unwrap(a), eint16.unwrap(b));
|
|
359
|
+
return (ebool.wrap(success), eint16.wrap(result));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function safeDiv(eint256 a, eint256 b) internal returns (ebool, eint256) {
|
|
363
|
+
(bytes32 success, bytes32 result) = _safeDiv(eint256.unwrap(a), eint256.unwrap(b));
|
|
364
|
+
return (ebool.wrap(success), eint256.wrap(result));
|
|
365
|
+
}
|
|
341
366
|
|
|
342
367
|
function select(ebool condition, euint16 ifTrue, euint16 ifFalse) internal returns (euint16) {
|
|
343
368
|
return
|
|
@@ -871,6 +896,86 @@ library Nox {
|
|
|
871
896
|
return _noxComputeContract().isPubliclyDecryptable(eint256.unwrap(handle));
|
|
872
897
|
}
|
|
873
898
|
|
|
899
|
+
// ============ Public decryption proof verification ============
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* @dev Verifies a decryption proof and returns the decrypted boolean value.
|
|
903
|
+
*/
|
|
904
|
+
function publicDecrypt(ebool handle, bytes calldata decryptionProof) internal returns (bool) {
|
|
905
|
+
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
906
|
+
ebool.unwrap(handle),
|
|
907
|
+
decryptionProof
|
|
908
|
+
);
|
|
909
|
+
return abi.decode(result, (bool));
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* @dev Verifies a decryption proof and returns the decrypted address value.
|
|
914
|
+
*/
|
|
915
|
+
function publicDecrypt(
|
|
916
|
+
eaddress handle,
|
|
917
|
+
bytes calldata decryptionProof
|
|
918
|
+
) internal returns (address) {
|
|
919
|
+
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
920
|
+
eaddress.unwrap(handle),
|
|
921
|
+
decryptionProof
|
|
922
|
+
);
|
|
923
|
+
return abi.decode(result, (address));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* @dev Verifies a decryption proof and returns the decrypted uint16 value.
|
|
928
|
+
*/
|
|
929
|
+
function publicDecrypt(
|
|
930
|
+
euint16 handle,
|
|
931
|
+
bytes calldata decryptionProof
|
|
932
|
+
) internal returns (uint16) {
|
|
933
|
+
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
934
|
+
euint16.unwrap(handle),
|
|
935
|
+
decryptionProof
|
|
936
|
+
);
|
|
937
|
+
return abi.decode(result, (uint16));
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* @dev Verifies a decryption proof and returns the decrypted uint256 value.
|
|
942
|
+
*/
|
|
943
|
+
function publicDecrypt(
|
|
944
|
+
euint256 handle,
|
|
945
|
+
bytes calldata decryptionProof
|
|
946
|
+
) internal returns (uint256) {
|
|
947
|
+
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
948
|
+
euint256.unwrap(handle),
|
|
949
|
+
decryptionProof
|
|
950
|
+
);
|
|
951
|
+
return abi.decode(result, (uint256));
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* @dev Verifies a decryption proof and returns the decrypted int16 value.
|
|
956
|
+
*/
|
|
957
|
+
function publicDecrypt(eint16 handle, bytes calldata decryptionProof) internal returns (int16) {
|
|
958
|
+
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
959
|
+
eint16.unwrap(handle),
|
|
960
|
+
decryptionProof
|
|
961
|
+
);
|
|
962
|
+
return abi.decode(result, (int16));
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* @dev Verifies a decryption proof and returns the decrypted int256 value.
|
|
967
|
+
*/
|
|
968
|
+
function publicDecrypt(
|
|
969
|
+
eint256 handle,
|
|
970
|
+
bytes calldata decryptionProof
|
|
971
|
+
) internal returns (int256) {
|
|
972
|
+
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
973
|
+
eint256.unwrap(handle),
|
|
974
|
+
decryptionProof
|
|
975
|
+
);
|
|
976
|
+
return abi.decode(result, (int256));
|
|
977
|
+
}
|
|
978
|
+
|
|
874
979
|
// ============ Private helpers ============
|
|
875
980
|
|
|
876
981
|
function _assertInitialized(bytes32 handle) private pure {
|
|
@@ -913,6 +1018,18 @@ library Nox {
|
|
|
913
1018
|
return _noxComputeContract().safeSub(a, b);
|
|
914
1019
|
}
|
|
915
1020
|
|
|
1021
|
+
function _safeMul(bytes32 a, bytes32 b) private returns (bytes32, bytes32) {
|
|
1022
|
+
_assertInitialized(a);
|
|
1023
|
+
_assertInitialized(b);
|
|
1024
|
+
return _noxComputeContract().safeMul(a, b);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
function _safeDiv(bytes32 a, bytes32 b) private returns (bytes32, bytes32) {
|
|
1028
|
+
_assertInitialized(a);
|
|
1029
|
+
_assertInitialized(b);
|
|
1030
|
+
return _noxComputeContract().safeDiv(a, b);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
916
1033
|
function _select(bytes32 condition, bytes32 ifTrue, bytes32 ifFalse) private returns (bytes32) {
|
|
917
1034
|
_assertInitialized(condition);
|
|
918
1035
|
_assertInitialized(ifTrue);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iexec-nox/nox-protocol-contracts",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.7",
|
|
4
4
|
"description": "Nox protocol smart contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Nox",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"set-gateway": "pnpm hardhat run scripts/set-gateway.ts",
|
|
27
27
|
"set-kms-public-key": "pnpm hardhat run scripts/set-kms-public-key.ts",
|
|
28
28
|
"upgrade": "pnpm hardhat run scripts/upgrade.ts",
|
|
29
|
+
"verify": "pnpm hardhat ignition verify",
|
|
29
30
|
"format": "pnpm prettier --write .",
|
|
30
31
|
"format:check": "pnpm prettier --check ."
|
|
31
32
|
},
|
|
@@ -35,22 +36,22 @@
|
|
|
35
36
|
]
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"@openzeppelin/contracts": "^5.
|
|
39
|
-
"@openzeppelin/contracts-upgradeable": "^5.
|
|
39
|
+
"@openzeppelin/contracts": "^5.6.1",
|
|
40
|
+
"@openzeppelin/contracts-upgradeable": "^5.6.1",
|
|
40
41
|
"encrypted-types": "^0.0.4"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@nomicfoundation/hardhat-ignition": "^3.0.
|
|
44
|
-
"@nomicfoundation/hardhat-toolbox-viem": "^5.0.
|
|
45
|
-
"@types/node": "^22.
|
|
44
|
+
"@nomicfoundation/hardhat-ignition": "^3.0.9",
|
|
45
|
+
"@nomicfoundation/hardhat-toolbox-viem": "^5.0.2",
|
|
46
|
+
"@types/node": "^22.19.13",
|
|
46
47
|
"forge-std": "github:foundry-rs/forge-std#v1.9.4",
|
|
47
|
-
"hardhat": "^3.1.
|
|
48
|
+
"hardhat": "^3.1.11",
|
|
48
49
|
"husky": "^9.1.7",
|
|
49
|
-
"lint-staged": "^16.
|
|
50
|
-
"prettier": "^3.
|
|
50
|
+
"lint-staged": "^16.3.1",
|
|
51
|
+
"prettier": "^3.8.1",
|
|
51
52
|
"prettier-plugin-solidity": "^2.2.1",
|
|
52
|
-
"typescript": "
|
|
53
|
-
"viem": "^2.
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"viem": "^2.46.3"
|
|
54
55
|
},
|
|
55
56
|
"homepage": "https://github.com/iExec-Nox/nox-protocol-contracts#readme",
|
|
56
57
|
"repository": {
|