@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
@@ -7,10 +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 { ZeroAddress } from 'ethers';
11
10
  import { v4 as uuidv4 } from 'uuid';
12
11
  import { Base } from './base.js';
13
- import { CONFLICT_ERROR_MSG, CreatorConfigHashSigningTypes, Domain, DKG_ALGORITHM, CONFIG_VERSION, OperatorConfigHashSigningTypes, EnrSigningTypes, TERMS_AND_CONDITIONS_VERSION, TermsAndConditionsSigningTypes, DEFAULT_BASE_VERSION, TERMS_AND_CONDITIONS_HASH, AVAILABLE_SPLITTER_CHAINS, CHAIN_CONFIGURATION, DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT, OBOL_SDK_EMAIL, } from './constants.js';
12
+ import { CONFLICT_ERROR_MSG, CreatorConfigHashSigningTypes, Domain, DKG_ALGORITHM, CONFIG_VERSION, OperatorConfigHashSigningTypes, EnrSigningTypes, TERMS_AND_CONDITIONS_VERSION, TermsAndConditionsSigningTypes, DEFAULT_BASE_VERSION, TERMS_AND_CONDITIONS_HASH, AVAILABLE_SPLITTER_CHAINS, CHAIN_CONFIGURATION, DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, OBOL_SDK_EMAIL, } from './constants.js';
14
13
  import { ConflictError } from './errors.js';
15
14
  import { clusterConfigOrDefinitionHash } from './verification/common.js';
16
15
  import { validatePayload } from './ajv.js';
@@ -22,6 +21,7 @@ export * from './types.js';
22
21
  export * from './services.js';
23
22
  export * from './verification/signature-validator.js';
24
23
  export * from './verification/common.js';
24
+ export * from './constants.js';
25
25
  export { Incentives } from './incentives.js';
26
26
  /**
27
27
  * Obol sdk Client can be used for creating, managing and activating distributed validators.
@@ -102,13 +102,13 @@ export class Client extends Base {
102
102
  * [createObolRewardsSplit](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L141)
103
103
  */
104
104
  // add the example reference
105
- createObolRewardsSplit({ splitRecipients, principalRecipient, etherAmount, ObolRAFSplit = DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, distributorFee = 0, controllerAddress = ZeroAddress, recoveryAddress = ZeroAddress, }) {
105
+ createObolRewardsSplit({ splitRecipients, principalRecipient, etherAmount, ObolRAFSplit = DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, distributorFee, controllerAddress, recoveryAddress, }) {
106
106
  return __awaiter(this, void 0, void 0, function* () {
107
107
  // This method doesnt require T&C signature
108
108
  if (!this.signer) {
109
109
  throw new Error('Signer is required in createObolRewardsSplit');
110
110
  }
111
- validatePayload({
111
+ const validatedPayload = validatePayload({
112
112
  splitRecipients,
113
113
  principalRecipient,
114
114
  etherAmount,
@@ -131,9 +131,9 @@ export class Client extends Base {
131
131
  }
132
132
  const retroActiveFundingRecipient = {
133
133
  account: CHAIN_CONFIGURATION[this.chainId].RETROACTIVE_FUNDING_ADDRESS.address,
134
- percentAllocation: ObolRAFSplit,
134
+ percentAllocation: validatedPayload.ObolRAFSplit,
135
135
  };
136
- const copiedSplitRecipients = [...splitRecipients];
136
+ const copiedSplitRecipients = [...validatedPayload.splitRecipients];
137
137
  copiedSplitRecipients.push(retroActiveFundingRecipient);
138
138
  const { accounts, percentAllocations } = formatSplitRecipients(copiedSplitRecipients);
139
139
  const predictedSplitterAddress = yield predictSplitterAddress({
@@ -141,8 +141,8 @@ export class Client extends Base {
141
141
  accounts,
142
142
  percentAllocations,
143
143
  chainId: this.chainId,
144
- distributorFee,
145
- controllerAddress,
144
+ distributorFee: validatedPayload.distributorFee,
145
+ controllerAddress: validatedPayload.controllerAddress,
146
146
  });
147
147
  const isSplitterDeployed = yield isContractAvailable(predictedSplitterAddress, this.signer.provider);
148
148
  const { withdrawal_address, fee_recipient_address } = yield handleDeployOWRAndSplitter({
@@ -151,12 +151,12 @@ export class Client extends Base {
151
151
  predictedSplitterAddress,
152
152
  accounts,
153
153
  percentAllocations,
154
- principalRecipient,
155
- etherAmount,
154
+ principalRecipient: validatedPayload.principalRecipient,
155
+ etherAmount: validatedPayload.etherAmount,
156
156
  chainId: this.chainId,
157
- distributorFee,
158
- controllerAddress,
159
- recoveryAddress,
157
+ distributorFee: validatedPayload.distributorFee,
158
+ controllerAddress: validatedPayload.controllerAddress,
159
+ recoveryAddress: validatedPayload.recoveryAddress,
160
160
  });
161
161
  return { withdrawal_address, fee_recipient_address };
162
162
  });
@@ -175,13 +175,13 @@ export class Client extends Base {
175
175
  * [createObolTotalSplit](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L168)
176
176
  */
177
177
  // add the example reference
178
- createObolTotalSplit({ splitRecipients, ObolRAFSplit = DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT, distributorFee = 0, controllerAddress = ZeroAddress, }) {
178
+ createObolTotalSplit({ splitRecipients, ObolRAFSplit, distributorFee, controllerAddress, }) {
179
179
  return __awaiter(this, void 0, void 0, function* () {
180
180
  // This method doesnt require T&C signature
181
181
  if (!this.signer) {
182
182
  throw new Error('Signer is required in createObolTotalSplit');
183
183
  }
184
- validatePayload({
184
+ const validatedPayload = validatePayload({
185
185
  splitRecipients,
186
186
  ObolRAFSplit,
187
187
  distributorFee,
@@ -197,9 +197,9 @@ export class Client extends Base {
197
197
  }
198
198
  const retroActiveFundingRecipient = {
199
199
  account: CHAIN_CONFIGURATION[this.chainId].RETROACTIVE_FUNDING_ADDRESS.address,
200
- percentAllocation: ObolRAFSplit,
200
+ percentAllocation: validatedPayload.ObolRAFSplit,
201
201
  };
202
- const copiedSplitRecipients = [...splitRecipients];
202
+ const copiedSplitRecipients = [...validatedPayload.splitRecipients];
203
203
  copiedSplitRecipients.push(retroActiveFundingRecipient);
204
204
  const { accounts, percentAllocations } = formatSplitRecipients(copiedSplitRecipients);
205
205
  const predictedSplitterAddress = yield predictSplitterAddress({
@@ -207,8 +207,8 @@ export class Client extends Base {
207
207
  accounts,
208
208
  percentAllocations,
209
209
  chainId: this.chainId,
210
- distributorFee,
211
- controllerAddress,
210
+ distributorFee: validatedPayload.distributorFee,
211
+ controllerAddress: validatedPayload.controllerAddress,
212
212
  });
213
213
  const isSplitterDeployed = yield isContractAvailable(predictedSplitterAddress, this.signer.provider);
214
214
  if (!isSplitterDeployed) {
@@ -217,8 +217,8 @@ export class Client extends Base {
217
217
  accounts,
218
218
  percentAllocations,
219
219
  chainId: this.chainId,
220
- distributorFee,
221
- controllerAddress,
220
+ distributorFee: validatedPayload.distributorFee,
221
+ controllerAddress: validatedPayload.controllerAddress,
222
222
  });
223
223
  return {
224
224
  withdrawal_address: splitterAddress,
@@ -265,10 +265,8 @@ export class Client extends Base {
265
265
  if (!this.signer) {
266
266
  throw new Error('Signer is required in createClusterDefinition');
267
267
  }
268
- validatePayload(newCluster, definitionSchema);
269
- const clusterConfig = Object.assign(Object.assign({}, newCluster), { fork_version: this.fork_version, dkg_algorithm: DKG_ALGORITHM, version: CONFIG_VERSION, uuid: uuidv4(), timestamp: new Date().toISOString(), threshold: Math.ceil((2 * newCluster.operators.length) / 3), num_validators: newCluster.validators.length, deposit_amounts: newCluster.deposit_amounts
270
- ? newCluster.deposit_amounts
271
- : ['32000000000'] });
268
+ const validatedCluster = validatePayload(newCluster, definitionSchema);
269
+ const clusterConfig = Object.assign(Object.assign({}, validatedCluster), { fork_version: this.fork_version, dkg_algorithm: DKG_ALGORITHM, version: CONFIG_VERSION, uuid: uuidv4(), timestamp: new Date().toISOString(), threshold: Math.ceil((2 * validatedCluster.operators.length) / 3), num_validators: validatedCluster.validators.length });
272
270
  try {
273
271
  const address = yield this.signer.getAddress();
274
272
  clusterConfig.creator = { address };
@@ -307,12 +305,12 @@ export class Client extends Base {
307
305
  if (!this.signer) {
308
306
  throw new Error('Signer is required in acceptClusterDefinition');
309
307
  }
310
- validatePayload(operatorPayload, operatorPayloadSchema);
308
+ const validatedPayload = validatePayload(operatorPayload, operatorPayloadSchema);
311
309
  try {
312
310
  const address = yield this.signer.getAddress();
313
311
  const operatorConfigSignature = yield this.signer.signTypedData(Domain(this.chainId), OperatorConfigHashSigningTypes, { operator_config_hash: configHash });
314
- const operatorENRSignature = yield this.signer.signTypedData(Domain(this.chainId), EnrSigningTypes, { enr: operatorPayload.enr });
315
- const operatorData = Object.assign(Object.assign({}, operatorPayload), { address, enr_signature: operatorENRSignature, fork_version: this.fork_version });
312
+ const operatorENRSignature = yield this.signer.signTypedData(Domain(this.chainId), EnrSigningTypes, { enr: validatedPayload.enr });
313
+ const operatorData = Object.assign(Object.assign({}, validatedPayload), { address, enr_signature: operatorENRSignature, fork_version: this.fork_version });
316
314
  const clusterDefinition = yield this.request(`/${DEFAULT_BASE_VERSION}/definition/${configHash}`, {
317
315
  method: 'PUT',
318
316
  body: JSON.stringify(operatorData),
@@ -1,37 +1,31 @@
1
+ import { ZeroAddress } from 'ethers';
1
2
  import { DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT, } from './constants';
3
+ import { VALID_DEPOSIT_AMOUNTS, VALID_NON_COMPOUNDING_AMOUNTS } from './ajv';
2
4
  export const operatorPayloadSchema = {
3
5
  type: 'object',
4
6
  properties: {
5
- version: {
6
- type: 'string',
7
- },
8
- enr: {
9
- type: 'string',
10
- },
7
+ version: { type: 'string' },
8
+ enr: { type: 'string' },
11
9
  },
12
10
  required: ['version', 'enr'],
13
11
  };
14
12
  export const definitionSchema = {
15
13
  type: 'object',
16
14
  properties: {
17
- name: {
18
- type: 'string',
19
- },
15
+ name: { type: 'string' },
20
16
  operators: {
21
17
  type: 'array',
22
18
  minItems: 4,
23
- uniqueItems: true,
24
19
  items: {
25
20
  type: 'object',
26
21
  properties: {
27
22
  address: {
28
23
  type: 'string',
29
- minLength: 42,
30
- maxLength: 42,
31
24
  },
32
25
  },
33
26
  required: ['address'],
34
27
  },
28
+ validateUniqueAddresses: true,
35
29
  },
36
30
  validators: {
37
31
  type: 'array',
@@ -52,12 +46,39 @@ export const definitionSchema = {
52
46
  },
53
47
  },
54
48
  deposit_amounts: {
55
- type: 'array',
49
+ type: ['array', 'null'],
56
50
  items: {
57
51
  type: 'string',
58
52
  pattern: '^[0-9]+$',
59
53
  },
60
- validDepositAmounts: true,
54
+ if: {
55
+ $data: '1/compounding',
56
+ },
57
+ then: {
58
+ items: {
59
+ enum: VALID_DEPOSIT_AMOUNTS,
60
+ },
61
+ },
62
+ else: {
63
+ items: {
64
+ enum: VALID_NON_COMPOUNDING_AMOUNTS,
65
+ },
66
+ },
67
+ default: null,
68
+ },
69
+ compounding: {
70
+ type: 'boolean',
71
+ default: true,
72
+ },
73
+ target_gas_limit: {
74
+ type: 'number',
75
+ minimum: 1,
76
+ default: 36000000,
77
+ },
78
+ consensus_protocol: {
79
+ type: 'string',
80
+ enum: ['qbft', ''],
81
+ default: '',
61
82
  },
62
83
  },
63
84
  required: ['name', 'operators', 'validators'],
@@ -74,9 +95,7 @@ export const totalSplitterPayloadSchema = {
74
95
  type: 'string',
75
96
  pattern: '^0x[a-fA-F0-9]{40}$',
76
97
  },
77
- percentAllocation: {
78
- type: 'number',
79
- },
98
+ percentAllocation: { type: 'number' },
80
99
  },
81
100
  required: ['account', 'percentAllocation'],
82
101
  },
@@ -84,29 +103,37 @@ export const totalSplitterPayloadSchema = {
84
103
  ObolRAFSplit: {
85
104
  type: 'number',
86
105
  minimum: DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
106
+ default: DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
87
107
  },
88
108
  distributorFee: {
89
109
  type: 'number',
90
110
  maximum: 10,
91
111
  multipleOf: 0.01,
112
+ default: 0,
92
113
  },
93
114
  controllerAddress: {
94
115
  type: 'string',
95
116
  pattern: '^0x[a-fA-F0-9]{40}$',
117
+ default: ZeroAddress,
96
118
  },
97
- validateSplitRecipients: true,
98
119
  },
120
+ validateTotalSplitRecipients: true,
99
121
  required: ['splitRecipients'],
100
122
  };
101
- export const rewardsSplitterPayloadSchema = Object.assign(Object.assign({}, totalSplitterPayloadSchema), { properties: Object.assign(Object.assign({}, totalSplitterPayloadSchema.properties), { ObolRAFSplit: {
123
+ export const rewardsSplitterPayloadSchema = {
124
+ type: 'object',
125
+ properties: Object.assign(Object.assign({}, totalSplitterPayloadSchema.properties), { ObolRAFSplit: {
102
126
  type: 'number',
103
127
  minimum: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
128
+ default: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
104
129
  }, recoveryAddress: {
105
130
  type: 'string',
106
131
  pattern: '^0x[a-fA-F0-9]{40}$',
107
- }, etherAmount: {
108
- type: 'number',
109
- }, principalRecipient: {
132
+ default: ZeroAddress,
133
+ }, etherAmount: { type: 'number' }, principalRecipient: {
110
134
  type: 'string',
111
135
  pattern: '^0x[a-fA-F0-9]{40}$',
112
- } }), required: ['splitRecipients', 'principalRecipient', 'etherAmount'] });
136
+ } }),
137
+ validateRewardsSplitRecipients: true,
138
+ required: ['splitRecipients', 'principalRecipient', 'etherAmount'],
139
+ };
@@ -11,15 +11,16 @@ import { isValidClusterLock } from './verification/common.js';
11
11
  /**
12
12
  * Verifies Cluster Lock's validity.
13
13
  * @param lock - cluster lock
14
+ * @param safeRpcUrl - optional safeRpcUrl for safe wallet verification
14
15
  * @returns {Promise<{ result: boolean }> } boolean result to indicate if lock is valid
15
16
  * @throws on missing keys or values.
16
17
  *
17
18
  * An example of how to use validateClusterLock:
18
19
  * [validateClusterLock](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L127)
19
20
  */
20
- export const validateClusterLock = (lock) => __awaiter(void 0, void 0, void 0, function* () {
21
+ export const validateClusterLock = (lock, safeRpcUrl) => __awaiter(void 0, void 0, void 0, function* () {
21
22
  try {
22
- const isLockValid = yield isValidClusterLock(lock);
23
+ const isLockValid = yield isValidClusterLock(lock, safeRpcUrl);
23
24
  return isLockValid;
24
25
  }
25
26
  catch (err) {
@@ -59,10 +59,10 @@ export const isContractAvailable = (contractAddress, provider, bytecode) => __aw
59
59
  }
60
60
  return !!code && code !== '0x' && code !== '0x0';
61
61
  });
62
- export const getProvider = (chainId) => {
63
- const rpcUrl = PROVIDER_MAP[chainId];
64
- if (!rpcUrl || rpcUrl === 'undefined') {
62
+ export const getProvider = (chainId, rpcUrl) => {
63
+ const resolvedRpcUrl = rpcUrl !== null && rpcUrl !== void 0 ? rpcUrl : PROVIDER_MAP[chainId];
64
+ if (chainId && (!resolvedRpcUrl || resolvedRpcUrl === 'undefined')) {
65
65
  throw new Error(`No provider configured for ${FORK_NAMES[chainId]}`);
66
66
  }
67
- return new ethers.JsonRpcProvider(rpcUrl);
67
+ return new ethers.JsonRpcProvider(resolvedRpcUrl);
68
68
  };
@@ -100,7 +100,7 @@ export const clusterLockHash = (clusterLock) => {
100
100
  };
101
101
  // Lock verification
102
102
  // cluster-definition signatures verification
103
- const validatePOSTConfigHashSigner = (address, signature, configHash, chainId) => __awaiter(void 0, void 0, void 0, function* () {
103
+ const validatePOSTConfigHashSigner = (address, signature, configHash, chainId, safeRpcUrl) => __awaiter(void 0, void 0, void 0, function* () {
104
104
  try {
105
105
  const data = signCreatorConfigHashPayload({ creator_config_hash: configHash }, chainId);
106
106
  return yield validateAddressSignature({
@@ -108,13 +108,14 @@ const validatePOSTConfigHashSigner = (address, signature, configHash, chainId) =
108
108
  token: signature,
109
109
  data,
110
110
  chainId,
111
+ safeRpcUrl,
111
112
  });
112
113
  }
113
114
  catch (err) {
114
115
  throw err;
115
116
  }
116
117
  });
117
- const validatePUTConfigHashSigner = (address, signature, configHash, chainId) => __awaiter(void 0, void 0, void 0, function* () {
118
+ const validatePUTConfigHashSigner = (address, signature, configHash, chainId, safeRpcUrl) => __awaiter(void 0, void 0, void 0, function* () {
118
119
  try {
119
120
  const data = signOperatorConfigHashPayload({ operator_config_hash: configHash }, chainId);
120
121
  return yield validateAddressSignature({
@@ -122,13 +123,14 @@ const validatePUTConfigHashSigner = (address, signature, configHash, chainId) =>
122
123
  token: signature,
123
124
  data,
124
125
  chainId,
126
+ safeRpcUrl,
125
127
  });
126
128
  }
127
129
  catch (err) {
128
130
  throw err;
129
131
  }
130
132
  });
131
- const validateEnrSigner = (address, signature, payload, chainId) => __awaiter(void 0, void 0, void 0, function* () {
133
+ const validateEnrSigner = (address, signature, payload, chainId, safeRpcUrl) => __awaiter(void 0, void 0, void 0, function* () {
132
134
  try {
133
135
  const data = signEnrPayload({ enr: payload }, chainId);
134
136
  return yield validateAddressSignature({
@@ -136,18 +138,19 @@ const validateEnrSigner = (address, signature, payload, chainId) => __awaiter(vo
136
138
  token: signature,
137
139
  data,
138
140
  chainId,
141
+ safeRpcUrl,
139
142
  });
140
143
  }
141
144
  catch (err) {
142
145
  throw err;
143
146
  }
144
147
  });
145
- const verifyDefinitionSignatures = (clusterDefinition, definitionType) => __awaiter(void 0, void 0, void 0, function* () {
148
+ const verifyDefinitionSignatures = (clusterDefinition, definitionType, safeRpcUrl) => __awaiter(void 0, void 0, void 0, function* () {
146
149
  if (definitionType === DefinitionFlow.Charon) {
147
150
  return true;
148
151
  }
149
152
  else {
150
- const isPOSTConfigHashSignerValid = yield validatePOSTConfigHashSigner(clusterDefinition.creator.address, clusterDefinition.creator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version]);
153
+ const isPOSTConfigHashSignerValid = yield validatePOSTConfigHashSigner(clusterDefinition.creator.address, clusterDefinition.creator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version], safeRpcUrl);
151
154
  if (!isPOSTConfigHashSignerValid) {
152
155
  return false;
153
156
  }
@@ -155,8 +158,8 @@ const verifyDefinitionSignatures = (clusterDefinition, definitionType) => __awai
155
158
  return true;
156
159
  }
157
160
  for (const operator of clusterDefinition.operators) {
158
- const isPUTConfigHashSignerValid = yield validatePUTConfigHashSigner(operator.address, operator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version]);
159
- const isENRSignerValid = yield validateEnrSigner(operator.address, operator.enr_signature, operator.enr, FORK_MAPPING[clusterDefinition.fork_version]);
161
+ const isPUTConfigHashSignerValid = yield validatePUTConfigHashSigner(operator.address, operator.config_signature, clusterDefinition.config_hash, FORK_MAPPING[clusterDefinition.fork_version], safeRpcUrl);
162
+ const isENRSignerValid = yield validateEnrSigner(operator.address, operator.enr_signature, operator.enr, FORK_MAPPING[clusterDefinition.fork_version], safeRpcUrl);
160
163
  if (!isPUTConfigHashSignerValid || !isENRSignerValid) {
161
164
  return false;
162
165
  }
@@ -287,13 +290,13 @@ const verifyLockData = (clusterLock) => __awaiter(void 0, void 0, void 0, functi
287
290
  }
288
291
  return false;
289
292
  });
290
- export const isValidClusterLock = (clusterLock) => __awaiter(void 0, void 0, void 0, function* () {
293
+ export const isValidClusterLock = (clusterLock, safeRpcUrl) => __awaiter(void 0, void 0, void 0, function* () {
291
294
  try {
292
295
  const definitionType = definitionFlow(clusterLock.cluster_definition);
293
296
  if (definitionType == null) {
294
297
  return false;
295
298
  }
296
- const isValidDefinitionData = yield verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
299
+ const isValidDefinitionData = yield verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType, safeRpcUrl);
297
300
  if (!isValidDefinitionData) {
298
301
  return false;
299
302
  }
@@ -14,9 +14,9 @@ import Safe from '@safe-global/protocol-kit';
14
14
  import { PROVIDER_MAP } from '../constants';
15
15
  import { hashTypedData } from '@safe-global/protocol-kit/dist/src/utils';
16
16
  import { isContractAvailable, getProvider } from '../utils';
17
- export const validateAddressSignature = ({ address, token, data, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
17
+ export const validateAddressSignature = ({ address, token, data, chainId, safeRpcUrl, }) => __awaiter(void 0, void 0, void 0, function* () {
18
18
  try {
19
- const provider = getProvider(chainId); // [TODO Hanan], should expect it from signer.provider, or passed in client
19
+ const provider = getProvider(chainId, safeRpcUrl);
20
20
  if (provider) {
21
21
  const contractAddress = yield isContractAvailable(address, provider);
22
22
  if (contractAddress) {
@@ -25,6 +25,7 @@ export const validateAddressSignature = ({ address, token, data, chainId, }) =>
25
25
  data: data,
26
26
  address,
27
27
  chainId,
28
+ safeRpcUrl,
28
29
  });
29
30
  }
30
31
  }
@@ -46,11 +47,11 @@ export const validateEOASignature = ({ token, data, address, }) => {
46
47
  throw err;
47
48
  }
48
49
  };
49
- export const validateSmartContractSignature = ({ token, data, address, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
50
+ export const validateSmartContractSignature = ({ token, data, address, chainId, safeRpcUrl, }) => __awaiter(void 0, void 0, void 0, function* () {
50
51
  try {
51
- const provider = PROVIDER_MAP[chainId];
52
+ const safeProvider = safeRpcUrl !== null && safeRpcUrl !== void 0 ? safeRpcUrl : PROVIDER_MAP[chainId];
52
53
  const protocolKit = yield Safe.init({
53
- provider,
54
+ provider: safeProvider,
54
55
  safeAddress: address,
55
56
  });
56
57
  const messageHash = hashTypedData(data);
@@ -141,6 +141,24 @@ export const verifyDVV1X8 = (clusterLock) => {
141
141
  for (const element of validatorPublicShares) {
142
142
  pubShares.push(fromHexString(element));
143
143
  }
144
+ // Check deposit amounts match exactly if they are defined
145
+ const depositAmounts = clusterLock.cluster_definition.deposit_amounts;
146
+ if (depositAmounts !== null) {
147
+ const partialDepositAmounts = validator.partial_deposit_data.map(d => d.amount);
148
+ if ((depositAmounts === null || depositAmounts === void 0 ? void 0 : depositAmounts.length) !== partialDepositAmounts.length) {
149
+ return false;
150
+ }
151
+ // Check that both arrays contain exactly the same elements
152
+ const sortedDepositAmounts = [...depositAmounts]
153
+ .map(Number)
154
+ .sort((a, b) => a - b);
155
+ const sortedPartialAmounts = [...partialDepositAmounts]
156
+ .map(Number)
157
+ .sort((a, b) => a - b);
158
+ if (!sortedDepositAmounts.every((amount, index) => amount === sortedPartialAmounts[index])) {
159
+ return false;
160
+ }
161
+ }
144
162
  // Deposit Data Verification
145
163
  for (const element of validator.partial_deposit_data) {
146
164
  const depositData = element;
@@ -170,22 +170,6 @@ export const clusterLockV1X7 = {
170
170
  ],
171
171
  };
172
172
  // v1.8.0
173
- export const clusterConfigV1X8 = {
174
- name: 'testSDK',
175
- operators: [
176
- { address: '0xC35CfCd67b9C27345a54EDEcC1033F2284148c81' },
177
- { address: '0x33807D6F1DCe44b9C599fFE03640762A6F08C496' },
178
- { address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f' },
179
- { address: '0xf6fF1a7A14D01e86a175bA958d3B6C75f2213966' },
180
- ],
181
- validators: [
182
- {
183
- fee_recipient_address: '0x3CD4958e76C317abcEA19faDd076348808424F99',
184
- withdrawal_address: '0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e',
185
- },
186
- ],
187
- deposit_amounts: ['8000000000', '16000000000', '8000000000'],
188
- };
189
173
  export const clusterLockV1X8 = {
190
174
  cluster_definition: {
191
175
  name: 'xxxx',
@@ -507,6 +491,40 @@ export const clusterLockWithSafe = {
507
491
  '0x4067921a5257efe4ceb103f2129faaa7a502781157b3b54e5efca559c558c2b43b89f30962e87df88fbf62250049a31888fcd62735d54b7553e5dc75c3b6ae0901',
508
492
  ],
509
493
  };
494
+ export const clusterConfigV1X10 = {
495
+ name: 'testSDK',
496
+ operators: [
497
+ { address: '0xC35CfCd67b9C27345a54EDEcC1033F2284148c81' },
498
+ { address: '0x33807D6F1DCe44b9C599fFE03640762A6F08C496' },
499
+ { address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f' },
500
+ { address: '0xf6fF1a7A14D01e86a175bA958d3B6C75f2213966' },
501
+ ],
502
+ validators: [
503
+ {
504
+ fee_recipient_address: '0x3CD4958e76C317abcEA19faDd076348808424F99',
505
+ withdrawal_address: '0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e',
506
+ },
507
+ ],
508
+ deposit_amounts: ['8000000000', '32000000000', '8000000000'],
509
+ compounding: true,
510
+ target_gas_limit: 36000000,
511
+ consensus_protocol: '',
512
+ };
513
+ export const soloClusterConfigV1X10 = {
514
+ name: 'testSDK',
515
+ operators: [
516
+ { address: '' },
517
+ { address: '' },
518
+ { address: '' },
519
+ { address: '' },
520
+ ],
521
+ validators: [
522
+ {
523
+ fee_recipient_address: '0x3CD4958e76C317abcEA19faDd076348808424F99',
524
+ withdrawal_address: '0xE0C5ceA4D3869F156717C66E188Ae81C80914a6e',
525
+ },
526
+ ],
527
+ };
510
528
  export const clusterLockV1X10 = {
511
529
  cluster_definition: {
512
530
  name: 'test',
@@ -771,3 +789,93 @@ export const clusterLockWithCompoundingWithdrawals = {
771
789
  '0x3e2548c3c35df90bd7af34f9cc439e7ec4e4fa5619f74ca393bde8e05942dfde0c2e5d95249f478533fe11581b794e5de55446452e4dd1648c33b319a987461701',
772
790
  ],
773
791
  };
792
+ export const clusterLockWithCompoundingWithdrawalss = {
793
+ cluster_definition: {
794
+ name: 'test v1.10.0',
795
+ creator: {
796
+ address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
797
+ config_signature: '0x62b609c3a56439fe60785895d60a36912b85d50c14a41fce36795c65d6263bb207467a4d4be7dc292bfd1c636332b1c409feaec6ce2ae9488ae1ad622264a7a81c',
798
+ },
799
+ operators: [
800
+ {
801
+ address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
802
+ enr: 'enr:-HW4QLlrtMjFLGkFT1bwdGbvZQlH8hLi0M2g44JAxEYP3BZmYpcsy9Q56HPPD87fMucjvLv4-obEFacpsg0ehRilbHeAgmlkgnY0iXNlY3AyNTZrMaEDRaa5o2aSgqyFq_ERZcQTztrOij1mFtXX1bJuVI6ieak',
803
+ config_signature: '0x58c31787cf1f9680f736713f4ce039c8bf69467e5128edb2888d380451bd757367665c1ba04dc7656eb0b77eac39f776780710e603372f2a41bd40f4d297c7fa1c',
804
+ enr_signature: '0x4df48b8fc16deb2441022a019b72976524f4ce3dd674ee70561fb9eb52985d3b0d9528532d3e5be7a44548be27e9e96c5358b9f6756c7c7a97e2126f9c2c9b071b',
805
+ },
806
+ {
807
+ address: '0xa084D9095BcBdFe8014B68fa52D73DCeb16C5129',
808
+ enr: 'enr:-Iu4QNbiUUUwT18LynBbVPJhNxvzQsaSpUr40mQTWscnZaqKb6vAlvV8j-eDDR3E0wjMQumGRbGm2IAb5_k4bVWJiVGAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPOiodUji0ohgJb5sNK1hgv8g6xO5_znZz3NkkBkyYyKIN0Y3CCDhqDdWRwgg4u',
809
+ config_signature: '0xc76b2f067a65c9cc6110ab99471b03f47545581e79164758114c98825d5440891843790ce92ca24ee7b3568b839cc88c2b3824587f8f260fca15bb852f69a3af1c',
810
+ enr_signature: '0x49e2a07424e412ed904fd47381c36203cb8c5575a31ba260b3f8420dc89a86fc0d7fbc087160a60690be2f6fb212423d467e212494992ab9a72f10319779a2cc1c',
811
+ },
812
+ {
813
+ address: '0xb1D2740Ee055693FE21bbCfc780729472C3f6B84',
814
+ enr: 'enr:-Iu4QJyserRukhG0Vgi2csu7GjpHYUGufNEbZ8Q7ZBrcZUb0KqpL5QzHonkh1xxHlxatTxrIcX_IS5J3SEWR_sa0ptGAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQMAUgEqczOjevyculnUIofhCj0DkgJudErM7qCYIvIkzIN0Y3CCDhqDdWRwgg4u',
815
+ config_signature: '0x8b0cea21e065d25faa3b9d3b9e8f497d3c2d8baf800a1c5ecbde64c041745b7e15131d28f7ab917bdcead3225dda16987f2fd160546e087987cbb483bd187cb11b',
816
+ enr_signature: '0x805f38269379087168900a37569f1108b9d4253a03c2650a4e8d69228cb4cc616b94988e4b218a8d47113063270c4a894e873a5070c7ef0f4d44ee3359eec51b1b',
817
+ },
818
+ {
819
+ address: '0x69D39e2d0Fb6fb80CCd5d49a75416cB7d1BC6DB6',
820
+ enr: 'enr:-HW4QKJTwXC6Chw6zbnA3HFZi6Jo0DkIgjKy4eUBpsSOGnAeWE6ChEjEyk_6R6Qrm7jI-iqfs3_HYxiKde8vFgvHHrCAgmlkgnY0iXNlY3AyNTZrMaECfFKQH4spdZCHqrKVz1Q02xYla6J_RQECDNNYBRWdzv8',
821
+ config_signature: '0x9e21c2308738fb17b38d1850b83312c25306c0bcbd575e0417e311d5fc1697807291328a299859df70ac3efb402207067dbb0559bc4fef4e6ca2fd77fa4c2a321c',
822
+ enr_signature: '0x3a6590600b2dc27b042aabd3fa4654f1064b42e8f850d1fac13394a6786463be2a2727623e4e9e98453113f7024e73d652d1c6288ef82ede7acbb08e25ff00751b',
823
+ },
824
+ ],
825
+ uuid: '34036642-9d45-4b6f-817d-f8d5aa0110d4',
826
+ version: 'v1.10.0',
827
+ timestamp: '2025-04-03T17:27:27.767Z',
828
+ num_validators: 1,
829
+ threshold: 3,
830
+ validators: [
831
+ {
832
+ fee_recipient_address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
833
+ withdrawal_address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f',
834
+ },
835
+ ],
836
+ dkg_algorithm: 'default',
837
+ fork_version: '0x10000910',
838
+ deposit_amounts: ['32000000000'],
839
+ consensus_protocol: 'abft',
840
+ target_gas_limit: 30000000,
841
+ compounding: true,
842
+ config_hash: '0xe6d2e8fe49d722f55ced8a028c62ffbde16a05bc74d7d9703b675ecc8c820b4b',
843
+ definition_hash: '0xb5d81494f98df23cdff33909888080054f1068c11bbee5ec39aeb95edfa3b37c',
844
+ },
845
+ distributed_validators: [
846
+ {
847
+ distributed_public_key: '0x8368525a657df38fd3d774a08f1a54f008ae3340e8441a2bbb2d2051a9a8f46d4eeb8610bbc088e7402cba6b138c7b70',
848
+ public_shares: [
849
+ '0xae455c727456238fadd7b84ded0f0fb700d0e2fa028fff34e3fc73d0279ee9a5aa9843f8af00c9518ea781d8d823cc0d',
850
+ '0x91a7cfa9682464dff7324c0907fdff41df3f2643fd97d30559d82fbbfa354dae1e3f9176d8562b5ab47d3460240004cb',
851
+ '0xac8c0ffdb9ea7ce63bbed67122123c86ffa669a5027da5f96ab85b68c3a491253ebf0308cf4e237ea4eaf8543064c8a8',
852
+ '0x85385ef7cf1b1d9cd648ceb45e92516ef9612c9830b0abf808ea7996ad9369764bb9ac27c3cd730497e12e394ab48d16',
853
+ ],
854
+ builder_registration: {
855
+ message: {
856
+ fee_recipient: '0xc6e76f72ea672fae05c357157cfc37720f0af26f',
857
+ gas_limit: 30000000,
858
+ timestamp: 1742213400,
859
+ pubkey: '0x8368525a657df38fd3d774a08f1a54f008ae3340e8441a2bbb2d2051a9a8f46d4eeb8610bbc088e7402cba6b138c7b70',
860
+ },
861
+ signature: '0xb73c4d55c17bdbbb53790fee3294ef2c69b2a6e4c700eb30603e437308005edacbe76591f917aa82cfcf60da2d669ccd101ae9b3dc4b1ae61ab7ad1794620adbdc0b4ea5f91fc5e790833537c610a57ccdc4c95ca53cb6dedec2be2ad8acf266',
862
+ },
863
+ partial_deposit_data: [
864
+ {
865
+ pubkey: '0x8368525a657df38fd3d774a08f1a54f008ae3340e8441a2bbb2d2051a9a8f46d4eeb8610bbc088e7402cba6b138c7b70',
866
+ withdrawal_credentials: '0x020000000000000000000000c6e76f72ea672fae05c357157cfc37720f0af26f',
867
+ amount: '32000000000',
868
+ signature: '0xa7c5c2417f5afd089b87df0eff10855b748d621749879b6087c8a1dbfcbb95aa79a2c8f4d637ecb283426b9c5af07c30080c19a245ea614ff3f609a92994e9a29f3c0f8c4bf1a4e7ef58615ff80586b40e36859c9fd935b68ae55670e29ea5d7',
869
+ },
870
+ ],
871
+ },
872
+ ],
873
+ signature_aggregate: '0x8b9f884ab3ab89d63f49437cae09e2b77757787f2c54bb52bc51df1d565bc6d63ef9fc4e0ea76bd72a58618651b827a611d8e96de238fad4616420b4cd393ad62428ac9db2e01a4e8dd8ae835845e7530f9e1b3b4ebf5ba2da3c77aec70d06b2',
874
+ lock_hash: '0xaf6b0d989fdd82f7f9683d62b9f797af129f9afae2a4b453a0bef1814b21ae5d',
875
+ node_signatures: [
876
+ '0xc059b3920fc107a909feb16217c7372911c5cb386954ed899b5d8e782d6d71e17a5dde1c2d4825b7c2b5edd63b4eb9c306afd4afd5dbce3f8db9c13d06d6cebf01',
877
+ '0x051a72e5f2a66a85aabd635a210350f82924350170981575f3cb67e7699791d7118ab7c284f10b52f3604a89aa0bc43ad2d2537a7b19b109610324926475d30900',
878
+ '0xa28a23de3703c3eeb9a091d4f47302a96079759baf1afca428cbd298b46c40fe103df0006dfd5a0ffd15ee7c5fa436d70d7a03d4537dd8292a6152734e5f689601',
879
+ '0x992ab5c73d30d10cc5c8f6d462ba387fd5a698e206b0dfdd0224d17117f85a2077fcaa62fd6d5170ff678249288fcd82877e2987ed5564df5dbd9d51370871c800',
880
+ ],
881
+ };
@@ -120,7 +120,7 @@ describe('Client.incentives', () => {
120
120
  .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
121
121
  const result = yield clientInstance.incentives.isClaimed(mockIncentivesData.contract_address, mockIncentivesData.index);
122
122
  expect(result).toBe(true);
123
- expect(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(clientInstance.incentives.chainId, mockIncentivesData.contract_address, mockIncentivesData.index, {});
123
+ expect(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(mockIncentivesData.contract_address, mockIncentivesData.index, {});
124
124
  }));
125
125
  test('isClaimed should return false when incentive is not claimed', () => __awaiter(void 0, void 0, void 0, function* () {
126
126
  jest
@@ -147,7 +147,7 @@ describe('Client.incentives', () => {
147
147
  .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
148
148
  const result = yield clientWithoutSigner.incentives.isClaimed(mockIncentivesData.contract_address, mockIncentivesData.index);
149
149
  expect(result).toBe(true);
150
- expect(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(clientWithoutSigner.incentives.chainId, mockIncentivesData.contract_address, mockIncentivesData.index, provider);
150
+ expect(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(mockIncentivesData.contract_address, mockIncentivesData.index, provider);
151
151
  }));
152
152
  test('getIncentivesByAddress should make the correct API request', () => __awaiter(void 0, void 0, void 0, function* () {
153
153
  const mockAddress = '0x1234567890abcdef1234567890abcdef12345678';