@obolnetwork/obol-sdk 2.0.0 → 2.1.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 +6 -0
  2. package/dist/cjs/package.json +4 -7
  3. package/dist/cjs/src/abi/Multicall.js +148 -0
  4. package/dist/cjs/src/abi/OWR.js +133 -0
  5. package/dist/cjs/src/abi/SplitMain.js +929 -0
  6. package/dist/cjs/src/ajv.js +17 -2
  7. package/dist/cjs/src/bytecodes.js +9 -0
  8. package/dist/cjs/src/constants.js +48 -1
  9. package/dist/cjs/src/index.js +146 -0
  10. package/dist/cjs/src/schema.js +52 -5
  11. package/dist/cjs/src/splitHelpers.js +177 -0
  12. package/dist/cjs/src/utils.js +22 -1
  13. package/dist/cjs/src/verification/common.js +8 -0
  14. package/dist/cjs/src/verification/v1.8.0.js +5 -3
  15. package/dist/cjs/test/fixtures.js +119 -2
  16. package/dist/cjs/test/methods.test.js +227 -11
  17. package/dist/esm/package.json +4 -7
  18. package/dist/esm/src/abi/Multicall.js +145 -0
  19. package/dist/esm/src/abi/OWR.js +130 -0
  20. package/dist/esm/src/abi/SplitMain.js +926 -0
  21. package/dist/esm/src/ajv.js +17 -2
  22. package/dist/esm/src/bytecodes.js +6 -0
  23. package/dist/esm/src/constants.js +47 -0
  24. package/dist/esm/src/index.js +148 -2
  25. package/dist/esm/src/schema.js +51 -4
  26. package/dist/esm/src/splitHelpers.js +168 -0
  27. package/dist/esm/src/utils.js +19 -0
  28. package/dist/esm/src/verification/common.js +8 -0
  29. package/dist/esm/src/verification/v1.8.0.js +5 -3
  30. package/dist/esm/test/fixtures.js +118 -1
  31. package/dist/esm/test/methods.test.js +206 -13
  32. package/dist/types/src/abi/Multicall.d.ts +35 -0
  33. package/dist/types/src/abi/OWR.d.ts +52 -0
  34. package/dist/types/src/abi/SplitMain.d.ts +1159 -0
  35. package/dist/types/src/bytecodes.d.ts +6 -0
  36. package/dist/types/src/constants.d.ts +25 -0
  37. package/dist/types/src/index.d.ts +29 -1
  38. package/dist/types/src/schema.d.ts +85 -4
  39. package/dist/types/src/splitHelpers.d.ts +62 -0
  40. package/dist/types/src/types.d.ts +40 -5
  41. package/dist/types/src/utils.d.ts +3 -0
  42. package/dist/types/test/fixtures.d.ts +51 -0
  43. package/package.json +4 -7
  44. package/src/abi/Multicall.ts +145 -0
  45. package/src/abi/OWR.ts +130 -0
  46. package/src/abi/SplitMain.ts +927 -0
  47. package/src/ajv.ts +33 -2
  48. package/src/bytecodes.ts +12 -0
  49. package/src/constants.ts +59 -0
  50. package/src/index.ts +244 -2
  51. package/src/schema.ts +67 -4
  52. package/src/splitHelpers.ts +341 -0
  53. package/src/types.ts +50 -6
  54. package/src/utils.ts +21 -0
  55. package/src/verification/common.ts +12 -0
  56. package/src/verification/v1.8.0.ts +4 -4
@@ -0,0 +1,168 @@
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
+ import { Contract, Interface, parseEther, ZeroAddress, } from 'ethers';
11
+ import { OWRFactoryContract } from './abi/OWR';
12
+ import { splitMainEthereumAbi } from './abi/SplitMain';
13
+ import { MultiCallContract } from './abi/Multicall';
14
+ import { CHAIN_CONFIGURATION } from './constants';
15
+ const splitMainContractInterface = new Interface(splitMainEthereumAbi);
16
+ const owrFactoryContractInterface = new Interface(OWRFactoryContract.abi);
17
+ export const formatSplitRecipients = (recipients) => {
18
+ // Has to be sorted when passed
19
+ recipients.sort((a, b) => a.account.localeCompare(b.account));
20
+ const accounts = recipients.map(item => item.account);
21
+ const percentAllocations = recipients.map(recipient => {
22
+ const splitTostring = (recipient.percentAllocation * 1e4).toFixed(0);
23
+ return parseInt(splitTostring);
24
+ });
25
+ return { accounts, percentAllocations };
26
+ };
27
+ export const predictSplitterAddress = ({ signer, accounts, percentAllocations, chainId, distributorFee, controllerAddress, }) => __awaiter(void 0, void 0, void 0, function* () {
28
+ try {
29
+ let predictedSplitterAddress;
30
+ const splitMainContractInstance = new Contract(CHAIN_CONFIGURATION[chainId].SPLITMAIN_ADDRESS.address, splitMainEthereumAbi, signer);
31
+ if (controllerAddress === ZeroAddress) {
32
+ predictedSplitterAddress =
33
+ yield splitMainContractInstance.predictImmutableSplitAddress(accounts, percentAllocations, distributorFee);
34
+ }
35
+ else {
36
+ // It throws on deployed Immutable splitter
37
+ predictedSplitterAddress =
38
+ yield splitMainContractInstance.createSplit.staticCall(accounts, percentAllocations, distributorFee, controllerAddress);
39
+ }
40
+ return predictedSplitterAddress;
41
+ }
42
+ catch (e) {
43
+ throw e;
44
+ }
45
+ });
46
+ export const handleDeployOWRAndSplitter = ({ signer, isSplitterDeployed, predictedSplitterAddress, accounts, percentAllocations, etherAmount, principalRecipient, chainId, distributorFee, controllerAddress, recoveryAddress, }) => __awaiter(void 0, void 0, void 0, function* () {
47
+ try {
48
+ if (isSplitterDeployed) {
49
+ const owrAddress = yield createOWRContract({
50
+ owrArgs: {
51
+ principalRecipient,
52
+ amountOfPrincipalStake: etherAmount,
53
+ predictedSplitterAddress,
54
+ recoveryAddress,
55
+ },
56
+ signer,
57
+ chainId,
58
+ });
59
+ return {
60
+ withdrawal_address: owrAddress,
61
+ fee_recipient_address: predictedSplitterAddress,
62
+ };
63
+ }
64
+ else {
65
+ const { owrAddress, splitterAddress } = yield deploySplitterAndOWRContracts({
66
+ owrArgs: {
67
+ principalRecipient,
68
+ amountOfPrincipalStake: etherAmount,
69
+ predictedSplitterAddress,
70
+ recoveryAddress,
71
+ },
72
+ splitterArgs: {
73
+ accounts,
74
+ percentAllocations,
75
+ distributorFee,
76
+ controllerAddress,
77
+ },
78
+ signer,
79
+ chainId,
80
+ });
81
+ return {
82
+ withdrawal_address: owrAddress,
83
+ fee_recipient_address: splitterAddress,
84
+ };
85
+ }
86
+ }
87
+ catch (e) {
88
+ throw e;
89
+ }
90
+ });
91
+ const createOWRContract = ({ owrArgs, signer, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
92
+ var _a;
93
+ try {
94
+ const OWRFactoryInstance = new Contract(CHAIN_CONFIGURATION[chainId].OWR_FACTORY_ADDRESS.address, OWRFactoryContract.abi, signer);
95
+ const tx = yield OWRFactoryInstance.createOWRecipient(owrArgs.recoveryAddress, owrArgs.principalRecipient, owrArgs.predictedSplitterAddress, parseEther(owrArgs.amountOfPrincipalStake.toString()));
96
+ const receipt = yield tx.wait();
97
+ const OWRAddressData = (_a = receipt === null || receipt === void 0 ? void 0 : receipt.logs[0]) === null || _a === void 0 ? void 0 : _a.topics[1];
98
+ const formattedOWRAddress = '0x' + (OWRAddressData === null || OWRAddressData === void 0 ? void 0 : OWRAddressData.slice(26, 66));
99
+ return formattedOWRAddress;
100
+ }
101
+ catch (e) {
102
+ throw e;
103
+ }
104
+ });
105
+ export const deploySplitterContract = ({ signer, accounts, percentAllocations, chainId, distributorFee, controllerAddress, }) => __awaiter(void 0, void 0, void 0, function* () {
106
+ var _b;
107
+ try {
108
+ const splitMainContractInstance = new Contract(CHAIN_CONFIGURATION[chainId].SPLITMAIN_ADDRESS.address, splitMainEthereumAbi, signer);
109
+ const tx = yield splitMainContractInstance.createSplit(accounts, percentAllocations, distributorFee, controllerAddress);
110
+ const receipt = yield tx.wait();
111
+ const splitterAddressData = (_b = receipt === null || receipt === void 0 ? void 0 : receipt.logs[0]) === null || _b === void 0 ? void 0 : _b.topics[1];
112
+ const formattedSplitterAddress = '0x' + (splitterAddressData === null || splitterAddressData === void 0 ? void 0 : splitterAddressData.slice(26, 66));
113
+ return formattedSplitterAddress;
114
+ }
115
+ catch (e) {
116
+ throw e;
117
+ }
118
+ });
119
+ export const deploySplitterAndOWRContracts = ({ owrArgs, splitterArgs, signer, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
120
+ var _c, _d;
121
+ const executeCalls = [];
122
+ try {
123
+ const splitTxData = encodeCreateSplitTxData(splitterArgs.accounts, splitterArgs.percentAllocations, splitterArgs.distributorFee, splitterArgs.controllerAddress);
124
+ const owrTxData = encodeCreateOWRecipientTxData(owrArgs.recoveryAddress, owrArgs.principalRecipient, owrArgs.predictedSplitterAddress, owrArgs.amountOfPrincipalStake);
125
+ executeCalls.push({
126
+ target: CHAIN_CONFIGURATION[chainId].SPLITMAIN_ADDRESS.address,
127
+ callData: splitTxData,
128
+ }, {
129
+ target: CHAIN_CONFIGURATION[chainId].OWR_FACTORY_ADDRESS.address,
130
+ callData: owrTxData,
131
+ });
132
+ const multicallAddess = CHAIN_CONFIGURATION[chainId].MULTICALL_ADDRESS.address;
133
+ const executeMultiCalls = yield multicall(executeCalls, signer, multicallAddess);
134
+ const splitAddressData = (_c = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.logs[0]) === null || _c === void 0 ? void 0 : _c.topics[1];
135
+ const formattedSplitterAddress = '0x' + (splitAddressData === null || splitAddressData === void 0 ? void 0 : splitAddressData.slice(26, 66));
136
+ const owrAddressData = (_d = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.logs[1]) === null || _d === void 0 ? void 0 : _d.topics[1];
137
+ const formattedOwrAddress = '0x' + (owrAddressData === null || owrAddressData === void 0 ? void 0 : owrAddressData.slice(26, 66));
138
+ return {
139
+ owrAddress: formattedOwrAddress,
140
+ splitterAddress: formattedSplitterAddress,
141
+ };
142
+ }
143
+ catch (e) {
144
+ throw e;
145
+ }
146
+ });
147
+ export const multicall = (calls, signer, multicallAddress) => __awaiter(void 0, void 0, void 0, function* () {
148
+ const multiCallContractInstance = new Contract(multicallAddress, MultiCallContract.abi, signer);
149
+ const tx = yield multiCallContractInstance.aggregate(calls);
150
+ const receipt = yield tx.wait();
151
+ return receipt;
152
+ });
153
+ const encodeCreateSplitTxData = (accounts, percentAllocations, distributorFee, controller) => {
154
+ return splitMainContractInterface.encodeFunctionData('createSplit', [
155
+ accounts,
156
+ percentAllocations,
157
+ distributorFee,
158
+ controller,
159
+ ]);
160
+ };
161
+ const encodeCreateOWRecipientTxData = (recoveryAddress, principalRecipient, rewardRecipient, amountOfPrincipalStake) => {
162
+ return owrFactoryContractInterface.encodeFunctionData('createOWRecipient', [
163
+ recoveryAddress,
164
+ principalRecipient,
165
+ rewardRecipient,
166
+ parseEther(amountOfPrincipalStake.toString()),
167
+ ]);
168
+ };
@@ -1,3 +1,12 @@
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
+ };
1
10
  import { DefinitionFlow } from './constants';
2
11
  export const hexWithout0x = (hex) => {
3
12
  return hex.slice(2, hex.length);
@@ -38,3 +47,13 @@ export const definitionFlow = (clusterDefinition) => {
38
47
  }
39
48
  return null;
40
49
  };
50
+ export const findDeployedBytecode = (contractAddress, provider) => __awaiter(void 0, void 0, void 0, function* () {
51
+ return yield (provider === null || provider === void 0 ? void 0 : provider.getCode(contractAddress));
52
+ });
53
+ export const isContractAvailable = (contractAddress, provider, bytecode) => __awaiter(void 0, void 0, void 0, function* () {
54
+ const code = yield findDeployedBytecode(contractAddress, provider);
55
+ if (bytecode) {
56
+ return !!code && code === bytecode;
57
+ }
58
+ return !!code && code !== '0x' && code !== '0x0';
59
+ });
@@ -63,6 +63,14 @@ export const clusterLockHash = (clusterLock) => {
63
63
  return hashClusterLockV1X7(clusterLock);
64
64
  }
65
65
  if (semver.eq(clusterLock.cluster_definition.version, 'v1.8.0')) {
66
+ if (clusterLock.cluster_definition.deposit_amounts === null &&
67
+ clusterLock.distributed_validators.some(distributedValidator => {
68
+ var _a;
69
+ return ((_a = distributedValidator.partial_deposit_data) === null || _a === void 0 ? void 0 : _a.length) !== 1 ||
70
+ distributedValidator.partial_deposit_data[0].amount !== '32000000000';
71
+ })) {
72
+ throw new Error('mismatch between deposit_amounts and partial_deposit_data fields');
73
+ }
66
74
  return hashClusterLockV1X8(clusterLock);
67
75
  }
68
76
  // other versions
@@ -63,9 +63,11 @@ export const hashClusterDefinitionV1X8 = (cluster, configOnly) => {
63
63
  withdrawal_address: fromHexString(validator.withdrawal_address),
64
64
  };
65
65
  });
66
- val.deposit_amounts = cluster.deposit_amounts.map((amount) => {
67
- return parseInt(amount);
68
- });
66
+ if (cluster.deposit_amounts) {
67
+ val.deposit_amounts = cluster.deposit_amounts.map((amount) => {
68
+ return parseInt(amount);
69
+ });
70
+ }
69
71
  if (!configOnly) {
70
72
  val.config_hash = fromHexString(cluster.config_hash);
71
73
  }
@@ -176,7 +176,7 @@ export const clusterConfigV1X8 = {
176
176
  { address: '0xC35CfCd67b9C27345a54EDEcC1033F2284148c81' },
177
177
  { address: '0x33807D6F1DCe44b9C599fFE03640762A6F08C496' },
178
178
  { address: '0xc6e76F72Ea672FAe05C357157CfC37720F0aF26f' },
179
- { address: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC' },
179
+ { address: '0xf6fF1a7A14D01e86a175bA958d3B6C75f2213966' },
180
180
  ],
181
181
  validators: [
182
182
  {
@@ -303,3 +303,120 @@ export const clusterLockV1X8 = {
303
303
  '0x0135f307831fa58ff64f9af46e00b92e2375162646af88f6991ba6f98a8c262f2c41846af59da68eadb8ff9c94db42ee8005bd4236e5897635573db2911460aa00',
304
304
  ],
305
305
  };
306
+ export const nullDepositAmountsClusterLockV1X8 = {
307
+ cluster_definition: {
308
+ name: 'null deposit_amounts',
309
+ creator: {
310
+ address: '',
311
+ config_signature: '',
312
+ },
313
+ operators: [
314
+ {
315
+ address: '',
316
+ enr: 'enr:-HW4QAsMPbcGWZlaTYG9NfyzIKnw6wN11MrH9OKeLq_a_mB8akJTkoqY8p_OrlMW3IWpcP2KcfaamQoAAL22gFnxAFaAgmlkgnY0iXNlY3AyNTZrMaECc8v8p8k9isB8n3q_0gs969Ec2H14tHwQ0KmyeskZ1O4',
317
+ config_signature: '',
318
+ enr_signature: '',
319
+ },
320
+ {
321
+ address: '',
322
+ enr: 'enr:-HW4QN2uvJ-A-HfrFFXk00h6_3woLkkJjduAAs4xpkp1g1pebhTFPEnTrnI0Pt2pdtH3OT5Yl6nFF4vzYW5gGJqs0cWAgmlkgnY0iXNlY3AyNTZrMaECA3fE7ero5CypBt5k_OPhXxjMVaG5U8pkcKNaZnWxhAI',
323
+ config_signature: '',
324
+ enr_signature: '',
325
+ },
326
+ {
327
+ address: '',
328
+ enr: 'enr:-HW4QKI7VF_B7JXG5w03Mm59hGM-p1odnD8bGiKFEdAXY4nqK2GP_2GQPY3LZxU6XXnuihkZJh8qo2Z2U_bBZVTII0WAgmlkgnY0iXNlY3AyNTZrMaECe9MQDo5o_v7q0eaHXn4OzgcfH4avwWM0u29cGnHDnGg',
329
+ config_signature: '',
330
+ enr_signature: '',
331
+ },
332
+ {
333
+ address: '',
334
+ enr: 'enr:-HW4QDi0E7eFnahIJd94VSbIG2_IuhkSVTAx_VOM6oQkDhA0eZTHZSQ2c00vsa8AeR8LPZjmUwDD7KvNxgebHc7UotqAgmlkgnY0iXNlY3AyNTZrMaECMBVL5rrUkm3sUq5F9t_MIoQWz5MQuo6NTr_5PhbxcjQ',
335
+ config_signature: '',
336
+ enr_signature: '',
337
+ },
338
+ ],
339
+ uuid: '23C75265-554C-83C1-4B82-A0F30159137E',
340
+ version: 'v1.8.0',
341
+ timestamp: '2024-08-06T14:51:06Z',
342
+ num_validators: 2,
343
+ threshold: 3,
344
+ validators: [
345
+ {
346
+ fee_recipient_address: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
347
+ withdrawal_address: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
348
+ },
349
+ {
350
+ fee_recipient_address: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
351
+ withdrawal_address: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
352
+ },
353
+ ],
354
+ dkg_algorithm: 'default',
355
+ fork_version: '0x00000000',
356
+ deposit_amounts: null,
357
+ config_hash: '0x2147b404cc1bbfb269e6d9fb244966221fcce33b15ab85b10652338254e4e81d',
358
+ definition_hash: '0xcb20df41459fba6adf25f49e94bcf06a7cce14289e186db256114ad8328137a8',
359
+ },
360
+ distributed_validators: [
361
+ {
362
+ distributed_public_key: '0x81bfe823b95a9f8799de07f6ae6accf1d7dc8f454be5e049f4e46d4dbd557269d3139ea7ede545c71517d099dc5da90a',
363
+ public_shares: [
364
+ '0xa52a4241e37b4e830f93bd740177d08c3308de5b7711d77641d278665c5c18961e02cb18ec394634d043b815a6a7cf31',
365
+ '0x8f1f017045e63451a3e421f3e5d1219f9e0dbee1ca312f31282ed09a67bbe2dd554d6fcb94c3d1ea37228a147ac14255',
366
+ '0x84f25caa20ac1ff695b9696b27042daec76b32c90bededc09908aed7b03198326ea63b3c7b6767395947ac76074bb688',
367
+ '0xb1718a37d0db33d5e935b1ee950fbf5ad88fe5fb558aa1cd252391f48cf69feaf2beccef1e6b7cbb5265729cb45cbf5b',
368
+ ],
369
+ builder_registration: {
370
+ message: {
371
+ fee_recipient: '0x86b8145c98e5bd25ba722645b15ed65f024a87ec',
372
+ gas_limit: 30000000,
373
+ timestamp: 1606824023,
374
+ pubkey: '0x81bfe823b95a9f8799de07f6ae6accf1d7dc8f454be5e049f4e46d4dbd557269d3139ea7ede545c71517d099dc5da90a',
375
+ },
376
+ signature: '0xb021590540f9c0a7ab1f7030a510f11dcfe701d7fca8d0811eedc898951457d28f269f6a0fecfc186136ba1522f2e93b18043313c5b76936d1ae923d736500c7283a9dafd3a59f4d80026d73aa9a3219453da62ad5721960f66119b0da624e5c',
377
+ },
378
+ partial_deposit_data: [
379
+ {
380
+ pubkey: '0x81bfe823b95a9f8799de07f6ae6accf1d7dc8f454be5e049f4e46d4dbd557269d3139ea7ede545c71517d099dc5da90a',
381
+ withdrawal_credentials: '0x01000000000000000000000086b8145c98e5bd25ba722645b15ed65f024a87ec',
382
+ amount: '32000000000',
383
+ signature: '0xb9969177a01747d320f3bd77f8010459b3e1e310e1a3b6c75bfbea1cd870fbd26a8a34131b8eadc480a8b6d0255a4501115f953617c10ac5032a12ed5002540b91e535b1b52b18d0a500c3f422039c950d29e61bf411fefc4e9d0aa568a43ca1',
384
+ },
385
+ ],
386
+ },
387
+ {
388
+ distributed_public_key: '0xb64918e0ac1d5e306c37d3ee21c9fbca3877397d9448841c779dcfdd6f4b9a949c285f72b19305b08e968b7d09974661',
389
+ public_shares: [
390
+ '0xb01cc7ecd82911092484cbb4cf211c14183f4820e037026afead639028b731ef0d7b62221fbcac0c0a48b12116063a32',
391
+ '0xacb06f314e0c116e5e33bffd2ada93832ca1215ea004c046f3d0cdee1bdc1c05787c074216dd0c99464bc471e179f981',
392
+ '0xa3bbf87357b4ebc6a479aeea73951609b9e4c4b1f28fdd2cf91350207029dc44fc28452792f6053c0f2889d9d4bbe9b0',
393
+ '0x84fc2f454d3fb023abcca1abab037d247923cd506a8ec26ef01755d10053acd2176af7baff805a0c7c2442d2ff981cf4',
394
+ ],
395
+ builder_registration: {
396
+ message: {
397
+ fee_recipient: '0x86b8145c98e5bd25ba722645b15ed65f024a87ec',
398
+ gas_limit: 30000000,
399
+ timestamp: 1606824023,
400
+ pubkey: '0xb64918e0ac1d5e306c37d3ee21c9fbca3877397d9448841c779dcfdd6f4b9a949c285f72b19305b08e968b7d09974661',
401
+ },
402
+ signature: '0xb91c5bc699018d1c0a9b68dacd60f5c85e9d2d769b4271d4c586cc576f0598e48257eafaa0db474b2d1f11fff7ed31d900d4052ae10675fc9713565c8131491588f522da114ea4f506712521683166cd729ddae1d854ca02708d0e15a4cf2155',
403
+ },
404
+ partial_deposit_data: [
405
+ {
406
+ pubkey: '0xb64918e0ac1d5e306c37d3ee21c9fbca3877397d9448841c779dcfdd6f4b9a949c285f72b19305b08e968b7d09974661',
407
+ withdrawal_credentials: '0x01000000000000000000000086b8145c98e5bd25ba722645b15ed65f024a87ec',
408
+ amount: '32000000000',
409
+ signature: '0xaaea8e3debc299b927e3930f4305dbe7a89a9ec217d0f262d01d68e64b1afc32d5f32d0d8cbfbdaf2edc2fbe97017bc4085ac76f0be189ce12d9cb80821d4b6a3ec29d3d0a78cc14e0a5117974b518860d4c5f8644126a82758dee5b944957a4',
410
+ },
411
+ ],
412
+ },
413
+ ],
414
+ signature_aggregate: '0xb0b2c13ec9c4bf39d731ad38710c67a86861945e320a51ec42c0fe1aba53c0afd9eed63d9e0b0bc503c615fe1aefd3d619292ef278463b3ca3c11741df12615e2984eeeb95750fa898c88fefbca7609da0b1dbfb225a03615bddfbf1f2d00a76',
415
+ lock_hash: '0xa79a0f71a7e0412faefd88197785cf4edeb36d4c07343e91132fdd38a649926f',
416
+ node_signatures: [
417
+ '0xe1406808f92ffff202cc627f21cf8a05ea6e5dd823fb2ce0c6b27286c09c7cfe4fa267752a3a53a276270d6d709d27cd5df292d2d5a1d442ad268126ff500c0400',
418
+ '0x7538367a0884a26df61d5514327c1791eb4cb34ab270b2c4fedba6f5954f39c445e49307a149b2ea341aaffb6213de498dd02185583b3fe04929756fe3a41c2300',
419
+ '0x971c848e4f1ec79aa954b5d6e2af7d445631f0ae082be87e9ac0332f5d693de72a52850583b52a10fbdb1ee8bdd009bafd7c1c5dbc726c623a2b22beee4e267501',
420
+ '0xdb6332a4ca9c41592511e7480c835b449fea120430e759735f3c24f1a6d496551a516151d2baae21d0cc9469b28dc1122b672af40ebb0e77045da92e0da6a03500',
421
+ ],
422
+ };
@@ -7,31 +7,27 @@ 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 { ethers } from 'ethers';
10
+ import { ethers, JsonRpcProvider } from 'ethers';
11
11
  import { Client, validateClusterLock } from '../src/index';
12
- import { clusterConfigV1X7, clusterConfigV1X8, clusterLockV1X6, clusterLockV1X7, clusterLockV1X8, } from './fixtures.js';
12
+ import { clusterConfigV1X7, clusterConfigV1X8, clusterLockV1X6, clusterLockV1X7, clusterLockV1X8, 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';
16
16
  import { HttpResponse, http } from 'msw';
17
17
  import { setupServer } from 'msw/node';
18
18
  import { hashTermsAndConditions } from '../src/verification/termsAndConditions';
19
+ import * as utils from '../src/utils';
20
+ import * as splitsHelpers from '../src/splitHelpers';
19
21
  /* eslint no-new: 0 */
20
22
  describe('Cluster Client', () => {
21
23
  var _a, _b;
22
24
  const mockConfigHash = '0x1f6c94e6c070393a68c1aa6073a21cb1fd57f0e14d2a475a2958990ab728c2fd';
23
25
  const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
24
26
  const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
25
- const wallet = new ethers.Wallet(privateKey);
26
- const mockSigner = wallet.connect(null);
27
- const clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 5 }, mockSigner);
28
- // test('throws invalid ChainId when it is equal to 1', async () => {
29
- // try {
30
- // new Client({ chainId: 1 }, mockSigner)
31
- // } catch (error: any) {
32
- // expect(error.message).toBe('Obol-SDK is in Beta phase, mainnet is not yet supported')
33
- // }
34
- // })
27
+ const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
28
+ const wallet = new ethers.Wallet(privateKey, provider);
29
+ const mockSigner = wallet.connect(provider);
30
+ const clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
35
31
  test('createTermsAndConditions should return "successful authorization"', () => __awaiter(void 0, void 0, void 0, function* () {
36
32
  clientInstance['request'] = jest
37
33
  .fn()
@@ -138,7 +134,7 @@ describe('Cluster Client', () => {
138
134
  describe('Cluster Client without a signer', () => {
139
135
  const clientInstance = new Client({
140
136
  baseUrl: 'https://obol-api-dev.gcp.obol.tech',
141
- chainId: 5,
137
+ chainId: 17000,
142
138
  });
143
139
  test('createClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
144
140
  try {
@@ -177,12 +173,209 @@ describe('Cluster Client without a signer', () => {
177
173
  { version: 'v1.6.0', clusterLock: clusterLockV1X6 },
178
174
  { version: 'v1.7.0', clusterLock: clusterLockV1X7 },
179
175
  { version: 'v1.8.0', clusterLock: clusterLockV1X8 },
176
+ {
177
+ version: 'null deposit_amounts v1.8.0',
178
+ clusterLock: nullDepositAmountsClusterLockV1X8,
179
+ },
180
180
  ])("$version: 'should return true on verified cluster lock'", ({ clusterLock }) => __awaiter(void 0, void 0, void 0, function* () {
181
181
  const isValidLock = yield validateClusterLock(clusterLock);
182
182
  expect(isValidLock).toEqual(true);
183
183
  }));
184
+ test('validateCluster should return false for cluster with null deposit_amounts and incorrect partial_deposits', () => __awaiter(void 0, void 0, void 0, function* () {
185
+ const partialDeposit = nullDepositAmountsClusterLockV1X8.distributed_validators[0]
186
+ .partial_deposit_data[0];
187
+ const isValidLock = yield validateClusterLock(Object.assign(Object.assign({}, nullDepositAmountsClusterLockV1X8), { distributed_validators: [
188
+ Object.assign(Object.assign({}, nullDepositAmountsClusterLockV1X8.distributed_validators[0]), { partial_deposit_data: [partialDeposit, partialDeposit] }),
189
+ ] }));
190
+ expect(isValidLock).toEqual(false);
191
+ }));
184
192
  test('Finds the hash of the latest version of terms and conditions', () => __awaiter(void 0, void 0, void 0, function* () {
185
193
  const termsAndConditionsHash = yield hashTermsAndConditions();
186
194
  expect(termsAndConditionsHash).toEqual('0xd33721644e8f3afab1495a74abe3523cec12d48b8da6cb760972492ca3f1a273');
187
195
  }));
188
196
  });
197
+ describe('createObolRewardsSplit', () => {
198
+ var _a, _b;
199
+ jest
200
+ .spyOn(utils, 'isContractAvailable')
201
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
202
+ jest
203
+ .spyOn(splitsHelpers, 'predictSplitterAddress')
204
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
205
+ jest.spyOn(splitsHelpers, 'handleDeployOWRAndSplitter').mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
206
+ return yield Promise.resolve({
207
+ withdrawal_address: '0xWithdrawalAddress',
208
+ fee_recipient_address: '0xFeeRecipientAddress',
209
+ });
210
+ }));
211
+ const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
212
+ const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
213
+ const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
214
+ const wallet = new ethers.Wallet(privateKey, provider);
215
+ const mockSigner = wallet.connect(provider);
216
+ const clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
217
+ const clientInstanceWithourSigner = new Client({
218
+ baseUrl: 'https://obol-api-dev.gcp.obol.tech',
219
+ chainId: 17000,
220
+ });
221
+ const mockSplitRecipients = [
222
+ {
223
+ account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
224
+ percentAllocation: 99,
225
+ },
226
+ ];
227
+ const mockPrincipalRecipient = '0x86B8145c98e5BD25BA722645b15eD65f024a87EC';
228
+ const mockEtherAmount = 64;
229
+ it('should throw an error if signer is not defined', () => __awaiter(void 0, void 0, void 0, function* () {
230
+ yield expect(clientInstanceWithourSigner.createObolRewardsSplit({
231
+ splitRecipients: mockSplitRecipients,
232
+ principalRecipient: mockPrincipalRecipient,
233
+ etherAmount: mockEtherAmount,
234
+ })).rejects.toThrow('Signer is required in createObolRewardsSplit');
235
+ }));
236
+ it('should throw an error if chainId is not supported', () => __awaiter(void 0, void 0, void 0, function* () {
237
+ const unsupportedSplitterChainClient = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 100 }, mockSigner);
238
+ try {
239
+ yield unsupportedSplitterChainClient.createObolRewardsSplit({
240
+ splitRecipients: mockSplitRecipients,
241
+ principalRecipient: mockPrincipalRecipient,
242
+ etherAmount: mockEtherAmount,
243
+ });
244
+ }
245
+ catch (error) {
246
+ expect(error.message).toEqual('Splitter configuration is not supported on 100 chain');
247
+ }
248
+ }));
249
+ test('should throw an error on invalid recipients', () => __awaiter(void 0, void 0, void 0, function* () {
250
+ try {
251
+ yield clientInstance.createObolRewardsSplit({
252
+ splitRecipients: [
253
+ {
254
+ account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
255
+ percentAllocation: 22,
256
+ },
257
+ ],
258
+ principalRecipient: mockPrincipalRecipient,
259
+ etherAmount: mockEtherAmount,
260
+ });
261
+ }
262
+ catch (error) {
263
+ expect(error.message).toEqual('Schema compilation errors\', must pass "validateSplitRecipients" keyword validation');
264
+ }
265
+ }));
266
+ test('should throw an error if ObolRAFSplit is less than 1', () => __awaiter(void 0, void 0, void 0, function* () {
267
+ try {
268
+ yield clientInstance.createObolRewardsSplit({
269
+ splitRecipients: mockSplitRecipients,
270
+ principalRecipient: mockPrincipalRecipient,
271
+ etherAmount: mockEtherAmount,
272
+ ObolRAFSplit: 0.5,
273
+ });
274
+ }
275
+ catch (error) {
276
+ expect(error.message).toEqual("Schema compilation errors', must be >= 1");
277
+ }
278
+ }));
279
+ it('should return the correct withdrawal and fee recipient addresses', () => __awaiter(void 0, void 0, void 0, function* () {
280
+ const result = yield clientInstance.createObolRewardsSplit({
281
+ splitRecipients: mockSplitRecipients,
282
+ principalRecipient: mockPrincipalRecipient,
283
+ etherAmount: mockEtherAmount,
284
+ });
285
+ expect(result).toEqual({
286
+ withdrawal_address: '0xWithdrawalAddress',
287
+ fee_recipient_address: '0xFeeRecipientAddress',
288
+ });
289
+ }));
290
+ });
291
+ describe('createObolTotalSplit', () => {
292
+ var _a, _b;
293
+ jest
294
+ .spyOn(utils, 'isContractAvailable')
295
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
296
+ jest
297
+ .spyOn(splitsHelpers, 'predictSplitterAddress')
298
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
299
+ jest
300
+ .spyOn(splitsHelpers, 'deploySplitterContract')
301
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xSplitterAddress'); }));
302
+ const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
303
+ const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
304
+ const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
305
+ const wallet = new ethers.Wallet(privateKey, provider);
306
+ const mockSigner = wallet.connect(provider);
307
+ const clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
308
+ const clientInstanceWithourSigner = new Client({
309
+ baseUrl: 'https://obol-api-dev.gcp.obol.tech',
310
+ chainId: 17000,
311
+ });
312
+ const mockSplitRecipients = [
313
+ {
314
+ account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
315
+ percentAllocation: 99.9,
316
+ },
317
+ ];
318
+ it('should throw an error if signer is not defined', () => __awaiter(void 0, void 0, void 0, function* () {
319
+ yield expect(clientInstanceWithourSigner.createObolTotalSplit({
320
+ splitRecipients: mockSplitRecipients,
321
+ })).rejects.toThrow('Signer is required in createObolTotalSplit');
322
+ }));
323
+ it('should throw an error if chainId is not supported', () => __awaiter(void 0, void 0, void 0, function* () {
324
+ const unsupportedSplitterChainClient = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 100 }, mockSigner);
325
+ try {
326
+ yield unsupportedSplitterChainClient.createObolTotalSplit({
327
+ splitRecipients: mockSplitRecipients,
328
+ });
329
+ }
330
+ catch (error) {
331
+ expect(error.message).toEqual('Splitter configuration is not supported on 100 chain');
332
+ }
333
+ }));
334
+ test('should throw an error on invalid recipients', () => __awaiter(void 0, void 0, void 0, function* () {
335
+ try {
336
+ yield clientInstance.createObolTotalSplit({
337
+ splitRecipients: [
338
+ {
339
+ account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
340
+ percentAllocation: 22,
341
+ },
342
+ ],
343
+ });
344
+ }
345
+ catch (error) {
346
+ expect(error.message).toEqual('Schema compilation errors\', must pass "validateSplitRecipients" keyword validation');
347
+ }
348
+ }));
349
+ test('should throw an error if ObolRAFSplit is less than 0.1', () => __awaiter(void 0, void 0, void 0, function* () {
350
+ try {
351
+ yield clientInstance.createObolTotalSplit({
352
+ splitRecipients: mockSplitRecipients,
353
+ ObolRAFSplit: 0.05,
354
+ });
355
+ }
356
+ catch (error) {
357
+ expect(error.message).toEqual("Schema compilation errors', must be >= 0.1");
358
+ }
359
+ }));
360
+ it('should return the correct withdrawal and fee recipient addresses and ObolRAFSplit', () => __awaiter(void 0, void 0, void 0, function* () {
361
+ const result = yield clientInstance.createObolTotalSplit({
362
+ splitRecipients: mockSplitRecipients,
363
+ ObolRAFSplit: 0.5,
364
+ });
365
+ // 0xPredictedAddress and not 0xSplitterAddress since were mocking isContractAvailable response to be true
366
+ expect(result).toEqual({
367
+ withdrawal_address: '0xPredictedAddress',
368
+ fee_recipient_address: '0xPredictedAddress',
369
+ });
370
+ }));
371
+ it('should return the correct withdrawal and fee recipient addresses without passing ObolRAFSplit', () => __awaiter(void 0, void 0, void 0, function* () {
372
+ const result = yield clientInstance.createObolTotalSplit({
373
+ splitRecipients: mockSplitRecipients,
374
+ });
375
+ // 0xPredictedAddress and not 0xSplitterAddress since were mocking isContractAvailable response to be true
376
+ expect(result).toEqual({
377
+ withdrawal_address: '0xPredictedAddress',
378
+ fee_recipient_address: '0xPredictedAddress',
379
+ });
380
+ }));
381
+ });
@@ -0,0 +1,35 @@
1
+ export declare const MultiCallContract: {
2
+ abi: ({
3
+ constant: boolean;
4
+ inputs: {
5
+ components: {
6
+ name: string;
7
+ type: string;
8
+ }[];
9
+ name: string;
10
+ type: string;
11
+ }[];
12
+ name: string;
13
+ outputs: {
14
+ name: string;
15
+ type: string;
16
+ }[];
17
+ payable: boolean;
18
+ stateMutability: string;
19
+ type: string;
20
+ } | {
21
+ constant: boolean;
22
+ inputs: {
23
+ name: string;
24
+ type: string;
25
+ }[];
26
+ name: string;
27
+ outputs: {
28
+ name: string;
29
+ type: string;
30
+ }[];
31
+ payable: boolean;
32
+ stateMutability: string;
33
+ type: string;
34
+ })[];
35
+ };