@obolnetwork/obol-sdk 2.1.2 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obolnetwork/obol-sdk",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
4
4
  "description": "A package for creating Distributed Validators using the Obol API.",
5
5
  "bugs": {
6
6
  "url": "https://github.com/obolnetwork/obol-sdk/issues"
@@ -44,9 +44,12 @@
44
44
  "@chainsafe/discv5": "^0.5.1",
45
45
  "@chainsafe/ssz": "^0.14.0",
46
46
  "@metamask/eth-sig-util": "^7.0.1",
47
+ "@safe-global/protocol-kit": "4.1.1",
48
+ "@safe-global/safe-core-sdk-types": "5.1.0",
47
49
  "@types/pdf-parse": "^1.1.4",
48
50
  "ajv": "^8.12.0",
49
51
  "cross-fetch": "^3.1.5",
52
+ "dotenv": "^16.4.7",
50
53
  "elliptic": "^6.5.4",
51
54
  "eslint-config-standard-with-typescript": "^43.0.1",
52
55
  "eslint-plugin-import": "^2.29.1",
@@ -23,10 +23,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.OBOL_SDK_EMAIL = exports.DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = exports.DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = exports.CHAIN_CONFIGURATION = exports.AVAILABLE_SPLITTER_CHAINS = exports.TERMS_AND_CONDITIONS_HASH = exports.TERMS_AND_CONDITIONS_URL = exports.TERMS_AND_CONDITIONS_VERSION = exports.ETHER_TO_GWEI = exports.DEFAULT_CHAIN_ID = exports.DEFAULT_BASE_VERSION = exports.DEFAULT_BASE_URL = exports.DefinitionFlow = exports.GENESIS_VALIDATOR_ROOT = exports.DOMAIN_DEPOSIT = exports.DOMAIN_APPLICATION_BUILDER = exports.SDK_VERSION = exports.CONFIG_VERSION = exports.DKG_ALGORITHM = exports.signEnrPayload = exports.signOperatorConfigHashPayload = exports.signCreatorConfigHashPayload = exports.ENRTypedMessage = exports.OperatorTypedMessage = exports.OperatorConfigHashSigningTypes = exports.EnrSigningTypes = exports.CreatorTypedMessage = exports.Domain = exports.TermsAndConditionsSigningTypes = exports.CreatorConfigHashSigningTypes = exports.EIP712_DOMAIN_VERSION = exports.EIP712_DOMAIN_NAME = exports.CONFLICT_ERROR_MSG = void 0;
26
+ exports.PROVIDER_MAP = exports.OBOL_SDK_EMAIL = exports.DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = exports.DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = exports.CHAIN_CONFIGURATION = exports.AVAILABLE_SPLITTER_CHAINS = exports.TERMS_AND_CONDITIONS_HASH = exports.TERMS_AND_CONDITIONS_URL = exports.TERMS_AND_CONDITIONS_VERSION = exports.ETHER_TO_GWEI = exports.DEFAULT_CHAIN_ID = exports.DEFAULT_BASE_VERSION = exports.DEFAULT_BASE_URL = exports.DefinitionFlow = exports.GENESIS_VALIDATOR_ROOT = exports.DOMAIN_DEPOSIT = exports.DOMAIN_APPLICATION_BUILDER = exports.SDK_VERSION = exports.CONFIG_VERSION = exports.DKG_ALGORITHM = exports.signEnrPayload = exports.signOperatorConfigHashPayload = exports.signCreatorConfigHashPayload = exports.ENRTypedMessage = exports.OperatorTypedMessage = exports.OperatorConfigHashSigningTypes = exports.EnrSigningTypes = exports.CreatorTypedMessage = exports.Domain = exports.TermsAndConditionsSigningTypes = exports.CreatorConfigHashSigningTypes = exports.EIP712_DOMAIN_VERSION = exports.EIP712_DOMAIN_NAME = exports.CONFLICT_ERROR_MSG = void 0;
27
27
  const pjson = __importStar(require("../package.json"));
28
28
  const types_1 = require("./types");
29
29
  const bytecodes_1 = require("./bytecodes");
30
+ const dotenv = __importStar(require("dotenv"));
31
+ dotenv.config();
30
32
  exports.CONFLICT_ERROR_MSG = 'Conflict';
31
33
  exports.EIP712_DOMAIN_NAME = 'Obol';
32
34
  exports.EIP712_DOMAIN_VERSION = '1';
@@ -171,3 +173,9 @@ exports.CHAIN_CONFIGURATION = {
171
173
  exports.DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = 1;
172
174
  exports.DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = 0.1;
173
175
  exports.OBOL_SDK_EMAIL = 'sdk@dvlabs.tech';
176
+ exports.PROVIDER_MAP = {
177
+ 1: `${process.env.RPC_MAINNET}`, // Mainnet
178
+ 17000: `${process.env.RPC_HOLESKY}`, // Holesky
179
+ 11155111: `${process.env.RPC_SEPOLIA}`, // Sepolia
180
+ 100: `${process.env.RPC_GNOSIS}`, // Gnosis
181
+ };
@@ -36,6 +36,7 @@ const splitHelpers_js_1 = require("./splitHelpers.js");
36
36
  const utils_js_1 = require("./utils.js");
37
37
  __exportStar(require("./types.js"), exports);
38
38
  __exportStar(require("./services.js"), exports);
39
+ __exportStar(require("./verification/signature-validator.js"), exports);
39
40
  /**
40
41
  * Obol sdk Client can be used for creating, managing and activating distributed validators.
41
42
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FORK_MAPPING = void 0;
3
+ exports.FORK_NAMES = exports.FORK_MAPPING = void 0;
4
4
  /**
5
5
  * Permitted ChainID's
6
6
  */
@@ -14,4 +14,21 @@ var FORK_MAPPING;
14
14
  FORK_MAPPING[FORK_MAPPING["0x00000064"] = 100] = "0x00000064";
15
15
  /** Holesky. */
16
16
  FORK_MAPPING[FORK_MAPPING["0x01017000"] = 17000] = "0x01017000";
17
+ /** Sepolia. */
18
+ FORK_MAPPING[FORK_MAPPING["0x90000069"] = 11155111] = "0x90000069";
17
19
  })(FORK_MAPPING || (exports.FORK_MAPPING = FORK_MAPPING = {}));
20
+ /**
21
+ * Permitted Chain Names
22
+ */
23
+ exports.FORK_NAMES = {
24
+ /** Mainnet. */
25
+ [FORK_MAPPING['0x00000000']]: 'mainnet',
26
+ /** Goerli/Prater. */
27
+ [FORK_MAPPING['0x00001020']]: 'goerli',
28
+ /** Gnosis Chain. */
29
+ [FORK_MAPPING['0x00000064']]: 'gnosis',
30
+ /** Holesky. */
31
+ [FORK_MAPPING['0x01017000']]: 'holesky',
32
+ /** Sepolia. */
33
+ [FORK_MAPPING['0x90000069']]: 'sepolia',
34
+ };
@@ -9,8 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.isContractAvailable = exports.findDeployedBytecode = exports.definitionFlow = exports.strToUint8Array = exports.hexWithout0x = void 0;
12
+ exports.getProvider = exports.isContractAvailable = exports.findDeployedBytecode = exports.definitionFlow = exports.strToUint8Array = exports.hexWithout0x = void 0;
13
+ const ethers_1 = require("ethers");
13
14
  const constants_1 = require("./constants");
15
+ const types_1 = require("./types");
14
16
  const hexWithout0x = (hex) => {
15
17
  return hex.slice(2, hex.length);
16
18
  };
@@ -65,3 +67,11 @@ const isContractAvailable = (contractAddress, provider, bytecode) => __awaiter(v
65
67
  return !!code && code !== '0x' && code !== '0x0';
66
68
  });
67
69
  exports.isContractAvailable = isContractAvailable;
70
+ const getProvider = (chainId) => {
71
+ const rpcUrl = constants_1.PROVIDER_MAP[chainId];
72
+ if (!rpcUrl) {
73
+ throw new Error(`No provider configured for ${types_1.FORK_NAMES[chainId]}`);
74
+ }
75
+ return new ethers_1.ethers.JsonRpcProvider(rpcUrl);
76
+ };
77
+ exports.getProvider = getProvider;
@@ -43,13 +43,12 @@ const types_js_1 = require("../types.js");
43
43
  const semver = __importStar(require("semver"));
44
44
  const v1_6_0_js_1 = require("./v1.6.0.js");
45
45
  const v1_7_0_js_1 = require("./v1.7.0.js");
46
- const ethers_1 = require("ethers");
47
46
  const constants_js_1 = require("../constants.js");
48
- const eth_sig_util_1 = require("@metamask/eth-sig-util");
49
47
  const sszTypes_js_1 = require("./sszTypes.js");
50
48
  const utils_js_1 = require("../utils.js");
51
49
  const discv5_1 = require("@chainsafe/discv5");
52
50
  const v1_8_0_js_1 = require("./v1.8.0.js");
51
+ const signature_validator_js_1 = require("./signature-validator.js");
53
52
  // cluster-definition hash
54
53
  /**
55
54
  * @param cluster The cluster configuration or the cluster definition
@@ -109,62 +108,70 @@ const clusterLockHash = (clusterLock) => {
109
108
  exports.clusterLockHash = clusterLockHash;
110
109
  // Lock verification
111
110
  // cluster-definition signatures verification
112
- const getPOSTConfigHashSigner = (signature, configHash, chainId) => {
111
+ const validatePOSTConfigHashSigner = (address, signature, configHash, chainId) => __awaiter(void 0, void 0, void 0, function* () {
113
112
  try {
114
- const sig = ethers_1.ethers.Signature.from(signature);
115
113
  const data = (0, constants_js_1.signCreatorConfigHashPayload)({ creator_config_hash: configHash }, chainId);
116
- const digest = eth_sig_util_1.TypedDataUtils.eip712Hash(data, eth_sig_util_1.SignTypedDataVersion.V4);
117
- return ethers_1.ethers.recoverAddress(digest, sig).toLowerCase();
114
+ return yield (0, signature_validator_js_1.validateAddressSignature)({
115
+ address,
116
+ token: signature,
117
+ data,
118
+ chainId,
119
+ });
118
120
  }
119
121
  catch (err) {
120
122
  throw err;
121
123
  }
122
- };
123
- const getPUTConfigHashSigner = (signature, configHash, chainId) => {
124
+ });
125
+ const validatePUTConfigHashSigner = (address, signature, configHash, chainId) => __awaiter(void 0, void 0, void 0, function* () {
124
126
  try {
125
- const sig = ethers_1.ethers.Signature.from(signature);
126
127
  const data = (0, constants_js_1.signOperatorConfigHashPayload)({ operator_config_hash: configHash }, chainId);
127
- const digest = eth_sig_util_1.TypedDataUtils.eip712Hash(data, eth_sig_util_1.SignTypedDataVersion.V4);
128
- return ethers_1.ethers.recoverAddress(digest, sig).toLowerCase();
128
+ return yield (0, signature_validator_js_1.validateAddressSignature)({
129
+ address,
130
+ token: signature,
131
+ data,
132
+ chainId,
133
+ });
129
134
  }
130
135
  catch (err) {
131
136
  throw err;
132
137
  }
133
- };
134
- const getEnrSigner = (signature, payload, chainId) => {
138
+ });
139
+ const validateEnrSigner = (address, signature, payload, chainId) => __awaiter(void 0, void 0, void 0, function* () {
135
140
  try {
136
- const sig = ethers_1.ethers.Signature.from(signature);
137
141
  const data = (0, constants_js_1.signEnrPayload)({ enr: payload }, chainId);
138
- const digest = eth_sig_util_1.TypedDataUtils.eip712Hash(data, eth_sig_util_1.SignTypedDataVersion.V4);
139
- return ethers_1.ethers.recoverAddress(digest, sig).toLowerCase();
142
+ return yield (0, signature_validator_js_1.validateAddressSignature)({
143
+ address,
144
+ token: signature,
145
+ data,
146
+ chainId,
147
+ });
140
148
  }
141
149
  catch (err) {
142
150
  throw err;
143
151
  }
144
- };
145
- const verifyDefinitionSignatures = (clusterDefinition, definitionType) => {
152
+ });
153
+ const verifyDefinitionSignatures = (clusterDefinition, definitionType) => __awaiter(void 0, void 0, void 0, function* () {
146
154
  if (definitionType === constants_js_1.DefinitionFlow.Charon) {
147
155
  return true;
148
156
  }
149
157
  else {
150
- const configSigner = getPOSTConfigHashSigner(clusterDefinition.creator.config_signature, clusterDefinition.config_hash, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
151
- if (configSigner !== clusterDefinition.creator.address.toLowerCase()) {
158
+ const isPOSTConfigHashSignerValid = yield validatePOSTConfigHashSigner(clusterDefinition.creator.address, clusterDefinition.creator.config_signature, clusterDefinition.config_hash, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
159
+ if (!isPOSTConfigHashSignerValid) {
152
160
  return false;
153
161
  }
154
162
  if (definitionType === constants_js_1.DefinitionFlow.Solo) {
155
163
  return true;
156
164
  }
157
- return clusterDefinition.operators.every(operator => {
158
- const configSigner = getPUTConfigHashSigner(operator.config_signature, clusterDefinition.config_hash, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
159
- const enrSigner = getEnrSigner(operator.enr_signature, operator.enr, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
160
- if (configSigner !== operator.address.toLowerCase() ||
161
- enrSigner !== operator.address.toLowerCase()) {
165
+ for (const operator of clusterDefinition.operators) {
166
+ const isPUTConfigHashSignerValid = yield validatePUTConfigHashSigner(operator.address, operator.config_signature, clusterDefinition.config_hash, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
167
+ const isENRSignerValid = yield validateEnrSigner(operator.address, operator.enr_signature, operator.enr, types_js_1.FORK_MAPPING[clusterDefinition.fork_version]);
168
+ if (!isPUTConfigHashSignerValid || !isENRSignerValid) {
162
169
  return false;
163
170
  }
164
- return true;
165
- });
171
+ }
172
+ return true;
166
173
  }
167
- };
174
+ });
168
175
  // cluster-lock data verification
169
176
  const computeSigningRoot = (sszObjectRoot, domain) => {
170
177
  const signingRootDefaultValue = sszTypes_js_1.signingRootType.defaultValue();
@@ -295,7 +302,7 @@ const isValidClusterLock = (clusterLock) => __awaiter(void 0, void 0, void 0, fu
295
302
  if (definitionType == null) {
296
303
  return false;
297
304
  }
298
- const isValidDefinitionData = verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
305
+ const isValidDefinitionData = yield verifyDefinitionSignatures(clusterLock.cluster_definition, definitionType);
299
306
  if (!isValidDefinitionData) {
300
307
  return false;
301
308
  }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.validateSmartContractSignature = exports.validateEOASignature = exports.validateAddressSignature = void 0;
16
+ /* eslint-disable @typescript-eslint/restrict-template-expressions */
17
+ const ethers_1 = require("ethers");
18
+ const eth_sig_util_1 = require("@metamask/eth-sig-util");
19
+ const protocol_kit_1 = __importDefault(require("@safe-global/protocol-kit"));
20
+ const constants_1 = require("../constants");
21
+ const utils_1 = require("@safe-global/protocol-kit/dist/src/utils");
22
+ const utils_2 = require("../utils");
23
+ const validateAddressSignature = ({ address, token, data, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
24
+ try {
25
+ const provider = (0, utils_2.getProvider)(chainId);
26
+ if (provider) {
27
+ const contractAddress = yield (0, utils_2.isContractAvailable)(address, provider);
28
+ if (contractAddress) {
29
+ return yield (0, exports.validateSmartContractSignature)({
30
+ token,
31
+ data: data,
32
+ address,
33
+ chainId,
34
+ });
35
+ }
36
+ }
37
+ return (0, exports.validateEOASignature)({ token, data, address });
38
+ }
39
+ catch (error) {
40
+ return (0, exports.validateEOASignature)({ token, data, address });
41
+ }
42
+ });
43
+ exports.validateAddressSignature = validateAddressSignature;
44
+ const validateEOASignature = ({ token, data, address, }) => {
45
+ try {
46
+ const sig = ethers_1.ethers.Signature.from(token);
47
+ const digest = eth_sig_util_1.TypedDataUtils.eip712Hash(data, eth_sig_util_1.SignTypedDataVersion.V4);
48
+ return (ethers_1.ethers.recoverAddress(digest, sig).toLowerCase() ===
49
+ address.toLocaleLowerCase());
50
+ }
51
+ catch (err) {
52
+ console.error(`validate EOA Signature error: ${err}`);
53
+ throw err;
54
+ }
55
+ };
56
+ exports.validateEOASignature = validateEOASignature;
57
+ const validateSmartContractSignature = ({ token, data, address, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
58
+ try {
59
+ const provider = constants_1.PROVIDER_MAP[chainId];
60
+ const protocolKit = yield protocol_kit_1.default.init({
61
+ provider,
62
+ safeAddress: address,
63
+ });
64
+ const messageHash = (0, utils_1.hashTypedData)(data);
65
+ const isValidSignature = yield protocolKit.isValidSignature(messageHash, token);
66
+ return isValidSignature;
67
+ }
68
+ catch (err) {
69
+ throw new Error(`Error validating smart contract signature: ${err.message}`);
70
+ }
71
+ });
72
+ exports.validateSmartContractSignature = validateSmartContractSignature;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.nullDepositAmountsClusterLockV1X8 = exports.clusterLockV1X8 = exports.clusterConfigV1X8 = exports.clusterLockV1X7 = exports.clusterConfigV1X7 = exports.clusterLockV1X6 = exports.enr = void 0;
3
+ exports.clusterLockWithSafe = exports.nullDepositAmountsClusterLockV1X8 = exports.clusterLockV1X8 = exports.clusterConfigV1X8 = 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 = {
@@ -423,3 +423,90 @@ exports.nullDepositAmountsClusterLockV1X8 = {
423
423
  '0xdb6332a4ca9c41592511e7480c835b449fea120430e759735f3c24f1a6d496551a516151d2baae21d0cc9469b28dc1122b672af40ebb0e77045da92e0da6a03500',
424
424
  ],
425
425
  };
426
+ exports.clusterLockWithSafe = {
427
+ cluster_definition: {
428
+ name: 'Rainy cluster',
429
+ creator: {
430
+ address: '0xEF43Ff87070247EC9D28B482D6474318e9DAFc98',
431
+ config_signature: '0x0c4aaafcef722cf770b419d47a21571b3c73275185a1b719e5709168af6327eb2ee392134e660d6e4ea113abd184dd4f2bd8ee09a4ae33f434433f7575873b981c',
432
+ },
433
+ operators: [
434
+ {
435
+ address: '0xe5b709A14859EdF820347D78E587b1634B0ec771',
436
+ enr: 'enr:-HW4QGrhMVQGLtKv_KlubzcVZM9AUsSVgDPtq4k_0q344ZtWJ6tD4riMP5jfWXXdHIklAm_pHdQv0MbTE1DrXmuEM7yAgmlkgnY0iXNlY3AyNTZrMaECa_cfUuFYiuFQp_dUEEicCBdFWNfzm8QhrNhBwey8PBg',
437
+ config_signature: '0xad813cd706d7469b560570ef13b0afdf36d0637e9bc5362ec358c218085d9b231b1377f0078e1ed9c2af99fddb3d7608c03bf8aee6edc931c73d7204a7fdee181b',
438
+ enr_signature: '0x304c022e74c13bfd492219efc52fb133f728afa01fefa9fe1d2540ce7c23ca730dd8248b0ad76630dd120975085d77fba20c050620f1e9583590d003110dbca41c',
439
+ },
440
+ {
441
+ address: '0x83b7CA5be230A50DEE511657CCFF262ba04D0353',
442
+ enr: 'enr:-HW4QNGbmr_0YhIeTUQfljlDGGCnR8RGhtWr_gjMI8a5BwigGur2qFgGZrqSumAvk4gX9ciP2Tu74uOdOZPw6nvTi7aAgmlkgnY0iXNlY3AyNTZrMaEC0nrshQsWtEOphaLrZVTzPFOz8EAJweGbhXDtSsXPiLY',
443
+ config_signature: '0x41dfb02ff13775d53ad3f739151c44952ff98a3283d124b6f69782e7458150e92771e32f965803391818d23ad95c9c7bcba15e896c3a576d1a700a16d8cb29fe1b',
444
+ enr_signature: '0x388460abfdeded56839e4ae1480ea48a70ac44a89963bda24152e4f879a9d22918c3e77733f2ccaed5c2745f503565d651a357c6a50634a597d61e16c384961e1c',
445
+ },
446
+ {
447
+ address: '0x123C1cD7AeE10b525D09b848D4A62596958f7D9C',
448
+ enr: 'enr:-HW4QMpDdYHJR3TlnWsnuIKfbmg2aRMMcAFlhTuuZa0Grd3dMFC0AEPHvHM2cKboULC4RUl2Dz7-LpwL2EJRGhJzxcyAgmlkgnY0iXNlY3AyNTZrMaEC73iR5dcgO_bJ8tvsHLrFglwspGFhaAbiBtGAHAJPD9c',
449
+ config_signature: '0x91d23aefc02263fdf0edd0dcfdf26eee01c4d673b97a91a099fc10dfc01f55fa690eccc809658ea2ed17383df355f80a62bd96df15e40a2e2f740189fa6537f61b',
450
+ enr_signature: '0x1035b284df3b1d60434bfe2d139f1472ed29663e2af719779bb5da35537f202032ab0f7cdc540c724c8e80bedfba247809d741260201fbb5bcbfc7f0813334241b',
451
+ },
452
+ {
453
+ address: '0x169212AE1A834C1E0efd8C827Abdbc2b5557B86C', // safe address
454
+ enr: 'enr:-HW4QPwheIq51G9r9k-6GYrhp9-y-hN2SSowqgQemvPyBu9cOKERqPeqy0JuZ0yKyWiji406eK8fFeHX5fKU-6OI3u6AgmlkgnY0iXNlY3AyNTZrMaECaARWVFt5KNkHPno5YYdFBUrkLU5EmOB3K0Ns9hp-s-s',
455
+ config_signature: '0xf97cc2536afe96e43e07aac140254cea37b0345573b12476f8840e571095d72049aa9e03a099ef2965523f83648c4323cd16ec2ee3f5e6efac177da13bcf6eb21c',
456
+ enr_signature: '0x422f471d54eb19fc52e076bb11fd5c8de895734ed111b66708ef805ae124f87b7519e841e59eccdb75c8328223efd8bb324f3fd7d3048f037419bb83e03caae41b',
457
+ },
458
+ ],
459
+ uuid: '3ac43657-b465-49a8-94aa-25b7f7979c2a',
460
+ version: 'v1.8.0',
461
+ timestamp: '2025-01-08T15:18:28.598Z',
462
+ num_validators: 1,
463
+ threshold: 3,
464
+ validators: [
465
+ {
466
+ fee_recipient_address: '0xe5b709A14859EdF820347D78E587b1634B0ec771',
467
+ withdrawal_address: '0xe5b709A14859EdF820347D78E587b1634B0ec771',
468
+ },
469
+ ],
470
+ dkg_algorithm: 'default',
471
+ fork_version: '0x01017000',
472
+ deposit_amounts: ['32000000000'],
473
+ config_hash: '0x1849d9434a5ce7a7d15e1e030a48740a307c80424608c6618386d78a2e597fb3',
474
+ definition_hash: '0x85dabfcebf9bf850521cdb65ea6083c462d9ac57eb62d9a9eb47761ec10bc80e',
475
+ },
476
+ distributed_validators: [
477
+ {
478
+ distributed_public_key: '0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92',
479
+ public_shares: [
480
+ '0x8d150f921a54fe258bb57f568288de87e392946124df65861afbe52d2a3be6a1fa09d19beb62f879b0f18b2e3b26f69f',
481
+ '0x8fa69ec7c31eeda66007bc3d49a3d5654dbe6a3c0890898ea5913ed263a0efc3f6744064dbde3ee0cc1d2f08db33b4d7',
482
+ '0xb862933b7b7adb33cfb3de1052f7dd9e45dfa30accda01dfc4d3c38716747504aade8f832fa5a256dcaf13f80dbbb38f',
483
+ '0xb5a15d86945c7ff38e0b09158a3264033e6dbaebf2ec9f3426988e6d24e38b3dbd6fe413d3253b308404bb691757fb2e',
484
+ ],
485
+ builder_registration: {
486
+ message: {
487
+ fee_recipient: '0xe5b709a14859edf820347d78e587b1634b0ec771',
488
+ gas_limit: 30000000,
489
+ timestamp: 1696000704,
490
+ pubkey: '0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92',
491
+ },
492
+ signature: '0xa5e4220e7d8e7fd4430590c70a27104473b15c8ea03b2d1571bc86bbbcb443e2b7467f75a7e77b5d9b1a10d5fbc335cf0d59c3b3bc1b6d036b2e22511abcf41373567a15185f011ce0129741f0ae0e9e49759f9c5c0ce62f2a99f30e8c7df996',
493
+ },
494
+ partial_deposit_data: [
495
+ {
496
+ pubkey: '0xb0427b57f3f31da882a1ca5d9980e6164ba4c4d3e2c35fb6699f4e76d88e1aede881e0bdb87c48ac707da282f0c92e92',
497
+ withdrawal_credentials: '0x010000000000000000000000e5b709a14859edf820347d78e587b1634b0ec771',
498
+ amount: '32000000000',
499
+ signature: '0x90aff3cb35c577644cb5fe3036ae8aeb5ac6cc2ce2a33a4332e2e883dc48f8fb71165531862d3035a4b6e3a60215d661021672a59f9406e3bd04b5f88f4f77c5f54a9ff37167807010123cdcc230040e5a485b5eb36704fd276dbf541f7151a8',
500
+ },
501
+ ],
502
+ },
503
+ ],
504
+ signature_aggregate: '0x86fdeae3db45215d5f24fc19645f8f95e91fffd1a1d0a87668d3f27724fa735b85fb15de120106b8463ff1d32823813e0f139ee7cb6d3b026c6b81cc96566819817742a13e24ac44fc6571385d5b0fdbaace7e50e2cfbcd692e36b1ca25436e4',
505
+ lock_hash: '0xe03b0fdf6d6a7d38360b0ceac1f0380b051f710e80d0e166b855ef3148d46733',
506
+ node_signatures: [
507
+ '0x564328bcad4cac498aa51b7e7e041cf264e9c65c95138df1fc00d4bf714bc38419c43b5a32662d5c8b747d646d6e77830b2bfd62fa8402e136147c244acdce0300',
508
+ '0xb0e984efd2874fc4bc97bb9c42dccbc723080cf29a20a3cf0b05867ebe87bc430c29a560936000439fcde610ce91452ced763586d3bcca24fbe4fe83bb39202601',
509
+ '0xf3633a8e59db2e8d253ffe1b284f9edab1c1c1aca6288c3a3b2e9cb4eb7e789e23366fc325274f13b7311b9faf53665c80b5cb1f93810713f7b094cd2bec1d9900',
510
+ '0x4067921a5257efe4ceb103f2129faaa7a502781157b3b54e5efca559c558c2b43b89f30962e87df88fbf62250049a31888fcd62735d54b7553e5dc75c3b6ae0901',
511
+ ],
512
+ };
@@ -43,6 +43,7 @@ const node_1 = require("msw/node");
43
43
  const termsAndConditions_1 = require("../src/verification/termsAndConditions");
44
44
  const utils = __importStar(require("../src/utils"));
45
45
  const splitsHelpers = __importStar(require("../src/splitHelpers"));
46
+ jest.setTimeout(20000);
46
47
  /* eslint no-new: 0 */
47
48
  describe('Cluster Client', () => {
48
49
  var _a, _b;
@@ -161,6 +162,9 @@ describe('Cluster Client without a signer', () => {
161
162
  baseUrl: 'https://obol-api-dev.gcp.obol.tech',
162
163
  chainId: 17000,
163
164
  });
165
+ beforeAll(() => {
166
+ jest.restoreAllMocks();
167
+ });
164
168
  test('createClusterDefinition should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
165
169
  try {
166
170
  yield clientInstance.createClusterDefinition(fixtures_js_1.clusterConfigV1X8);
@@ -202,6 +206,10 @@ describe('Cluster Client without a signer', () => {
202
206
  version: 'null deposit_amounts v1.8.0',
203
207
  clusterLock: fixtures_js_1.nullDepositAmountsClusterLockV1X8,
204
208
  },
209
+ {
210
+ version: 'Cluster with safe address v1.8.0',
211
+ clusterLock: fixtures_js_1.clusterLockWithSafe,
212
+ },
205
213
  ])("$version: 'should return true on verified cluster lock'", ({ clusterLock }) => __awaiter(void 0, void 0, void 0, function* () {
206
214
  const isValidLock = yield (0, index_1.validateClusterLock)(clusterLock);
207
215
  expect(isValidLock).toEqual(true);
@@ -220,37 +228,40 @@ describe('Cluster Client without a signer', () => {
220
228
  }));
221
229
  });
222
230
  describe('createObolRewardsSplit', () => {
223
- var _a, _b;
224
- jest
225
- .spyOn(utils, 'isContractAvailable')
226
- .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
227
- jest
228
- .spyOn(splitsHelpers, 'predictSplitterAddress')
229
- .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
230
- jest.spyOn(splitsHelpers, 'handleDeployOWRAndSplitter').mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
231
- return yield Promise.resolve({
232
- withdrawal_address: '0xWithdrawalAddress',
233
- fee_recipient_address: '0xFeeRecipientAddress',
231
+ let clientInstance, clientInstanceWithourSigner, mockSplitRecipients, mockPrincipalRecipient, mockEtherAmount, mockSigner;
232
+ beforeAll(() => {
233
+ var _a, _b;
234
+ jest
235
+ .spyOn(utils, 'isContractAvailable')
236
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
237
+ jest
238
+ .spyOn(splitsHelpers, 'predictSplitterAddress')
239
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
240
+ jest.spyOn(splitsHelpers, 'handleDeployOWRAndSplitter').mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
241
+ return yield Promise.resolve({
242
+ withdrawal_address: '0xWithdrawalAddress',
243
+ fee_recipient_address: '0xFeeRecipientAddress',
244
+ });
245
+ }));
246
+ const mnemonic = (_b = (_a = ethers_1.ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
247
+ const privateKey = ethers_1.ethers.Wallet.fromPhrase(mnemonic).privateKey;
248
+ const provider = new ethers_1.JsonRpcProvider('https://ethereum-holesky.publicnode.com');
249
+ const wallet = new ethers_1.ethers.Wallet(privateKey, provider);
250
+ mockSigner = wallet.connect(provider);
251
+ clientInstance = new index_1.Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
252
+ clientInstanceWithourSigner = new index_1.Client({
253
+ baseUrl: 'https://obol-api-dev.gcp.obol.tech',
254
+ chainId: 17000,
234
255
  });
235
- }));
236
- const mnemonic = (_b = (_a = ethers_1.ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
237
- const privateKey = ethers_1.ethers.Wallet.fromPhrase(mnemonic).privateKey;
238
- const provider = new ethers_1.JsonRpcProvider('https://ethereum-holesky.publicnode.com');
239
- const wallet = new ethers_1.ethers.Wallet(privateKey, provider);
240
- const mockSigner = wallet.connect(provider);
241
- const clientInstance = new index_1.Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
242
- const clientInstanceWithourSigner = new index_1.Client({
243
- baseUrl: 'https://obol-api-dev.gcp.obol.tech',
244
- chainId: 17000,
256
+ mockSplitRecipients = [
257
+ {
258
+ account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
259
+ percentAllocation: 99,
260
+ },
261
+ ];
262
+ mockPrincipalRecipient = '0x86B8145c98e5BD25BA722645b15eD65f024a87EC';
263
+ mockEtherAmount = 64;
245
264
  });
246
- const mockSplitRecipients = [
247
- {
248
- account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
249
- percentAllocation: 99,
250
- },
251
- ];
252
- const mockPrincipalRecipient = '0x86B8145c98e5BD25BA722645b15eD65f024a87EC';
253
- const mockEtherAmount = 64;
254
265
  it('should throw an error if signer is not defined', () => __awaiter(void 0, void 0, void 0, function* () {
255
266
  yield expect(clientInstanceWithourSigner.createObolRewardsSplit({
256
267
  splitRecipients: mockSplitRecipients,
@@ -314,32 +325,35 @@ describe('createObolRewardsSplit', () => {
314
325
  }));
315
326
  });
316
327
  describe('createObolTotalSplit', () => {
317
- var _a, _b;
318
- jest
319
- .spyOn(utils, 'isContractAvailable')
320
- .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
321
- jest
322
- .spyOn(splitsHelpers, 'predictSplitterAddress')
323
- .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
324
- jest
325
- .spyOn(splitsHelpers, 'deploySplitterContract')
326
- .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xSplitterAddress'); }));
327
- const mnemonic = (_b = (_a = ethers_1.ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
328
- const privateKey = ethers_1.ethers.Wallet.fromPhrase(mnemonic).privateKey;
329
- const provider = new ethers_1.JsonRpcProvider('https://ethereum-holesky.publicnode.com');
330
- const wallet = new ethers_1.ethers.Wallet(privateKey, provider);
331
- const mockSigner = wallet.connect(provider);
332
- const clientInstance = new index_1.Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
333
- const clientInstanceWithourSigner = new index_1.Client({
334
- baseUrl: 'https://obol-api-dev.gcp.obol.tech',
335
- chainId: 17000,
328
+ let clientInstanceWithourSigner, mockSplitRecipients, mockSigner, clientInstance;
329
+ beforeAll(() => {
330
+ var _a, _b;
331
+ jest
332
+ .spyOn(utils, 'isContractAvailable')
333
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
334
+ jest
335
+ .spyOn(splitsHelpers, 'predictSplitterAddress')
336
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xPredictedAddress'); }));
337
+ jest
338
+ .spyOn(splitsHelpers, 'deploySplitterContract')
339
+ .mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xSplitterAddress'); }));
340
+ const mnemonic = (_b = (_a = ethers_1.ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
341
+ const privateKey = ethers_1.ethers.Wallet.fromPhrase(mnemonic).privateKey;
342
+ const provider = new ethers_1.JsonRpcProvider('https://ethereum-holesky.publicnode.com');
343
+ const wallet = new ethers_1.ethers.Wallet(privateKey, provider);
344
+ mockSigner = wallet.connect(provider);
345
+ clientInstance = new index_1.Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
346
+ clientInstanceWithourSigner = new index_1.Client({
347
+ baseUrl: 'https://obol-api-dev.gcp.obol.tech',
348
+ chainId: 17000,
349
+ });
350
+ mockSplitRecipients = [
351
+ {
352
+ account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
353
+ percentAllocation: 99.9,
354
+ },
355
+ ];
336
356
  });
337
- const mockSplitRecipients = [
338
- {
339
- account: '0x86B8145c98e5BD25BA722645b15eD65f024a87EC',
340
- percentAllocation: 99.9,
341
- },
342
- ];
343
357
  it('should throw an error if signer is not defined', () => __awaiter(void 0, void 0, void 0, function* () {
344
358
  yield expect(clientInstanceWithourSigner.createObolTotalSplit({
345
359
  splitRecipients: mockSplitRecipients,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obolnetwork/obol-sdk",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
4
4
  "description": "A package for creating Distributed Validators using the Obol API.",
5
5
  "bugs": {
6
6
  "url": "https://github.com/obolnetwork/obol-sdk/issues"
@@ -44,9 +44,12 @@
44
44
  "@chainsafe/discv5": "^0.5.1",
45
45
  "@chainsafe/ssz": "^0.14.0",
46
46
  "@metamask/eth-sig-util": "^7.0.1",
47
+ "@safe-global/protocol-kit": "4.1.1",
48
+ "@safe-global/safe-core-sdk-types": "5.1.0",
47
49
  "@types/pdf-parse": "^1.1.4",
48
50
  "ajv": "^8.12.0",
49
51
  "cross-fetch": "^3.1.5",
52
+ "dotenv": "^16.4.7",
50
53
  "elliptic": "^6.5.4",
51
54
  "eslint-config-standard-with-typescript": "^43.0.1",
52
55
  "eslint-plugin-import": "^2.29.1",
@@ -1,6 +1,8 @@
1
1
  import * as pjson from '../package.json';
2
2
  import { FORK_MAPPING } from './types';
3
3
  import { HOLESKY_MULTICALL_BYTECODE, HOLESKY_OWR_FACTORY_BYTECODE, HOLESKY_SPLITMAIN_BYTECODE, MAINNET_MULTICALL_BYTECODE, MAINNET_OWR_FACTORY_BYTECODE, MAINNET_SPLITMAIN_BYTECODE, } from './bytecodes';
4
+ import * as dotenv from 'dotenv';
5
+ dotenv.config();
4
6
  export const CONFLICT_ERROR_MSG = 'Conflict';
5
7
  export const EIP712_DOMAIN_NAME = 'Obol';
6
8
  export const EIP712_DOMAIN_VERSION = '1';
@@ -141,3 +143,9 @@ export const CHAIN_CONFIGURATION = {
141
143
  export const DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT = 1;
142
144
  export const DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT = 0.1;
143
145
  export const OBOL_SDK_EMAIL = 'sdk@dvlabs.tech';
146
+ export const PROVIDER_MAP = {
147
+ 1: `${process.env.RPC_MAINNET}`, // Mainnet
148
+ 17000: `${process.env.RPC_HOLESKY}`, // Holesky
149
+ 11155111: `${process.env.RPC_SEPOLIA}`, // Sepolia
150
+ 100: `${process.env.RPC_GNOSIS}`, // Gnosis
151
+ };
@@ -19,6 +19,7 @@ import { deploySplitterContract, formatSplitRecipients, handleDeployOWRAndSplitt
19
19
  import { isContractAvailable } from './utils.js';
20
20
  export * from './types.js';
21
21
  export * from './services.js';
22
+ export * from './verification/signature-validator.js';
22
23
  /**
23
24
  * Obol sdk Client can be used for creating, managing and activating distributed validators.
24
25
  */
@@ -11,4 +11,21 @@ export var FORK_MAPPING;
11
11
  FORK_MAPPING[FORK_MAPPING["0x00000064"] = 100] = "0x00000064";
12
12
  /** Holesky. */
13
13
  FORK_MAPPING[FORK_MAPPING["0x01017000"] = 17000] = "0x01017000";
14
+ /** Sepolia. */
15
+ FORK_MAPPING[FORK_MAPPING["0x90000069"] = 11155111] = "0x90000069";
14
16
  })(FORK_MAPPING || (FORK_MAPPING = {}));
17
+ /**
18
+ * Permitted Chain Names
19
+ */
20
+ export const FORK_NAMES = {
21
+ /** Mainnet. */
22
+ [FORK_MAPPING['0x00000000']]: 'mainnet',
23
+ /** Goerli/Prater. */
24
+ [FORK_MAPPING['0x00001020']]: 'goerli',
25
+ /** Gnosis Chain. */
26
+ [FORK_MAPPING['0x00000064']]: 'gnosis',
27
+ /** Holesky. */
28
+ [FORK_MAPPING['0x01017000']]: 'holesky',
29
+ /** Sepolia. */
30
+ [FORK_MAPPING['0x90000069']]: 'sepolia',
31
+ };