@obolnetwork/obol-sdk 2.4.5 → 2.5.0

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.
Files changed (56) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/package.json +4 -2
  3. package/dist/cjs/src/ajv.js +67 -41
  4. package/dist/cjs/src/constants.js +1 -1
  5. package/dist/cjs/src/incentiveHelpers.js +2 -4
  6. package/dist/cjs/src/incentives.js +1 -1
  7. package/dist/cjs/src/index.js +25 -27
  8. package/dist/cjs/src/schema.js +50 -23
  9. package/dist/cjs/src/services.js +3 -2
  10. package/dist/cjs/src/utils.js +4 -4
  11. package/dist/cjs/src/verification/common.js +12 -9
  12. package/dist/cjs/src/verification/signature-validator.js +6 -5
  13. package/dist/cjs/src/verification/v1.8.0.js +18 -0
  14. package/dist/cjs/test/fixtures.js +125 -17
  15. package/dist/cjs/test/incentives.test.js +2 -2
  16. package/dist/cjs/test/methods.test.js +47 -42
  17. package/dist/esm/package.json +4 -2
  18. package/dist/esm/src/ajv.js +66 -40
  19. package/dist/esm/src/constants.js +1 -1
  20. package/dist/esm/src/incentiveHelpers.js +2 -4
  21. package/dist/esm/src/incentives.js +1 -1
  22. package/dist/esm/src/index.js +26 -28
  23. package/dist/esm/src/schema.js +50 -23
  24. package/dist/esm/src/services.js +3 -2
  25. package/dist/esm/src/utils.js +4 -4
  26. package/dist/esm/src/verification/common.js +12 -9
  27. package/dist/esm/src/verification/signature-validator.js +6 -5
  28. package/dist/esm/src/verification/v1.8.0.js +18 -0
  29. package/dist/esm/test/fixtures.js +124 -16
  30. package/dist/esm/test/incentives.test.js +2 -2
  31. package/dist/esm/test/methods.test.js +48 -43
  32. package/dist/types/src/ajv.d.ts +3 -2
  33. package/dist/types/src/constants.d.ts +1 -1
  34. package/dist/types/src/incentiveHelpers.d.ts +1 -1
  35. package/dist/types/src/incentives.d.ts +2 -2
  36. package/dist/types/src/index.d.ts +1 -0
  37. package/dist/types/src/schema.d.ts +40 -8
  38. package/dist/types/src/services.d.ts +3 -2
  39. package/dist/types/src/types.d.ts +16 -6
  40. package/dist/types/src/utils.d.ts +1 -1
  41. package/dist/types/src/verification/common.d.ts +2 -2
  42. package/dist/types/src/verification/signature-validator.d.ts +5 -2
  43. package/dist/types/test/fixtures.d.ts +78 -11
  44. package/package.json +4 -2
  45. package/src/ajv.ts +91 -52
  46. package/src/constants.ts +1 -1
  47. package/src/incentiveHelpers.ts +1 -5
  48. package/src/incentives.ts +3 -4
  49. package/src/index.ts +37 -35
  50. package/src/schema.ts +45 -22
  51. package/src/services.ts +4 -2
  52. package/src/types.ts +20 -5
  53. package/src/utils.ts +7 -4
  54. package/src/verification/common.ts +13 -1
  55. package/src/verification/signature-validator.ts +10 -3
  56. package/src/verification/v1.8.0.ts +24 -0
@@ -20,9 +20,9 @@ const protocol_kit_1 = __importDefault(require("@safe-global/protocol-kit"));
20
20
  const constants_1 = require("../constants");
21
21
  const utils_1 = require("@safe-global/protocol-kit/dist/src/utils");
22
22
  const utils_2 = require("../utils");
23
- const validateAddressSignature = ({ address, token, data, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
23
+ const validateAddressSignature = ({ address, token, data, chainId, safeRpcUrl, }) => __awaiter(void 0, void 0, void 0, function* () {
24
24
  try {
25
- const provider = (0, utils_2.getProvider)(chainId); // [TODO Hanan], should expect it from signer.provider, or passed in client
25
+ const provider = (0, utils_2.getProvider)(chainId, safeRpcUrl);
26
26
  if (provider) {
27
27
  const contractAddress = yield (0, utils_2.isContractAvailable)(address, provider);
28
28
  if (contractAddress) {
@@ -31,6 +31,7 @@ const validateAddressSignature = ({ address, token, data, chainId, }) => __await
31
31
  data: data,
32
32
  address,
33
33
  chainId,
34
+ safeRpcUrl,
34
35
  });
35
36
  }
36
37
  }
@@ -54,11 +55,11 @@ const validateEOASignature = ({ token, data, address, }) => {
54
55
  }
55
56
  };
56
57
  exports.validateEOASignature = validateEOASignature;
57
- const validateSmartContractSignature = ({ token, data, address, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
58
+ const validateSmartContractSignature = ({ token, data, address, chainId, safeRpcUrl, }) => __awaiter(void 0, void 0, void 0, function* () {
58
59
  try {
59
- const provider = constants_1.PROVIDER_MAP[chainId];
60
+ const safeProvider = safeRpcUrl !== null && safeRpcUrl !== void 0 ? safeRpcUrl : constants_1.PROVIDER_MAP[chainId];
60
61
  const protocolKit = yield protocol_kit_1.default.init({
61
- provider,
62
+ provider: safeProvider,
62
63
  safeAddress: address,
63
64
  });
64
65
  const messageHash = (0, utils_1.hashTypedData)(data);
@@ -147,6 +147,24 @@ const verifyDVV1X8 = (clusterLock) => {
147
147
  for (const element of validatorPublicShares) {
148
148
  pubShares.push((0, ssz_1.fromHexString)(element));
149
149
  }
150
+ // Check deposit amounts match exactly if they are defined
151
+ const depositAmounts = clusterLock.cluster_definition.deposit_amounts;
152
+ if (depositAmounts !== null) {
153
+ const partialDepositAmounts = validator.partial_deposit_data.map(d => d.amount);
154
+ if ((depositAmounts === null || depositAmounts === void 0 ? void 0 : depositAmounts.length) !== partialDepositAmounts.length) {
155
+ return false;
156
+ }
157
+ // Check that both arrays contain exactly the same elements
158
+ const sortedDepositAmounts = [...depositAmounts]
159
+ .map(Number)
160
+ .sort((a, b) => a - b);
161
+ const sortedPartialAmounts = [...partialDepositAmounts]
162
+ .map(Number)
163
+ .sort((a, b) => a - b);
164
+ if (!sortedDepositAmounts.every((amount, index) => amount === sortedPartialAmounts[index])) {
165
+ return false;
166
+ }
167
+ }
150
168
  // Deposit Data Verification
151
169
  for (const element of validator.partial_deposit_data) {
152
170
  const depositData = element;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.clusterLockWithCompoundingWithdrawals = exports.clusterLockV1X10 = exports.clusterLockWithSafe = exports.nullDepositAmountsClusterLockV1X8 = exports.clusterLockV1X8 = exports.clusterConfigV1X8 = exports.clusterLockV1X7 = exports.clusterConfigV1X7 = exports.clusterLockV1X6 = exports.enr = void 0;
3
+ exports.clusterLockWithCompoundingWithdrawalss = exports.clusterLockWithCompoundingWithdrawals = exports.clusterLockV1X10 = exports.soloClusterConfigV1X10 = exports.clusterConfigV1X10 = exports.clusterLockWithSafe = exports.nullDepositAmountsClusterLockV1X8 = exports.clusterLockV1X8 = exports.clusterLockV1X7 = exports.clusterConfigV1X7 = exports.clusterLockV1X6 = exports.enr = void 0;
4
4
  exports.enr = 'enr:-HW4QLlrtMjFLGkFT1bwdGbvZQlH8hLi0M2g44JAxEYP3BZmYpcsy9Q56HPPD87fMucjvLv4-obEFacpsg0ehRilbHeAgmlkgnY0iXNlY3AyNTZrMaEDRaa5o2aSgqyFq_ERZcQTztrOij1mFtXX1bJuVI6ieak';
5
5
  // v1.6.0
6
6
  exports.clusterLockV1X6 = {
@@ -173,22 +173,6 @@ exports.clusterLockV1X7 = {
173
173
  ],
174
174
  };
175
175
  // v1.8.0
176
- exports.clusterConfigV1X8 = {
177
- name: 'testSDK',
178
- operators: [
179
- { address: '0xC35CfCd67b9C27345a54EDEcC1033F2284148c81' },
180
- { address: '0x33807D6F1DCe44b9C599fFE03640762A6F08C496' },
181
- { address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f' },
182
- { address: '0xf6fF1a7A14D01e86a175bA958d3B6C75f2213966' },
183
- ],
184
- validators: [
185
- {
186
- fee_recipient_address: '0x3CD4958e76C317abcEA19faDd076348808424F99',
187
- withdrawal_address: '0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e',
188
- },
189
- ],
190
- deposit_amounts: ['8000000000', '16000000000', '8000000000'],
191
- };
192
176
  exports.clusterLockV1X8 = {
193
177
  cluster_definition: {
194
178
  name: 'xxxx',
@@ -510,6 +494,40 @@ exports.clusterLockWithSafe = {
510
494
  '0x4067921a5257efe4ceb103f2129faaa7a502781157b3b54e5efca559c558c2b43b89f30962e87df88fbf62250049a31888fcd62735d54b7553e5dc75c3b6ae0901',
511
495
  ],
512
496
  };
497
+ exports.clusterConfigV1X10 = {
498
+ name: 'testSDK',
499
+ operators: [
500
+ { address: '0xC35CfCd67b9C27345a54EDEcC1033F2284148c81' },
501
+ { address: '0x33807D6F1DCe44b9C599fFE03640762A6F08C496' },
502
+ { address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f' },
503
+ { address: '0xf6fF1a7A14D01e86a175bA958d3B6C75f2213966' },
504
+ ],
505
+ validators: [
506
+ {
507
+ fee_recipient_address: '0x3CD4958e76C317abcEA19faDd076348808424F99',
508
+ withdrawal_address: '0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e',
509
+ },
510
+ ],
511
+ deposit_amounts: ['8000000000', '32000000000', '8000000000'],
512
+ compounding: true,
513
+ target_gas_limit: 36000000,
514
+ consensus_protocol: '',
515
+ };
516
+ exports.soloClusterConfigV1X10 = {
517
+ name: 'testSDK',
518
+ operators: [
519
+ { address: '' },
520
+ { address: '' },
521
+ { address: '' },
522
+ { address: '' },
523
+ ],
524
+ validators: [
525
+ {
526
+ fee_recipient_address: '0x3CD4958e76C317abcEA19faDd076348808424F99',
527
+ withdrawal_address: '0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e',
528
+ },
529
+ ],
530
+ };
513
531
  exports.clusterLockV1X10 = {
514
532
  cluster_definition: {
515
533
  name: 'test',
@@ -774,3 +792,93 @@ exports.clusterLockWithCompoundingWithdrawals = {
774
792
  '0x3e2548c3c35df90bd7af34f9cc439e7ec4e4fa5619f74ca393bde8e05942dfde0c2e5d95249f478533fe11581b794e5de55446452e4dd1648c33b319a987461701',
775
793
  ],
776
794
  };
795
+ exports.clusterLockWithCompoundingWithdrawalss = {
796
+ cluster_definition: {
797
+ name: 'test v1.10.0',
798
+ creator: {
799
+ address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
800
+ config_signature: '0x62b609c3a56439fe60785895d60a36912b85d50c14a41fce36795c65d6263bb207467a4d4be7dc292bfd1c636332b1c409feaec6ce2ae9488ae1ad622264a7a81c',
801
+ },
802
+ operators: [
803
+ {
804
+ address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
805
+ enr: 'enr:-HW4QLlrtMjFLGkFT1bwdGbvZQlH8hLi0M2g44JAxEYP3BZmYpcsy9Q56HPPD87fMucjvLv4-obEFacpsg0ehRilbHeAgmlkgnY0iXNlY3AyNTZrMaEDRaa5o2aSgqyFq_ERZcQTztrOij1mFtXX1bJuVI6ieak',
806
+ config_signature: '0x58c31787cf1f9680f736713f4ce039c8bf69467e5128edb2888d380451bd757367665c1ba04dc7656eb0b77eac39f776780710e603372f2a41bd40f4d297c7fa1c',
807
+ enr_signature: '0x4df48b8fc16deb2441022a019b72976524f4ce3dd674ee70561fb9eb52985d3b0d9528532d3e5be7a44548be27e9e96c5358b9f6756c7c7a97e2126f9c2c9b071b',
808
+ },
809
+ {
810
+ address: '0xa084D9095BcBdFe8014B68fa52D73DCeb16C5129',
811
+ enr: 'enr:-Iu4QNbiUUUwT18LynBbVPJhNxvzQsaSpUr40mQTWscnZaqKb6vAlvV8j-eDDR3E0wjMQumGRbGm2IAb5_k4bVWJiVGAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPOiodUji0ohgJb5sNK1hgv8g6xO5_znZz3NkkBkyYyKIN0Y3CCDhqDdWRwgg4u',
812
+ config_signature: '0xc76b2f067a65c9cc6110ab99471b03f47545581e79164758114c98825d5440891843790ce92ca24ee7b3568b839cc88c2b3824587f8f260fca15bb852f69a3af1c',
813
+ enr_signature: '0x49e2a07424e412ed904fd47381c36203cb8c5575a31ba260b3f8420dc89a86fc0d7fbc087160a60690be2f6fb212423d467e212494992ab9a72f10319779a2cc1c',
814
+ },
815
+ {
816
+ address: '0xb1D2740Ee055693FE21bbCfc780729472C3f6B84',
817
+ enr: 'enr:-Iu4QJyserRukhG0Vgi2csu7GjpHYUGufNEbZ8Q7ZBrcZUb0KqpL5QzHonkh1xxHlxatTxrIcX_IS5J3SEWR_sa0ptGAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQMAUgEqczOjevyculnUIofhCj0DkgJudErM7qCYIvIkzIN0Y3CCDhqDdWRwgg4u',
818
+ config_signature: '0x8b0cea21e065d25faa3b9d3b9e8f497d3c2d8baf800a1c5ecbde64c041745b7e15131d28f7ab917bdcead3225dda16987f2fd160546e087987cbb483bd187cb11b',
819
+ enr_signature: '0x805f38269379087168900a37569f1108b9d4253a03c2650a4e8d69228cb4cc616b94988e4b218a8d47113063270c4a894e873a5070c7ef0f4d44ee3359eec51b1b',
820
+ },
821
+ {
822
+ address: '0x69D39e2d0Fb6fb80CCd5d49a75416cB7d1BC6DB6',
823
+ enr: 'enr:-HW4QKJTwXC6Chw6zbnA3HFZi6Jo0DkIgjKy4eUBpsSOGnAeWE6ChEjEyk_6R6Qrm7jI-iqfs3_HYxiKde8vFgvHHrCAgmlkgnY0iXNlY3AyNTZrMaECfFKQH4spdZCHqrKVz1Q02xYla6J_RQECDNNYBRWdzv8',
824
+ config_signature: '0x9e21c2308738fb17b38d1850b83312c25306c0bcbd575e0417e311d5fc1697807291328a299859df70ac3efb402207067dbb0559bc4fef4e6ca2fd77fa4c2a321c',
825
+ enr_signature: '0x3a6590600b2dc27b042aabd3fa4654f1064b42e8f850d1fac13394a6786463be2a2727623e4e9e98453113f7024e73d652d1c6288ef82ede7acbb08e25ff00751b',
826
+ },
827
+ ],
828
+ uuid: '34036642-9d45-4b6f-817d-f8d5aa0110d4',
829
+ version: 'v1.10.0',
830
+ timestamp: '2025-04-03T17:27:27.767Z',
831
+ num_validators: 1,
832
+ threshold: 3,
833
+ validators: [
834
+ {
835
+ fee_recipient_address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
836
+ withdrawal_address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
837
+ },
838
+ ],
839
+ dkg_algorithm: 'default',
840
+ fork_version: '0x10000910',
841
+ deposit_amounts: ['32000000000'],
842
+ consensus_protocol: 'abft',
843
+ target_gas_limit: 30000000,
844
+ compounding: true,
845
+ config_hash: '0xe6d2e8fe49d722f55ced8a028c62ffbde16a05bc74d7d9703b675ecc8c820b4b',
846
+ definition_hash: '0xb5d81494f98df23cdff33909888080054f1068c11bbee5ec39aeb95edfa3b37c',
847
+ },
848
+ distributed_validators: [
849
+ {
850
+ distributed_public_key: '0x8368525a657df38fd3d774a08f1a54f008ae3340e8441a2bbb2d2051a9a8f46d4eeb8610bbc088e7402cba6b138c7b70',
851
+ public_shares: [
852
+ '0xae455c727456238fadd7b84ded0f0fb700d0e2fa028fff34e3fc73d0279ee9a5aa9843f8af00c9518ea781d8d823cc0d',
853
+ '0x91a7cfa9682464dff7324c0907fdff41df3f2643fd97d30559d82fbbfa354dae1e3f9176d8562b5ab47d3460240004cb',
854
+ '0xac8c0ffdb9ea7ce63bbed67122123c86ffa669a5027da5f96ab85b68c3a491253ebf0308cf4e237ea4eaf8543064c8a8',
855
+ '0x85385ef7cf1b1d9cd648ceb45e92516ef9612c9830b0abf808ea7996ad9369764bb9ac27c3cd730497e12e394ab48d16',
856
+ ],
857
+ builder_registration: {
858
+ message: {
859
+ fee_recipient: '0xc6e76f72ea672fae05c357157cfc37720f0af26f',
860
+ gas_limit: 30000000,
861
+ timestamp: 1742213400,
862
+ pubkey: '0x8368525a657df38fd3d774a08f1a54f008ae3340e8441a2bbb2d2051a9a8f46d4eeb8610bbc088e7402cba6b138c7b70',
863
+ },
864
+ signature: '0xb73c4d55c17bdbbb53790fee3294ef2c69b2a6e4c700eb30603e437308005edacbe76591f917aa82cfcf60da2d669ccd101ae9b3dc4b1ae61ab7ad1794620adbdc0b4ea5f91fc5e790833537c610a57ccdc4c95ca53cb6dedec2be2ad8acf266',
865
+ },
866
+ partial_deposit_data: [
867
+ {
868
+ pubkey: '0x8368525a657df38fd3d774a08f1a54f008ae3340e8441a2bbb2d2051a9a8f46d4eeb8610bbc088e7402cba6b138c7b70',
869
+ withdrawal_credentials: '0x020000000000000000000000c6e76f72ea672fae05c357157cfc37720f0af26f',
870
+ amount: '32000000000',
871
+ signature: '0xa7c5c2417f5afd089b87df0eff10855b748d621749879b6087c8a1dbfcbb95aa79a2c8f4d637ecb283426b9c5af07c30080c19a245ea614ff3f609a92994e9a29f3c0f8c4bf1a4e7ef58615ff80586b40e36859c9fd935b68ae55670e29ea5d7',
872
+ },
873
+ ],
874
+ },
875
+ ],
876
+ signature_aggregate: '0x8b9f884ab3ab89d63f49437cae09e2b77757787f2c54bb52bc51df1d565bc6d63ef9fc4e0ea76bd72a58618651b827a611d8e96de238fad4616420b4cd393ad62428ac9db2e01a4e8dd8ae835845e7530f9e1b3b4ebf5ba2da3c77aec70d06b2',
877
+ lock_hash: '0xaf6b0d989fdd82f7f9683d62b9f797af129f9afae2a4b453a0bef1814b21ae5d',
878
+ node_signatures: [
879
+ '0xc059b3920fc107a909feb16217c7372911c5cb386954ed899b5d8e782d6d71e17a5dde1c2d4825b7c2b5edd63b4eb9c306afd4afd5dbce3f8db9c13d06d6cebf01',
880
+ '0x051a72e5f2a66a85aabd635a210350f82924350170981575f3cb67e7699791d7118ab7c284f10b52f3604a89aa0bc43ad2d2537a7b19b109610324926475d30900',
881
+ '0xa28a23de3703c3eeb9a091d4f47302a96079759baf1afca428cbd298b46c40fe103df0006dfd5a0ffd15ee7c5fa436d70d7a03d4537dd8292a6152734e5f689601',
882
+ '0x992ab5c73d30d10cc5c8f6d462ba387fd5a698e206b0dfdd0224d17117f85a2077fcaa62fd6d5170ff678249288fcd82877e2987ed5564df5dbd9d51370871c800',
883
+ ],
884
+ };
@@ -145,7 +145,7 @@ global.fetch = globals_1.jest.fn();
145
145
  .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
146
146
  const result = yield clientInstance.incentives.isClaimed(mockIncentivesData.contract_address, mockIncentivesData.index);
147
147
  (0, globals_1.expect)(result).toBe(true);
148
- (0, globals_1.expect)(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(clientInstance.incentives.chainId, mockIncentivesData.contract_address, mockIncentivesData.index, {});
148
+ (0, globals_1.expect)(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(mockIncentivesData.contract_address, mockIncentivesData.index, {});
149
149
  }));
150
150
  (0, globals_1.test)('isClaimed should return false when incentive is not claimed', () => __awaiter(void 0, void 0, void 0, function* () {
151
151
  globals_1.jest
@@ -172,7 +172,7 @@ global.fetch = globals_1.jest.fn();
172
172
  .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
173
173
  const result = yield clientWithoutSigner.incentives.isClaimed(mockIncentivesData.contract_address, mockIncentivesData.index);
174
174
  (0, globals_1.expect)(result).toBe(true);
175
- (0, globals_1.expect)(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(clientWithoutSigner.incentives.chainId, mockIncentivesData.contract_address, mockIncentivesData.index, provider);
175
+ (0, globals_1.expect)(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(mockIncentivesData.contract_address, mockIncentivesData.index, provider);
176
176
  }));
177
177
  (0, globals_1.test)('getIncentivesByAddress should make the correct API request', () => __awaiter(void 0, void 0, void 0, function* () {
178
178
  const mockAddress = '0x1234567890abcdef1234567890abcdef12345678';
@@ -38,7 +38,6 @@ const index_1 = require("../src/index");
38
38
  const fixtures_js_1 = require("./fixtures.js");
39
39
  const constants_1 = require("../src/constants");
40
40
  const base_1 = require("../src/base");
41
- const ajv_1 = require("../src/ajv");
42
41
  const msw_1 = require("msw");
43
42
  const node_1 = require("msw/node");
44
43
  const termsAndConditions_1 = require("../src/verification/termsAndConditions");
@@ -65,31 +64,31 @@ describe('Cluster Client', () => {
65
64
  clientInstance['request'] = jest
66
65
  .fn()
67
66
  .mockReturnValue(Promise.resolve({ config_hash: mockConfigHash }));
68
- const configHash = yield clientInstance.createClusterDefinition(fixtures_js_1.clusterConfigV1X8);
67
+ const configHash = yield clientInstance.createClusterDefinition(fixtures_js_1.clusterConfigV1X10);
69
68
  expect(configHash).toEqual(mockConfigHash);
70
69
  }));
71
70
  test('acceptClusterDefinition should return cluster definition', () => __awaiter(void 0, void 0, void 0, function* () {
72
71
  clientInstance['request'] = jest
73
72
  .fn()
74
- .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X8.cluster_definition));
73
+ .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X10.cluster_definition));
75
74
  const clusterDefinition = yield clientInstance.acceptClusterDefinition({
76
- enr: fixtures_js_1.clusterLockV1X8.cluster_definition.operators[0].enr,
77
- version: fixtures_js_1.clusterLockV1X8.cluster_definition.version,
78
- }, fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
79
- expect(clusterDefinition).toEqual(fixtures_js_1.clusterLockV1X8.cluster_definition);
75
+ enr: fixtures_js_1.clusterLockV1X10.cluster_definition.operators[0].enr,
76
+ version: fixtures_js_1.clusterLockV1X10.cluster_definition.version,
77
+ }, fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
78
+ expect(clusterDefinition).toEqual(fixtures_js_1.clusterLockV1X10.cluster_definition);
80
79
  }));
81
80
  test('createClusterDefinition should throw an error on invalid operators', () => __awaiter(void 0, void 0, void 0, function* () {
82
81
  clientInstance['request'] = jest
83
82
  .fn()
84
83
  .mockReturnValue(Promise.resolve({ config_hash: mockConfigHash }));
85
84
  try {
86
- yield clientInstance.createClusterDefinition(Object.assign(Object.assign({}, fixtures_js_1.clusterConfigV1X8), { operators: [] }));
85
+ yield clientInstance.createClusterDefinition(Object.assign(Object.assign({}, fixtures_js_1.clusterConfigV1X10), { operators: [] }));
87
86
  }
88
87
  catch (error) {
89
- expect(error.message).toEqual("Schema compilation errors', must NOT have fewer than 4 items");
88
+ expect(error.message).toEqual('Validation failed: /operators must pass "validateUniqueAddresses" keyword validation, /operators must NOT have fewer than 4 items');
90
89
  }
91
90
  }));
92
- // cause we default to 32000000000
91
+ // cause we default to null
93
92
  test('createClusterDefinition should accept a configuration without deposit_amounts', () => __awaiter(void 0, void 0, void 0, function* () {
94
93
  clientInstance['request'] = jest
95
94
  .fn()
@@ -105,32 +104,27 @@ describe('Cluster Client', () => {
105
104
  yield clientInstance.createClusterDefinition(Object.assign(Object.assign({}, fixtures_js_1.clusterConfigV1X7), { deposit_amounts: ['34000000'] }));
106
105
  }
107
106
  catch (error) {
108
- expect(error.message).toEqual('Schema compilation errors\', must pass "validDepositAmounts" keyword validation');
109
- }
110
- }));
111
- test('validatePayload should throw an error on empty schema', () => __awaiter(void 0, void 0, void 0, function* () {
112
- try {
113
- (0, ajv_1.validatePayload)(Object.assign(Object.assign({}, fixtures_js_1.clusterConfigV1X8), { operators: [] }), '');
114
- }
115
- catch (error) {
116
- expect(error.message).toEqual('schema must be object or boolean');
107
+ expect(error.message).toEqual('Validation failed: /deposit_amounts/0 must be equal to one of the allowed values, /deposit_amounts must match "then" schema');
117
108
  }
118
109
  }));
119
110
  test('getClusterdefinition should return cluster definition if config hash exist', () => __awaiter(void 0, void 0, void 0, function* () {
120
- var _a;
121
111
  clientInstance['request'] = jest
122
112
  .fn()
123
- .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X8.cluster_definition));
124
- const clusterDefinition = yield clientInstance.getClusterDefinition(fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
125
- expect((_a = clusterDefinition.deposit_amounts) === null || _a === void 0 ? void 0 : _a.length).toEqual(fixtures_js_1.clusterLockV1X8.cluster_definition.deposit_amounts.length);
126
- expect(clusterDefinition.config_hash).toEqual(fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
113
+ .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X10.cluster_definition));
114
+ const clusterDefinition = yield clientInstance.getClusterDefinition(fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
115
+ expect(clusterDefinition.deposit_amounts).toBeDefined();
116
+ expect(clusterDefinition.config_hash).toEqual(fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
117
+ // Test for new fields
118
+ expect(clusterDefinition.compounding).toBeDefined();
119
+ expect(clusterDefinition.target_gas_limit).toBeDefined();
120
+ expect(clusterDefinition.consensus_protocol).toBeDefined();
127
121
  }));
128
122
  test('getClusterLock should return lockFile if exist', () => __awaiter(void 0, void 0, void 0, function* () {
129
123
  clientInstance['request'] = jest
130
124
  .fn()
131
- .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X8));
132
- const clusterLock = yield clientInstance.getClusterLock(fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
133
- expect(clusterLock.lock_hash).toEqual(fixtures_js_1.clusterLockV1X8.lock_hash);
125
+ .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X10));
126
+ const clusterLock = yield clientInstance.getClusterLock(fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
127
+ expect(clusterLock.lock_hash).toEqual(fixtures_js_1.clusterLockV1X10.lock_hash);
134
128
  }));
135
129
  test('request method should set user agent header', () => __awaiter(void 0, void 0, void 0, function* () {
136
130
  const server = (0, node_1.setupServer)(msw_1.http.get('http://testexample.com/test', ({ request }) => {
@@ -165,9 +159,12 @@ describe('Cluster Client without a signer', () => {
165
159
  beforeAll(() => {
166
160
  jest.restoreAllMocks();
167
161
  });
162
+ beforeEach(() => {
163
+ jest.resetModules();
164
+ });
168
165
  test('createClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
169
166
  try {
170
- yield clientInstance.createClusterDefinition(fixtures_js_1.clusterConfigV1X8);
167
+ yield clientInstance.createClusterDefinition(fixtures_js_1.clusterConfigV1X10);
171
168
  }
172
169
  catch (err) {
173
170
  expect(err.message).toEqual('Signer is required in createClusterDefinition');
@@ -176,9 +173,9 @@ describe('Cluster Client without a signer', () => {
176
173
  test('acceptClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
177
174
  try {
178
175
  yield clientInstance.acceptClusterDefinition({
179
- enr: fixtures_js_1.clusterLockV1X8.cluster_definition.operators[0].enr,
180
- version: fixtures_js_1.clusterLockV1X8.cluster_definition.version,
181
- }, fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
176
+ enr: fixtures_js_1.clusterLockV1X10.cluster_definition.operators[0].enr,
177
+ version: fixtures_js_1.clusterLockV1X10.cluster_definition.version,
178
+ }, fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
182
179
  }
183
180
  catch (err) {
184
181
  expect(err.message).toEqual('Signer is required in acceptClusterDefinition');
@@ -187,16 +184,16 @@ describe('Cluster Client without a signer', () => {
187
184
  test('getClusterdefinition should return cluster definition if config hash exist', () => __awaiter(void 0, void 0, void 0, function* () {
188
185
  clientInstance['request'] = jest
189
186
  .fn()
190
- .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X8.cluster_definition));
191
- const clusterDefinition = yield clientInstance.getClusterDefinition(fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
192
- expect(clusterDefinition.config_hash).toEqual(fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
187
+ .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X10.cluster_definition));
188
+ const clusterDefinition = yield clientInstance.getClusterDefinition(fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
189
+ expect(clusterDefinition.config_hash).toEqual(fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
193
190
  }));
194
191
  test('getClusterLock should return lockFile if exist', () => __awaiter(void 0, void 0, void 0, function* () {
195
192
  clientInstance['request'] = jest
196
193
  .fn()
197
- .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X8));
198
- const clusterLock = yield clientInstance.getClusterLock(fixtures_js_1.clusterLockV1X8.cluster_definition.config_hash);
199
- expect(clusterLock.lock_hash).toEqual(fixtures_js_1.clusterLockV1X8.lock_hash);
194
+ .mockReturnValue(Promise.resolve(fixtures_js_1.clusterLockV1X10));
195
+ const clusterLock = yield clientInstance.getClusterLock(fixtures_js_1.clusterLockV1X10.cluster_definition.config_hash);
196
+ expect(clusterLock.lock_hash).toEqual(fixtures_js_1.clusterLockV1X10.lock_hash);
200
197
  }));
201
198
  test.each([
202
199
  { version: 'v1.6.0', clusterLock: fixtures_js_1.clusterLockV1X6 },
@@ -219,6 +216,14 @@ describe('Cluster Client without a signer', () => {
219
216
  const isValidLock = yield (0, index_1.validateClusterLock)(clusterLock);
220
217
  expect(isValidLock).toEqual(true);
221
218
  }));
219
+ test('should return true on verified cluster lock with Safe wallet and safe rpc url', () => __awaiter(void 0, void 0, void 0, function* () {
220
+ process.env.RPC_HOLESKY = undefined;
221
+ /* eslint-disable @typescript-eslint/no-var-requires */
222
+ const { validateClusterLock: validateLockWithRpcUrl, } = require('../src/index');
223
+ const safeRpcUrl = 'https://ethereum-holesky-rpc.publicnode.com';
224
+ const isValidLock = yield validateLockWithRpcUrl(fixtures_js_1.clusterLockWithSafe, safeRpcUrl);
225
+ expect(isValidLock).toEqual(true);
226
+ }));
222
227
  test('validateCluster should return false for cluster with null deposit_amounts and incorrect partial_deposits', () => __awaiter(void 0, void 0, void 0, function* () {
223
228
  const partialDeposit = fixtures_js_1.nullDepositAmountsClusterLockV1X8.distributed_validators[0]
224
229
  .partial_deposit_data[0];
@@ -295,7 +300,7 @@ describe('createObolRewardsSplit', () => {
295
300
  });
296
301
  }
297
302
  catch (error) {
298
- expect(error.message).toEqual('Schema compilation errors\', must pass "validateSplitRecipients" keyword validation');
303
+ expect(error.message).toEqual('Validation failed: must pass "validateRewardsSplitRecipients" keyword validation');
299
304
  }
300
305
  }));
301
306
  test('should throw an error if ObolRAFSplit is less than 1', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -308,7 +313,7 @@ describe('createObolRewardsSplit', () => {
308
313
  });
309
314
  }
310
315
  catch (error) {
311
- expect(error.message).toEqual("Schema compilation errors', must be >= 1");
316
+ expect(error.message).toEqual('Validation failed: must pass "validateRewardsSplitRecipients" keyword validation, /ObolRAFSplit must be >= 1');
312
317
  }
313
318
  }));
314
319
  it('should return the correct withdrawal and fee recipient addresses', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -375,7 +380,7 @@ describe('createObolTotalSplit', () => {
375
380
  });
376
381
  }
377
382
  catch (error) {
378
- expect(error.message).toEqual('Schema compilation errors\', must pass "validateSplitRecipients" keyword validation');
383
+ expect(error.message).toEqual('Validation failed: must pass "validateTotalSplitRecipients" keyword validation');
379
384
  }
380
385
  }));
381
386
  test('should throw an error if ObolRAFSplit is less than 0.1', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -386,13 +391,13 @@ describe('createObolTotalSplit', () => {
386
391
  });
387
392
  }
388
393
  catch (error) {
389
- expect(error.message).toEqual("Schema compilation errors', must be >= 0.1");
394
+ expect(error.message).toEqual('Validation failed: must pass "validateTotalSplitRecipients" keyword validation, /ObolRAFSplit must be >= 0.1');
390
395
  }
391
396
  }));
392
397
  it('should return the correct withdrawal and fee recipient addresses and ObolRAFSplit', () => __awaiter(void 0, void 0, void 0, function* () {
393
398
  const result = yield clientInstance.createObolTotalSplit({
394
399
  splitRecipients: mockSplitRecipients,
395
- ObolRAFSplit: 0.5,
400
+ ObolRAFSplit: 0.1,
396
401
  });
397
402
  // 0xPredictedAddress and not 0xSplitterAddress since were mocking isContractAvailable response to be true
398
403
  expect(result).toEqual({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obolnetwork/obol-sdk",
3
- "version": "2.4.5",
3
+ "version": "2.5.0",
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"
@@ -49,6 +49,8 @@
49
49
  "@safe-global/safe-core-sdk-types": "5.1.0",
50
50
  "@types/pdf-parse": "^1.1.4",
51
51
  "ajv": "^8.12.0",
52
+ "ajv-formats": "^3.0.1",
53
+ "ajv-keywords": "^5.1.0",
52
54
  "cross-fetch": "^3.1.5",
53
55
  "dotenv": "^16.4.7",
54
56
  "elliptic": "^6.5.4",
@@ -56,7 +58,7 @@
56
58
  "eslint-plugin-import": "^2.29.1",
57
59
  "eslint-plugin-n": "^16.6.2",
58
60
  "eslint-plugin-promise": "^6.1.1",
59
- "ethers": "6.4.0",
61
+ "ethers": "^6.13.5",
60
62
  "nock": "^13.5.3",
61
63
  "pdf-parse": "^1.1.1",
62
64
  "semver": "^7.6.0",
@@ -1,53 +1,79 @@
1
- import Ajv from 'ajv';
1
+ import addFormats from 'ajv-formats';
2
+ import addKeywords from 'ajv-keywords';
2
3
  import { parseUnits } from 'ethers';
4
+ import Ajv from 'ajv';
3
5
  import { DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT, } from './constants';
4
- const validDepositAmounts = (data, deposits) => {
5
- let sum = 0;
6
- // from ether togwei is same as from gwei to wei
7
- const maxDeposit = Number(parseUnits('32', 'gwei'));
8
- const minDeposit = Number(parseUnits('1', 'gwei'));
9
- for (const element of deposits) {
10
- const amountInGWei = Number(element);
11
- if (!Number.isInteger(amountInGWei) ||
12
- amountInGWei > maxDeposit ||
13
- amountInGWei < minDeposit) {
14
- return false;
15
- }
16
- sum += amountInGWei;
6
+ export const VALID_DEPOSIT_AMOUNTS = [
7
+ parseUnits('1', 'gwei').toString(),
8
+ parseUnits('32', 'gwei').toString(),
9
+ parseUnits('8', 'gwei').toString(),
10
+ parseUnits('256', 'gwei').toString(),
11
+ ];
12
+ export const VALID_NON_COMPOUNDING_AMOUNTS = [
13
+ parseUnits('1', 'gwei').toString(),
14
+ parseUnits('32', 'gwei').toString(),
15
+ ];
16
+ // They dont see defaults set in schema
17
+ const validateRewardsSplitRecipients = (_, data) => {
18
+ var _a;
19
+ const obolRAFSplit = (_a = data.ObolRAFSplit) !== null && _a !== void 0 ? _a : DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT;
20
+ const splitPercentage = data.splitRecipients.reduce((acc, curr) => acc + curr.percentAllocation, 0);
21
+ return splitPercentage + obolRAFSplit === 100;
22
+ };
23
+ const validateTotalSplitRecipients = (_, data) => {
24
+ var _a;
25
+ const obolRAFSplit = (_a = data.ObolRAFSplit) !== null && _a !== void 0 ? _a : DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT;
26
+ const splitPercentage = data.splitRecipients.reduce((acc, curr) => acc + curr.percentAllocation, 0);
27
+ return splitPercentage + obolRAFSplit === 100;
28
+ };
29
+ const validateUniqueAddresses = (_, operators) => {
30
+ if (!operators) {
31
+ return false;
17
32
  }
18
- if (sum / minDeposit !== 32) {
33
+ if (operators.length < 4) {
19
34
  return false;
20
35
  }
21
- else {
36
+ if (operators.every(op => op.address === '')) {
22
37
  return true;
23
38
  }
39
+ if (operators.some(op => op.address.length !== 42)) {
40
+ return false;
41
+ }
42
+ const addresses = operators.map(op => op.address);
43
+ const uniqueAddresses = new Set(addresses);
44
+ const isUnique = uniqueAddresses.size === addresses.length;
45
+ return isUnique;
24
46
  };
25
- const validateSplitRecipients = (_, data) => {
26
- const splitPercentage = data.splitRecipients.reduce((acc, curr) => acc + curr.percentAllocation, 0);
27
- const ObolRAFSplitParam = data.ObolRAFSplit
28
- ? data.ObolRAFSplit
29
- : 'principalRecipient' in data
30
- ? DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT
31
- : DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT;
32
- return splitPercentage + ObolRAFSplitParam === 100;
33
- };
47
+ const ajv = new Ajv({
48
+ allErrors: true,
49
+ useDefaults: true,
50
+ strict: false,
51
+ $data: true,
52
+ });
53
+ addFormats(ajv);
54
+ addKeywords(ajv, ['patternRequired']);
55
+ ajv.addKeyword({
56
+ keyword: 'validateRewardsSplitRecipients',
57
+ validate: validateRewardsSplitRecipients,
58
+ schemaType: 'boolean',
59
+ });
60
+ ajv.addKeyword({
61
+ keyword: 'validateTotalSplitRecipients',
62
+ validate: validateTotalSplitRecipients,
63
+ schemaType: 'boolean',
64
+ });
65
+ ajv.addKeyword({
66
+ keyword: 'validateUniqueAddresses',
67
+ validate: validateUniqueAddresses,
68
+ schemaType: 'boolean',
69
+ });
34
70
  export function validatePayload(data, schema) {
35
71
  var _a;
36
- const ajv = new Ajv();
37
- ajv.addKeyword({
38
- keyword: 'validDepositAmounts',
39
- validate: validDepositAmounts,
40
- errors: true,
41
- });
42
- ajv.addKeyword({
43
- keyword: 'validateSplitRecipients',
44
- validate: validateSplitRecipients,
45
- errors: true,
46
- });
47
72
  const validate = ajv.compile(schema);
48
- const isValid = validate(data);
49
- if (!isValid) {
50
- throw new Error(`Schema compilation errors', ${(_a = validate.errors) === null || _a === void 0 ? void 0 : _a[0].message}`);
73
+ const valid = validate(data);
74
+ if (!valid) {
75
+ const errors = (_a = validate.errors) === null || _a === void 0 ? void 0 : _a.map(e => `${e.instancePath} ${e.message}`).join(', ');
76
+ throw new Error(`Validation failed: ${errors}`);
51
77
  }
52
- return isValid;
78
+ return data;
53
79
  }
@@ -77,7 +77,7 @@ export const signEnrPayload = (payload, chainId) => {
77
77
  };
78
78
  };
79
79
  export const DKG_ALGORITHM = 'default';
80
- export const CONFIG_VERSION = 'v1.8.0';
80
+ export const CONFIG_VERSION = 'v1.10.0';
81
81
  export const SDK_VERSION = pjson.version;
82
82
  export const DOMAIN_APPLICATION_BUILDER = '00000001';
83
83
  export const DOMAIN_DEPOSIT = '03000000';
@@ -9,7 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { Contract } from 'ethers';
11
11
  import { MerkleDistributorABI } from './abi/MerkleDistributorWithDeadline';
12
- import { getProvider } from './utils';
13
12
  export const claimIncentivesFromMerkleDistributor = (incentivesData) => __awaiter(void 0, void 0, void 0, function* () {
14
13
  try {
15
14
  const contract = new Contract(incentivesData.contractAddress, MerkleDistributorABI.abi, incentivesData.signer);
@@ -22,10 +21,9 @@ export const claimIncentivesFromMerkleDistributor = (incentivesData) => __awaite
22
21
  throw new Error(`Failed to claim incentives: ${error.message}`);
23
22
  }
24
23
  });
25
- export const isClaimedFromMerkleDistributor = (chainId, contractAddress, index, provider) => __awaiter(void 0, void 0, void 0, function* () {
24
+ export const isClaimedFromMerkleDistributor = (contractAddress, index, provider) => __awaiter(void 0, void 0, void 0, function* () {
26
25
  try {
27
- const clientProvider = provider !== null && provider !== void 0 ? provider : getProvider(chainId);
28
- const contract = new Contract(contractAddress, MerkleDistributorABI.abi, clientProvider);
26
+ const contract = new Contract(contractAddress, MerkleDistributorABI.abi, provider);
29
27
  const claimed = yield contract.isClaimed(BigInt(index));
30
28
  return claimed;
31
29
  }
@@ -92,7 +92,7 @@ export class Incentives {
92
92
  */
93
93
  isClaimed(contractAddress, index) {
94
94
  return __awaiter(this, void 0, void 0, function* () {
95
- return yield isClaimedFromMerkleDistributor(this.chainId, contractAddress, index, this.provider);
95
+ return yield isClaimedFromMerkleDistributor(contractAddress, index, this.provider);
96
96
  });
97
97
  }
98
98
  /**