@obolnetwork/obol-sdk 2.1.2 → 2.2.1
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/dist/cjs/package.json +4 -1
- package/dist/cjs/src/constants.js +9 -1
- package/dist/cjs/src/index.js +1 -0
- package/dist/cjs/src/types.js +18 -1
- package/dist/cjs/src/utils.js +11 -1
- package/dist/cjs/src/verification/common.js +36 -29
- package/dist/cjs/src/verification/signature-validator.js +72 -0
- package/dist/cjs/test/fixtures.js +88 -1
- package/dist/cjs/test/methods.test.js +68 -54
- package/dist/esm/package.json +4 -1
- package/dist/esm/src/constants.js +8 -0
- package/dist/esm/src/index.js +1 -0
- package/dist/esm/src/types.js +17 -0
- package/dist/esm/src/utils.js +10 -1
- package/dist/esm/src/verification/common.js +36 -29
- package/dist/esm/src/verification/signature-validator.js +63 -0
- package/dist/esm/test/fixtures.js +87 -0
- package/dist/esm/test/methods.test.js +69 -55
- package/dist/types/src/constants.d.ts +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/types.d.ts +7 -1
- package/dist/types/src/utils.d.ts +2 -1
- package/dist/types/src/verification/signature-validator.d.ts +19 -0
- package/dist/types/test/fixtures.d.ts +51 -0
- package/package.json +4 -1
- package/src/constants.ts +10 -0
- package/src/index.ts +1 -0
- package/src/types.ts +23 -0
- package/src/utils.ts +11 -3
- package/src/verification/common.ts +44 -33
- package/src/verification/signature-validator.ts +96 -0
package/dist/esm/src/utils.js
CHANGED
|
@@ -7,7 +7,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import {
|
|
10
|
+
import { ethers } from 'ethers';
|
|
11
|
+
import { DefinitionFlow, PROVIDER_MAP } from './constants';
|
|
12
|
+
import { FORK_NAMES } from './types';
|
|
11
13
|
export const hexWithout0x = (hex) => {
|
|
12
14
|
return hex.slice(2, hex.length);
|
|
13
15
|
};
|
|
@@ -57,3 +59,10 @@ export const isContractAvailable = (contractAddress, provider, bytecode) => __aw
|
|
|
57
59
|
}
|
|
58
60
|
return !!code && code !== '0x' && code !== '0x0';
|
|
59
61
|
});
|
|
62
|
+
export const getProvider = (chainId) => {
|
|
63
|
+
const rpcUrl = PROVIDER_MAP[chainId];
|
|
64
|
+
if (!rpcUrl) {
|
|
65
|
+
throw new Error(`No provider configured for ${FORK_NAMES[chainId]}`);
|
|
66
|
+
}
|
|
67
|
+
return new ethers.JsonRpcProvider(rpcUrl);
|
|
68
|
+
};
|
|
@@ -14,13 +14,12 @@ import { FORK_MAPPING, } from '../types.js';
|
|
|
14
14
|
import * as semver from 'semver';
|
|
15
15
|
import { clusterDefinitionContainerTypeV1X6, hashClusterDefinitionV1X6, hashClusterLockV1X6, verifyDVV1X6, } from './v1.6.0.js';
|
|
16
16
|
import { clusterDefinitionContainerTypeV1X7, hashClusterDefinitionV1X7, hashClusterLockV1X7, verifyDVV1X7, } from './v1.7.0.js';
|
|
17
|
-
import { ethers } from 'ethers';
|
|
18
17
|
import { DOMAIN_APPLICATION_BUILDER, DOMAIN_DEPOSIT, DefinitionFlow, GENESIS_VALIDATOR_ROOT, signCreatorConfigHashPayload, signEnrPayload, signOperatorConfigHashPayload, } from '../constants.js';
|
|
19
|
-
import { SignTypedDataVersion, TypedDataUtils } from '@metamask/eth-sig-util';
|
|
20
18
|
import { builderRegistrationMessageType, depositMessageType, forkDataType, signingRootType, } from './sszTypes.js';
|
|
21
19
|
import { definitionFlow, hexWithout0x } from '../utils.js';
|
|
22
20
|
import { ENR } from '@chainsafe/discv5';
|
|
23
21
|
import { clusterDefinitionContainerTypeV1X8, hashClusterDefinitionV1X8, hashClusterLockV1X8, verifyDVV1X8, } from './v1.8.0.js';
|
|
22
|
+
import { validateAddressSignature } from './signature-validator.js';
|
|
24
23
|
// cluster-definition hash
|
|
25
24
|
/**
|
|
26
25
|
* @param cluster The cluster configuration or the cluster definition
|
|
@@ -78,62 +77,70 @@ export const clusterLockHash = (clusterLock) => {
|
|
|
78
77
|
};
|
|
79
78
|
// Lock verification
|
|
80
79
|
// cluster-definition signatures verification
|
|
81
|
-
const
|
|
80
|
+
const validatePOSTConfigHashSigner = (address, signature, configHash, chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
81
|
try {
|
|
83
|
-
const sig = ethers.Signature.from(signature);
|
|
84
82
|
const data = signCreatorConfigHashPayload({ creator_config_hash: configHash }, chainId);
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
return yield validateAddressSignature({
|
|
84
|
+
address,
|
|
85
|
+
token: signature,
|
|
86
|
+
data,
|
|
87
|
+
chainId,
|
|
88
|
+
});
|
|
87
89
|
}
|
|
88
90
|
catch (err) {
|
|
89
91
|
throw err;
|
|
90
92
|
}
|
|
91
|
-
};
|
|
92
|
-
const
|
|
93
|
+
});
|
|
94
|
+
const validatePUTConfigHashSigner = (address, signature, configHash, chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
93
95
|
try {
|
|
94
|
-
const sig = ethers.Signature.from(signature);
|
|
95
96
|
const data = signOperatorConfigHashPayload({ operator_config_hash: configHash }, chainId);
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
return yield validateAddressSignature({
|
|
98
|
+
address,
|
|
99
|
+
token: signature,
|
|
100
|
+
data,
|
|
101
|
+
chainId,
|
|
102
|
+
});
|
|
98
103
|
}
|
|
99
104
|
catch (err) {
|
|
100
105
|
throw err;
|
|
101
106
|
}
|
|
102
|
-
};
|
|
103
|
-
const
|
|
107
|
+
});
|
|
108
|
+
const validateEnrSigner = (address, signature, payload, chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
109
|
try {
|
|
105
|
-
const sig = ethers.Signature.from(signature);
|
|
106
110
|
const data = signEnrPayload({ enr: payload }, chainId);
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
return yield validateAddressSignature({
|
|
112
|
+
address,
|
|
113
|
+
token: signature,
|
|
114
|
+
data,
|
|
115
|
+
chainId,
|
|
116
|
+
});
|
|
109
117
|
}
|
|
110
118
|
catch (err) {
|
|
111
119
|
throw err;
|
|
112
120
|
}
|
|
113
|
-
};
|
|
114
|
-
const verifyDefinitionSignatures = (clusterDefinition, definitionType) => {
|
|
121
|
+
});
|
|
122
|
+
const verifyDefinitionSignatures = (clusterDefinition, definitionType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
123
|
if (definitionType === DefinitionFlow.Charon) {
|
|
116
124
|
return true;
|
|
117
125
|
}
|
|
118
126
|
else {
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
127
|
+
const isPOSTConfigHashSignerValid = yield validatePOSTConfigHashSigner(clusterDefinition.creator.address, clusterDefinition.creator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version]);
|
|
128
|
+
if (!isPOSTConfigHashSignerValid) {
|
|
121
129
|
return false;
|
|
122
130
|
}
|
|
123
131
|
if (definitionType === DefinitionFlow.Solo) {
|
|
124
132
|
return true;
|
|
125
133
|
}
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
130
|
-
enrSigner !== operator.address.toLowerCase()) {
|
|
134
|
+
for (const operator of clusterDefinition.operators) {
|
|
135
|
+
const isPUTConfigHashSignerValid = yield validatePUTConfigHashSigner(operator.address, operator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version]);
|
|
136
|
+
const isENRSignerValid = yield validateEnrSigner(operator.address, operator.enr_signature, operator.enr, FORK_MAPPING[clusterDefinition.fork_version]);
|
|
137
|
+
if (!isPUTConfigHashSignerValid || !isENRSignerValid) {
|
|
131
138
|
return false;
|
|
132
139
|
}
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
135
142
|
}
|
|
136
|
-
};
|
|
143
|
+
});
|
|
137
144
|
// cluster-lock data verification
|
|
138
145
|
const computeSigningRoot = (sszObjectRoot, domain) => {
|
|
139
146
|
const signingRootDefaultValue = signingRootType.defaultValue();
|
|
@@ -260,7 +267,7 @@ export const isValidClusterLock = (clusterLock) => __awaiter(void 0, void 0, voi
|
|
|
260
267
|
if (definitionType == null) {
|
|
261
268
|
return false;
|
|
262
269
|
}
|
|
263
|
-
const isValidDefinitionData = verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
|
|
270
|
+
const isValidDefinitionData = yield verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
|
|
264
271
|
if (!isValidDefinitionData) {
|
|
265
272
|
return false;
|
|
266
273
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
11
|
+
import { ethers } from 'ethers';
|
|
12
|
+
import { SignTypedDataVersion, TypedDataUtils, } from '@metamask/eth-sig-util';
|
|
13
|
+
import Safe from '@safe-global/protocol-kit';
|
|
14
|
+
import { PROVIDER_MAP } from '../constants';
|
|
15
|
+
import { hashTypedData } from '@safe-global/protocol-kit/dist/src/utils';
|
|
16
|
+
import { isContractAvailable, getProvider } from '../utils';
|
|
17
|
+
export const validateAddressSignature = ({ address, token, data, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
try {
|
|
19
|
+
const provider = getProvider(chainId);
|
|
20
|
+
if (provider) {
|
|
21
|
+
const contractAddress = yield isContractAvailable(address, provider);
|
|
22
|
+
if (contractAddress) {
|
|
23
|
+
return yield validateSmartContractSignature({
|
|
24
|
+
token,
|
|
25
|
+
data: data,
|
|
26
|
+
address,
|
|
27
|
+
chainId,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return validateEOASignature({ token, data, address });
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return validateEOASignature({ token, data, address });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
export const validateEOASignature = ({ token, data, address, }) => {
|
|
38
|
+
try {
|
|
39
|
+
const sig = ethers.Signature.from(token);
|
|
40
|
+
const digest = TypedDataUtils.eip712Hash(data, SignTypedDataVersion.V4);
|
|
41
|
+
return (ethers.recoverAddress(digest, sig).toLowerCase() ===
|
|
42
|
+
address.toLocaleLowerCase());
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
console.error(`validate EOA Signature error: ${err}`);
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export const validateSmartContractSignature = ({ token, data, address, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
|
+
try {
|
|
51
|
+
const provider = PROVIDER_MAP[chainId];
|
|
52
|
+
const protocolKit = yield Safe.init({
|
|
53
|
+
provider,
|
|
54
|
+
safeAddress: address,
|
|
55
|
+
});
|
|
56
|
+
const messageHash = hashTypedData(data);
|
|
57
|
+
const isValidSignature = yield protocolKit.isValidSignature(messageHash, token);
|
|
58
|
+
return isValidSignature;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
throw new Error(`Error validating smart contract signature: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -420,3 +420,90 @@ export const nullDepositAmountsClusterLockV1X8 = {
|
|
|
420
420
|
'0xdb6332a4ca9c41592511e7480c835b449fea120430e759735f3c24f1a6d496551a516151d2baae21d0cc9469b28dc1122b672af40ebb0e77045da92e0da6a03500',
|
|
421
421
|
],
|
|
422
422
|
};
|
|
423
|
+
export const clusterLockWithSafe = {
|
|
424
|
+
cluster_definition: {
|
|
425
|
+
name: 'Rainy cluster',
|
|
426
|
+
creator: {
|
|
427
|
+
address: '0xEF43Ff87070247EC9D28B482D6474318e9DAFc98',
|
|
428
|
+
config_signature: '0x0c4aaafcef722cf770b419d47a21571b3c73275185a1b719e5709168af6327eb2ee392134e660d6e4ea113abd184dd4f2bd8ee09a4ae33f434433f7575873b981c',
|
|
429
|
+
},
|
|
430
|
+
operators: [
|
|
431
|
+
{
|
|
432
|
+
address: '0xe5b709A14859EdF820347D78E587b1634B0ec771',
|
|
433
|
+
enr: 'enr:-HW4QGrhMVQGLtKv_KlubzcVZM9AUsSVgDPtq4k_0q344ZtWJ6tD4riMP5jfWXXdHIklAm_pHdQv0MbTE1DrXmuEM7yAgmlkgnY0iXNlY3AyNTZrMaECa_cfUuFYiuFQp_dUEEicCBdFWNfzm8QhrNhBwey8PBg',
|
|
434
|
+
config_signature: '0xad813cd706d7469b560570ef13b0afdf36d0637e9bc5362ec358c218085d9b231b1377f0078e1ed9c2af99fddb3d7608c03bf8aee6edc931c73d7204a7fdee181b',
|
|
435
|
+
enr_signature: '0x304c022e74c13bfd492219efc52fb133f728afa01fefa9fe1d2540ce7c23ca730dd8248b0ad76630dd120975085d77fba20c050620f1e9583590d003110dbca41c',
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
address: '0x83b7CA5be230A50DEE511657CCFF262ba04D0353',
|
|
439
|
+
enr: 'enr:-HW4QNGbmr_0YhIeTUQfljlDGGCnR8RGhtWr_gjMI8a5BwigGur2qFgGZrqSumAvk4gX9ciP2Tu74uOdOZPw6nvTi7aAgmlkgnY0iXNlY3AyNTZrMaEC0nrshQsWtEOphaLrZVTzPFOz8EAJweGbhXDtSsXPiLY',
|
|
440
|
+
config_signature: '0x41dfb02ff13775d53ad3f739151c44952ff98a3283d124b6f69782e7458150e92771e32f965803391818d23ad95c9c7bcba15e896c3a576d1a700a16d8cb29fe1b',
|
|
441
|
+
enr_signature: '0x388460abfdeded56839e4ae1480ea48a70ac44a89963bda24152e4f879a9d22918c3e77733f2ccaed5c2745f503565d651a357c6a50634a597d61e16c384961e1c',
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
address: '0x123C1cD7AeE10b525D09b848D4A62596958f7D9C',
|
|
445
|
+
enr: 'enr:-HW4QMpDdYHJR3TlnWsnuIKfbmg2aRMMcAFlhTuuZa0Grd3dMFC0AEPHvHM2cKboULC4RUl2Dz7-LpwL2EJRGhJzxcyAgmlkgnY0iXNlY3AyNTZrMaEC73iR5dcgO_bJ8tvsHLrFglwspGFhaAbiBtGAHAJPD9c',
|
|
446
|
+
config_signature: '0x91d23aefc02263fdf0edd0dcfdf26eee01c4d673b97a91a099fc10dfc01f55fa690eccc809658ea2ed17383df355f80a62bd96df15e40a2e2f740189fa6537f61b',
|
|
447
|
+
enr_signature: '0x1035b284df3b1d60434bfe2d139f1472ed29663e2af719779bb5da35537f202032ab0f7cdc540c724c8e80bedfba247809d741260201fbb5bcbfc7f0813334241b',
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
address: '0x169212AE1A834C1E0efd8C827Abdbc2b5557B86C', // safe address
|
|
451
|
+
enr: 'enr:-HW4QPwheIq51G9r9k-6GYrhp9-y-hN2SSowqgQemvPyBu9cOKERqPeqy0JuZ0yKyWiji406eK8fFeHX5fKU-6OI3u6AgmlkgnY0iXNlY3AyNTZrMaECaARWVFt5KNkHPno5YYdFBUrkLU5EmOB3K0Ns9hp-s-s',
|
|
452
|
+
config_signature: '0xf97cc2536afe96e43e07aac140254cea37b0345573b12476f8840e571095d72049aa9e03a099ef2965523f83648c4323cd16ec2ee3f5e6efac177da13bcf6eb21c',
|
|
453
|
+
enr_signature: '0x422f471d54eb19fc52e076bb11fd5c8de895734ed111b66708ef805ae124f87b7519e841e59eccdb75c8328223efd8bb324f3fd7d3048f037419bb83e03caae41b',
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
uuid: '3ac43657-b465-49a8-94aa-25b7f7979c2a',
|
|
457
|
+
version: 'v1.8.0',
|
|
458
|
+
timestamp: '2025-01-08T15:18:28.598Z',
|
|
459
|
+
num_validators: 1,
|
|
460
|
+
threshold: 3,
|
|
461
|
+
validators: [
|
|
462
|
+
{
|
|
463
|
+
fee_recipient_address: '0xe5b709A14859EdF820347D78E587b1634B0ec771',
|
|
464
|
+
withdrawal_address: '0xe5b709A14859EdF820347D78E587b1634B0ec771',
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
dkg_algorithm: 'default',
|
|
468
|
+
fork_version: '0x01017000',
|
|
469
|
+
deposit_amounts: ['32000000000'],
|
|
470
|
+
config_hash: '0x1849d9434a5ce7a7d15e1e030a48740a307c80424608c6618386d78a2e597fb3',
|
|
471
|
+
definition_hash: '0x85dabfcebf9bf850521cdb65ea6083c462d9ac57eb62d9a9eb47761ec10bc80e',
|
|
472
|
+
},
|
|
473
|
+
distributed_validators: [
|
|
474
|
+
{
|
|
475
|
+
distributed_public_key: '0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92',
|
|
476
|
+
public_shares: [
|
|
477
|
+
'0x8d150f921a54fe258bb57f568288de87e392946124df65861afbe52d2a3be6a1fa09d19beb62f879b0f18b2e3b26f69f',
|
|
478
|
+
'0x8fa69ec7c31eeda66007bc3d49a3d5654dbe6a3c0890898ea5913ed263a0efc3f6744064dbde3ee0cc1d2f08db33b4d7',
|
|
479
|
+
'0xb862933b7b7adb33cfb3de1052f7dd9e45dfa30accda01dfc4d3c38716747504aade8f832fa5a256dcaf13f80dbbb38f',
|
|
480
|
+
'0xb5a15d86945c7ff38e0b09158a3264033e6dbaebf2ec9f3426988e6d24e38b3dbd6fe413d3253b308404bb691757fb2e',
|
|
481
|
+
],
|
|
482
|
+
builder_registration: {
|
|
483
|
+
message: {
|
|
484
|
+
fee_recipient: '0xe5b709a14859edf820347d78e587b1634b0ec771',
|
|
485
|
+
gas_limit: 30000000,
|
|
486
|
+
timestamp: 1696000704,
|
|
487
|
+
pubkey: '0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92',
|
|
488
|
+
},
|
|
489
|
+
signature: '0xa5e4220e7d8e7fd4430590c70a27104473b15c8ea03b2d1571bc86bbbcb443e2b7467f75a7e77b5d9b1a10d5fbc335cf0d59c3b3bc1b6d036b2e22511abcf41373567a15185f011ce0129741f0ae0e9e49759f9c5c0ce62f2a99f30e8c7df996',
|
|
490
|
+
},
|
|
491
|
+
partial_deposit_data: [
|
|
492
|
+
{
|
|
493
|
+
pubkey: '0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92',
|
|
494
|
+
withdrawal_credentials: '0x010000000000000000000000e5b709a14859edf820347d78e587b1634b0ec771',
|
|
495
|
+
amount: '32000000000',
|
|
496
|
+
signature: '0x90aff3cb35c577644cb5fe3036ae8aeb5ac6cc2ce2a33a4332e2e883dc48f8fb71165531862d3035a4b6e3a60215d661021672a59f9406e3bd04b5f88f4f77c5f54a9ff37167807010123cdcc230040e5a485b5eb36704fd276dbf541f7151a8',
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
signature_aggregate: '0x86fdeae3db45215d5f24fc19645f8f95e91fffd1a1d0a87668d3f27724fa735b85fb15de120106b8463ff1d32823813e0f139ee7cb6d3b026c6b81cc96566819817742a13e24ac44fc6571385d5b0fdbaace7e50e2cfbcd692e36b1ca25436e4',
|
|
502
|
+
lock_hash: '0xe03b0fdf6d6a7d38360b0ceac1f0380b051f710e80d0e166b855ef3148d46733',
|
|
503
|
+
node_signatures: [
|
|
504
|
+
'0x564328bcad4cac498aa51b7e7e041cf264e9c65c95138df1fc00d4bf714bc38419c43b5a32662d5c8b747d646d6e77830b2bfd62fa8402e136147c244acdce0300',
|
|
505
|
+
'0xb0e984efd2874fc4bc97bb9c42dccbc723080cf29a20a3cf0b05867ebe87bc430c29a560936000439fcde610ce91452ced763586d3bcca24fbe4fe83bb39202601',
|
|
506
|
+
'0xf3633a8e59db2e8d253ffe1b284f9edab1c1c1aca6288c3a3b2e9cb4eb7e789e23366fc325274f13b7311b9faf53665c80b5cb1f93810713f7b094cd2bec1d9900',
|
|
507
|
+
'0x4067921a5257efe4ceb103f2129faaa7a502781157b3b54e5efca559c558c2b43b89f30962e87df88fbf62250049a31888fcd62735d54b7553e5dc75c3b6ae0901',
|
|
508
|
+
],
|
|
509
|
+
};
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { ethers, JsonRpcProvider } from 'ethers';
|
|
11
11
|
import { Client, validateClusterLock } from '../src/index';
|
|
12
|
-
import { clusterConfigV1X7, clusterConfigV1X8, clusterLockV1X6, clusterLockV1X7, clusterLockV1X8, nullDepositAmountsClusterLockV1X8, } from './fixtures.js';
|
|
12
|
+
import { clusterConfigV1X7, clusterConfigV1X8, clusterLockV1X6, clusterLockV1X7, clusterLockV1X8, clusterLockWithSafe, nullDepositAmountsClusterLockV1X8, } from './fixtures.js';
|
|
13
13
|
import { SDK_VERSION } from '../src/constants';
|
|
14
14
|
import { Base } from '../src/base';
|
|
15
15
|
import { validatePayload } from '../src/ajv';
|
|
@@ -18,6 +18,7 @@ import { setupServer } from 'msw/node';
|
|
|
18
18
|
import { hashTermsAndConditions } from '../src/verification/termsAndConditions';
|
|
19
19
|
import * as utils from '../src/utils';
|
|
20
20
|
import * as splitsHelpers from '../src/splitHelpers';
|
|
21
|
+
jest.setTimeout(20000);
|
|
21
22
|
/* eslint no-new: 0 */
|
|
22
23
|
describe('Cluster Client', () => {
|
|
23
24
|
var _a, _b;
|
|
@@ -136,6 +137,9 @@ describe('Cluster Client without a signer', () => {
|
|
|
136
137
|
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
137
138
|
chainId: 17000,
|
|
138
139
|
});
|
|
140
|
+
beforeAll(() => {
|
|
141
|
+
jest.restoreAllMocks();
|
|
142
|
+
});
|
|
139
143
|
test('createClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
140
144
|
try {
|
|
141
145
|
yield clientInstance.createClusterDefinition(clusterConfigV1X8);
|
|
@@ -177,6 +181,10 @@ describe('Cluster Client without a signer', () => {
|
|
|
177
181
|
version: 'null deposit_amounts v1.8.0',
|
|
178
182
|
clusterLock: nullDepositAmountsClusterLockV1X8,
|
|
179
183
|
},
|
|
184
|
+
{
|
|
185
|
+
version: 'Cluster with safe address v1.8.0',
|
|
186
|
+
clusterLock: clusterLockWithSafe,
|
|
187
|
+
},
|
|
180
188
|
])("$version: 'should return true on verified cluster lock'", ({ clusterLock }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
181
189
|
const isValidLock = yield validateClusterLock(clusterLock);
|
|
182
190
|
expect(isValidLock).toEqual(true);
|
|
@@ -195,37 +203,40 @@ describe('Cluster Client without a signer', () => {
|
|
|
195
203
|
}));
|
|
196
204
|
});
|
|
197
205
|
describe('createObolRewardsSplit', () => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
let clientInstance, clientInstanceWithourSigner, mockSplitRecipients, mockPrincipalRecipient, mockEtherAmount, mockSigner;
|
|
207
|
+
beforeAll(() => {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
jest
|
|
210
|
+
.spyOn(utils, 'isContractAvailable')
|
|
211
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
212
|
+
jest
|
|
213
|
+
.spyOn(splitsHelpers, 'predictSplitterAddress')
|
|
214
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
|
|
215
|
+
jest.spyOn(splitsHelpers, 'handleDeployOWRAndSplitter').mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
216
|
+
return yield Promise.resolve({
|
|
217
|
+
withdrawal_address: '0xWithdrawalAddress',
|
|
218
|
+
fee_recipient_address: '0xFeeRecipientAddress',
|
|
219
|
+
});
|
|
220
|
+
}));
|
|
221
|
+
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
222
|
+
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
223
|
+
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
224
|
+
const wallet = new ethers.Wallet(privateKey, provider);
|
|
225
|
+
mockSigner = wallet.connect(provider);
|
|
226
|
+
clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
|
|
227
|
+
clientInstanceWithourSigner = new Client({
|
|
228
|
+
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
229
|
+
chainId: 17000,
|
|
209
230
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
219
|
-
chainId: 17000,
|
|
231
|
+
mockSplitRecipients = [
|
|
232
|
+
{
|
|
233
|
+
account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
|
|
234
|
+
percentAllocation: 99,
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
mockPrincipalRecipient = '0x86B8145c98e5BD25BA722645b15eD65f024a87EC';
|
|
238
|
+
mockEtherAmount = 64;
|
|
220
239
|
});
|
|
221
|
-
const mockSplitRecipients = [
|
|
222
|
-
{
|
|
223
|
-
account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
|
|
224
|
-
percentAllocation: 99,
|
|
225
|
-
},
|
|
226
|
-
];
|
|
227
|
-
const mockPrincipalRecipient = '0x86B8145c98e5BD25BA722645b15eD65f024a87EC';
|
|
228
|
-
const mockEtherAmount = 64;
|
|
229
240
|
it('should throw an error if signer is not defined', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
230
241
|
yield expect(clientInstanceWithourSigner.createObolRewardsSplit({
|
|
231
242
|
splitRecipients: mockSplitRecipients,
|
|
@@ -289,32 +300,35 @@ describe('createObolRewardsSplit', () => {
|
|
|
289
300
|
}));
|
|
290
301
|
});
|
|
291
302
|
describe('createObolTotalSplit', () => {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
310
|
-
|
|
303
|
+
let clientInstanceWithourSigner, mockSplitRecipients, mockSigner, clientInstance;
|
|
304
|
+
beforeAll(() => {
|
|
305
|
+
var _a, _b;
|
|
306
|
+
jest
|
|
307
|
+
.spyOn(utils, 'isContractAvailable')
|
|
308
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
309
|
+
jest
|
|
310
|
+
.spyOn(splitsHelpers, 'predictSplitterAddress')
|
|
311
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
|
|
312
|
+
jest
|
|
313
|
+
.spyOn(splitsHelpers, 'deploySplitterContract')
|
|
314
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xSplitterAddress'); }));
|
|
315
|
+
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
316
|
+
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
317
|
+
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
318
|
+
const wallet = new ethers.Wallet(privateKey, provider);
|
|
319
|
+
mockSigner = wallet.connect(provider);
|
|
320
|
+
clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
|
|
321
|
+
clientInstanceWithourSigner = new Client({
|
|
322
|
+
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
323
|
+
chainId: 17000,
|
|
324
|
+
});
|
|
325
|
+
mockSplitRecipients = [
|
|
326
|
+
{
|
|
327
|
+
account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
|
|
328
|
+
percentAllocation: 99.9,
|
|
329
|
+
},
|
|
330
|
+
];
|
|
311
331
|
});
|
|
312
|
-
const mockSplitRecipients = [
|
|
313
|
-
{
|
|
314
|
-
account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
|
|
315
|
-
percentAllocation: 99.9,
|
|
316
|
-
},
|
|
317
|
-
];
|
|
318
332
|
it('should throw an error if signer is not defined', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
319
333
|
yield expect(clientInstanceWithourSigner.createObolTotalSplit({
|
|
320
334
|
splitRecipients: mockSplitRecipients,
|
|
@@ -110,3 +110,4 @@ export declare const CHAIN_CONFIGURATION: {
|
|
|
110
110
|
export declare const DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = 1;
|
|
111
111
|
export declare const DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = 0.1;
|
|
112
112
|
export declare const OBOL_SDK_EMAIL = "sdk@dvlabs.tech";
|
|
113
|
+
export declare const PROVIDER_MAP: Record<number, string>;
|
|
@@ -3,6 +3,7 @@ import { Base } from './base.js';
|
|
|
3
3
|
import { type RewardsSplitPayload, type ClusterDefinition, type ClusterLock, type ClusterPayload, type OperatorPayload, type TotalSplitPayload, type ClusterValidator, type ETH_ADDRESS, type OWRTranches } from './types.js';
|
|
4
4
|
export * from './types.js';
|
|
5
5
|
export * from './services.js';
|
|
6
|
+
export * from './verification/signature-validator.js';
|
|
6
7
|
/**
|
|
7
8
|
* Obol sdk Client can be used for creating, managing and activating distributed validators.
|
|
8
9
|
*/
|
|
@@ -9,8 +9,14 @@ export declare enum FORK_MAPPING {
|
|
|
9
9
|
/** Gnosis Chain. */
|
|
10
10
|
'0x00000064' = 100,
|
|
11
11
|
/** Holesky. */
|
|
12
|
-
'0x01017000' = 17000
|
|
12
|
+
'0x01017000' = 17000,
|
|
13
|
+
/** Sepolia. */
|
|
14
|
+
'0x90000069' = 11155111
|
|
13
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Permitted Chain Names
|
|
18
|
+
*/
|
|
19
|
+
export declare const FORK_NAMES: Record<number, string>;
|
|
14
20
|
/**
|
|
15
21
|
* Node operator data
|
|
16
22
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Provider } from 'ethers';
|
|
1
|
+
import { ethers, type Provider } from 'ethers';
|
|
2
2
|
import { DefinitionFlow } from './constants';
|
|
3
3
|
import { type ClusterDefinition } from './types';
|
|
4
4
|
export declare const hexWithout0x: (hex: string) => string;
|
|
@@ -6,3 +6,4 @@ export declare const strToUint8Array: (str: string) => Uint8Array;
|
|
|
6
6
|
export declare const definitionFlow: (clusterDefinition: ClusterDefinition) => DefinitionFlow | null;
|
|
7
7
|
export declare const findDeployedBytecode: (contractAddress: string, provider: Provider) => Promise<string>;
|
|
8
8
|
export declare const isContractAvailable: (contractAddress: string, provider: Provider, bytecode?: string) => Promise<boolean>;
|
|
9
|
+
export declare const getProvider: (chainId: number) => ethers.Provider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type TypedMessage } from '@metamask/eth-sig-util';
|
|
2
|
+
import { type EIP712TypedData } from '@safe-global/safe-core-sdk-types';
|
|
3
|
+
export declare const validateAddressSignature: ({ address, token, data, chainId, }: {
|
|
4
|
+
address: string;
|
|
5
|
+
token: string;
|
|
6
|
+
data: TypedMessage<any>;
|
|
7
|
+
chainId: number;
|
|
8
|
+
}) => Promise<boolean>;
|
|
9
|
+
export declare const validateEOASignature: ({ token, data, address, }: {
|
|
10
|
+
token: string;
|
|
11
|
+
data: TypedMessage<any>;
|
|
12
|
+
address: string;
|
|
13
|
+
}) => boolean;
|
|
14
|
+
export declare const validateSmartContractSignature: ({ token, data, address, chainId, }: {
|
|
15
|
+
token: string;
|
|
16
|
+
data: EIP712TypedData;
|
|
17
|
+
address: string;
|
|
18
|
+
chainId: number;
|
|
19
|
+
}) => Promise<boolean>;
|
|
@@ -212,3 +212,54 @@ export declare const nullDepositAmountsClusterLockV1X8: {
|
|
|
212
212
|
lock_hash: string;
|
|
213
213
|
node_signatures: string[];
|
|
214
214
|
};
|
|
215
|
+
export declare const clusterLockWithSafe: {
|
|
216
|
+
cluster_definition: {
|
|
217
|
+
name: string;
|
|
218
|
+
creator: {
|
|
219
|
+
address: string;
|
|
220
|
+
config_signature: string;
|
|
221
|
+
};
|
|
222
|
+
operators: {
|
|
223
|
+
address: string;
|
|
224
|
+
enr: string;
|
|
225
|
+
config_signature: string;
|
|
226
|
+
enr_signature: string;
|
|
227
|
+
}[];
|
|
228
|
+
uuid: string;
|
|
229
|
+
version: string;
|
|
230
|
+
timestamp: string;
|
|
231
|
+
num_validators: number;
|
|
232
|
+
threshold: number;
|
|
233
|
+
validators: {
|
|
234
|
+
fee_recipient_address: string;
|
|
235
|
+
withdrawal_address: string;
|
|
236
|
+
}[];
|
|
237
|
+
dkg_algorithm: string;
|
|
238
|
+
fork_version: string;
|
|
239
|
+
deposit_amounts: string[];
|
|
240
|
+
config_hash: string;
|
|
241
|
+
definition_hash: string;
|
|
242
|
+
};
|
|
243
|
+
distributed_validators: {
|
|
244
|
+
distributed_public_key: string;
|
|
245
|
+
public_shares: string[];
|
|
246
|
+
builder_registration: {
|
|
247
|
+
message: {
|
|
248
|
+
fee_recipient: string;
|
|
249
|
+
gas_limit: number;
|
|
250
|
+
timestamp: number;
|
|
251
|
+
pubkey: string;
|
|
252
|
+
};
|
|
253
|
+
signature: string;
|
|
254
|
+
};
|
|
255
|
+
partial_deposit_data: {
|
|
256
|
+
pubkey: string;
|
|
257
|
+
withdrawal_credentials: string;
|
|
258
|
+
amount: string;
|
|
259
|
+
signature: string;
|
|
260
|
+
}[];
|
|
261
|
+
}[];
|
|
262
|
+
signature_aggregate: string;
|
|
263
|
+
lock_hash: string;
|
|
264
|
+
node_signatures: string[];
|
|
265
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obolnetwork/obol-sdk",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "A package for creating Distributed Validators using the Obol API.",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/obolnetwork/obol-sdk/issues"
|
|
@@ -44,9 +44,12 @@
|
|
|
44
44
|
"@chainsafe/discv5": "^0.5.1",
|
|
45
45
|
"@chainsafe/ssz": "^0.14.0",
|
|
46
46
|
"@metamask/eth-sig-util": "^7.0.1",
|
|
47
|
+
"@safe-global/protocol-kit": "4.1.1",
|
|
48
|
+
"@safe-global/safe-core-sdk-types": "5.1.0",
|
|
47
49
|
"@types/pdf-parse": "^1.1.4",
|
|
48
50
|
"ajv": "^8.12.0",
|
|
49
51
|
"cross-fetch": "^3.1.5",
|
|
52
|
+
"dotenv": "^16.4.7",
|
|
50
53
|
"elliptic": "^6.5.4",
|
|
51
54
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
|
52
55
|
"eslint-plugin-import": "^2.29.1",
|
package/src/constants.ts
CHANGED
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
MAINNET_SPLITMAIN_BYTECODE,
|
|
12
12
|
} from './bytecodes';
|
|
13
13
|
|
|
14
|
+
import * as dotenv from 'dotenv';
|
|
15
|
+
dotenv.config();
|
|
16
|
+
|
|
14
17
|
export const CONFLICT_ERROR_MSG = 'Conflict';
|
|
15
18
|
|
|
16
19
|
export const EIP712_DOMAIN_NAME = 'Obol';
|
|
@@ -195,3 +198,10 @@ export const DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = 1;
|
|
|
195
198
|
export const DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = 0.1;
|
|
196
199
|
|
|
197
200
|
export const OBOL_SDK_EMAIL = 'sdk@dvlabs.tech';
|
|
201
|
+
|
|
202
|
+
export const PROVIDER_MAP: Record<number, string> = {
|
|
203
|
+
1: `${process.env.RPC_MAINNET}`, // Mainnet
|
|
204
|
+
17000: `${process.env.RPC_HOLESKY}`, // Holesky
|
|
205
|
+
11155111: `${process.env.RPC_SEPOLIA}`, // Sepolia
|
|
206
|
+
100: `${process.env.RPC_GNOSIS}`, // Gnosis
|
|
207
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
import { isContractAvailable } from './utils.js';
|
|
50
50
|
export * from './types.js';
|
|
51
51
|
export * from './services.js';
|
|
52
|
+
export * from './verification/signature-validator.js';
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Obol sdk Client can be used for creating, managing and activating distributed validators.
|