@obolnetwork/obol-sdk 2.5.1 → 2.6.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.
- package/dist/cjs/package.json +3 -3
- package/dist/cjs/src/constants.js +13 -1
- package/dist/cjs/src/exits/ethUtils.js +57 -0
- package/dist/cjs/src/exits/exit.js +420 -0
- package/dist/cjs/src/exits/verificationHelpers.js +86 -0
- package/dist/cjs/src/{incentiveHelpers.js → incentives/incentiveHelpers.js} +1 -1
- package/dist/cjs/src/{incentives.js → incentives/incentives.js} +3 -3
- package/dist/cjs/src/index.js +22 -4
- package/dist/cjs/src/splits/splitHelpers.js +327 -0
- package/dist/cjs/src/types.js +1 -0
- package/dist/cjs/test/exit/ethUtils.spec.js +83 -0
- package/dist/cjs/test/exit/exit.spec.js +328 -0
- package/dist/cjs/test/exit/verificationHelpers.spec.js +68 -0
- package/dist/cjs/test/{incentives.test.js → incentives/incentives.spec.js} +4 -4
- package/dist/esm/package.json +3 -3
- package/dist/esm/src/constants.js +12 -0
- package/dist/esm/src/exits/ethUtils.js +52 -0
- package/dist/esm/src/exits/exit.js +393 -0
- package/dist/esm/src/exits/verificationHelpers.js +81 -0
- package/dist/esm/src/{incentiveHelpers.js → incentives/incentiveHelpers.js} +1 -1
- package/dist/esm/src/{incentives.js → incentives/incentives.js} +3 -3
- package/dist/esm/src/index.js +20 -3
- package/dist/esm/src/splits/splitHelpers.js +317 -0
- package/dist/esm/src/types.js +1 -0
- package/dist/esm/test/exit/ethUtils.spec.js +81 -0
- package/dist/esm/test/exit/exit.spec.js +303 -0
- package/dist/esm/test/exit/verificationHelpers.spec.js +66 -0
- package/dist/esm/test/{incentives.test.js → incentives/incentives.spec.js} +4 -4
- package/dist/types/src/constants.d.ts +5 -0
- package/dist/types/src/exits/ethUtils.d.ts +13 -0
- package/dist/types/src/exits/exit.d.ts +180 -0
- package/dist/types/src/exits/verificationHelpers.d.ts +20 -0
- package/dist/types/src/{incentiveHelpers.d.ts → incentives/incentiveHelpers.d.ts} +1 -1
- package/dist/types/src/{incentives.d.ts → incentives/incentives.d.ts} +1 -1
- package/dist/types/src/index.d.ts +16 -2
- package/dist/types/src/{splitHelpers.d.ts → splits/splitHelpers.d.ts} +1 -1
- package/dist/types/src/types.d.ts +98 -0
- package/dist/types/test/exit/verificationHelpers.spec.d.ts +1 -0
- package/dist/types/test/incentives/incentives.spec.d.ts +1 -0
- package/package.json +3 -3
- package/src/constants.ts +13 -0
- package/src/exits/ethUtils.ts +49 -0
- package/src/exits/exit.ts +564 -0
- package/src/exits/verificationHelpers.ts +110 -0
- package/src/{incentiveHelpers.ts → incentives/incentiveHelpers.ts} +2 -2
- package/src/{incentives.ts → incentives/incentives.ts} +3 -3
- package/src/index.ts +29 -3
- package/src/splits/splitHelpers.ts +557 -0
- package/src/types.ts +123 -0
- package/dist/cjs/src/splitHelpers.js +0 -187
- package/dist/cjs/test/methods.test.js +0 -418
- package/dist/esm/src/splitHelpers.js +0 -177
- package/dist/esm/test/methods.test.js +0 -393
- package/src/splitHelpers.ts +0 -355
- /package/dist/types/test/{incentives.test.d.ts → exit/ethUtils.spec.d.ts} +0 -0
- /package/dist/types/test/{methods.test.d.ts → exit/exit.spec.d.ts} +0 -0
package/dist/cjs/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obolnetwork/obol-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "A package for creating Distributed Validators using the Obol API.",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/obolnetwork/obol-sdk/issues"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json",
|
|
16
16
|
"build:clean": "rm -rf ./dist",
|
|
17
17
|
"build": "npm-run-all build:clean compile",
|
|
18
|
-
"test": "jest
|
|
18
|
+
"test": "jest test/**/*.spec.ts --testPathIgnorePatterns=test/sdk-package/",
|
|
19
19
|
"generate-typedoc": "typedoc",
|
|
20
20
|
"npm:publish": "npm publish --tag latest",
|
|
21
21
|
"release": "release-it",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"^bn.js$": "<rootDir>/node_modules/bn.js",
|
|
131
131
|
"^asn1.js$": "<rootDir>/node_modules/asn1.js",
|
|
132
132
|
"^hash.js$": "<rootDir>/node_modules/hash.js",
|
|
133
|
-
"(
|
|
133
|
+
"^(\\.\\.?\\/.+)\\.js$": "$1"
|
|
134
134
|
},
|
|
135
135
|
"extensionsToTreatAsEsm": [
|
|
136
136
|
".ts"
|
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
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;
|
|
29
|
+
exports.CAPELLA_FORK_MAPPING = 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;
|
|
30
30
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
31
31
|
const types_1 = require("./types");
|
|
32
32
|
const bytecodes_1 = require("./bytecodes");
|
|
@@ -182,3 +182,15 @@ exports.PROVIDER_MAP = {
|
|
|
182
182
|
11155111: `${process.env.RPC_SEPOLIA}`, // Sepolia
|
|
183
183
|
100: `${process.env.RPC_GNOSIS}`, // Gnosis
|
|
184
184
|
};
|
|
185
|
+
/**
|
|
186
|
+
* Maps base fork versions to their corresponding Capella fork versions.
|
|
187
|
+
* Example: Mainnet Capella fork version.
|
|
188
|
+
*/
|
|
189
|
+
exports.CAPELLA_FORK_MAPPING = {
|
|
190
|
+
'0x00000000': '0x03000000', // Mainnet
|
|
191
|
+
'0x00001020': '0x03001020', // Goerli
|
|
192
|
+
'0x00000064': '0x03000064', // Gnosis
|
|
193
|
+
'0x90000069': '0x90000072', // Sepolia
|
|
194
|
+
'0x01017000': '0x04017000', // Holesky
|
|
195
|
+
'0x10000910': '0x40000910', // Hoodi
|
|
196
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getGenesisValidatorsRoot = exports.getCapellaFork = void 0;
|
|
13
|
+
const constants_1 = require("../constants");
|
|
14
|
+
/**
|
|
15
|
+
* Retrieves the Capella fork version for a given base fork version.
|
|
16
|
+
* @param fork_version - The base fork version string (e.g., '0x00000000' for mainnet).
|
|
17
|
+
* @returns A promise that resolves to the Capella fork version string, or null if not found.
|
|
18
|
+
*/
|
|
19
|
+
function getCapellaFork(fork_version) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
// Ensure the CAPELLA_FORK_MAPPING uses the base fork_version as key
|
|
22
|
+
if (constants_1.CAPELLA_FORK_MAPPING[fork_version]) {
|
|
23
|
+
return constants_1.CAPELLA_FORK_MAPPING[fork_version];
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
exports.getCapellaFork = getCapellaFork;
|
|
29
|
+
/**
|
|
30
|
+
* Fetches the genesis validators root from a beacon node.
|
|
31
|
+
* @param beaconNodeApiUrl - The base URL of the beacon node API (e.g., http://localhost:5052).
|
|
32
|
+
* @returns A promise that resolves to the genesis_validators_root string, or null on error.
|
|
33
|
+
* @throws Will throw an error if the network corresponding to the fork_version is not supported or if the HTTP request fails.
|
|
34
|
+
*/
|
|
35
|
+
function getGenesisValidatorsRoot(beaconNodeApiUrl) {
|
|
36
|
+
var _a;
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
const genesisEndpoint = `${beaconNodeApiUrl}/eth/v1/beacon/genesis`;
|
|
39
|
+
try {
|
|
40
|
+
const response = yield fetch(genesisEndpoint, {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
});
|
|
43
|
+
const json = yield response.json();
|
|
44
|
+
if ((_a = json.data) === null || _a === void 0 ? void 0 : _a.genesis_validators_root) {
|
|
45
|
+
return json.data.genesis_validators_root;
|
|
46
|
+
}
|
|
47
|
+
console.error('Invalid response structure from genesis endpoint', json);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.error(`Error fetching genesis validators root from ${genesisEndpoint}:`, e);
|
|
52
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
53
|
+
throw new Error(`Failed to fetch genesis validators root: ${errorMessage}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
exports.getGenesisValidatorsRoot = getGenesisValidatorsRoot;
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.Exit = void 0;
|
|
36
|
+
const discv5_1 = require("@chainsafe/discv5");
|
|
37
|
+
const elliptic = __importStar(require("elliptic"));
|
|
38
|
+
const bls_1 = require("@chainsafe/bls");
|
|
39
|
+
const ssz_1 = require("@chainsafe/ssz");
|
|
40
|
+
const ethUtils_1 = require("./ethUtils");
|
|
41
|
+
const verificationHelpers_1 = require("./verificationHelpers");
|
|
42
|
+
// Constants from obol-api/src/verification/exit.ts (assuming these might be needed or were in the original context)
|
|
43
|
+
const DOMAIN_VOLUNTARY_EXIT = '0x04000000';
|
|
44
|
+
// SSZ Type Definitions (adapted from obol-api/src/verification/exit.ts)
|
|
45
|
+
const SSZExitMessageType = new ssz_1.ContainerType({
|
|
46
|
+
epoch: new ssz_1.UintNumberType(8),
|
|
47
|
+
validator_index: new ssz_1.UintNumberType(8),
|
|
48
|
+
});
|
|
49
|
+
const SSZPartialExitsPayloadType = new ssz_1.ContainerType({
|
|
50
|
+
partial_exits: new ssz_1.ListCompositeType(new ssz_1.ContainerType({
|
|
51
|
+
public_key: new ssz_1.ByteVectorType(48),
|
|
52
|
+
signed_exit_message: new ssz_1.ContainerType({
|
|
53
|
+
message: new ssz_1.ContainerType({
|
|
54
|
+
epoch: new ssz_1.UintNumberType(8),
|
|
55
|
+
validator_index: new ssz_1.UintNumberType(8),
|
|
56
|
+
}),
|
|
57
|
+
signature: new ssz_1.ByteVectorType(96),
|
|
58
|
+
}),
|
|
59
|
+
}), 65536),
|
|
60
|
+
share_idx: new ssz_1.UintNumberType(8),
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Exit validation and verification class for Obol distributed validators.
|
|
64
|
+
*
|
|
65
|
+
* This class provides functionality to validate and verify voluntary exit signatures
|
|
66
|
+
* for distributed validators in an Obol cluster. It handles both partial exit signatures
|
|
67
|
+
* from individual operators and payload signatures that authorize exit operations.
|
|
68
|
+
*
|
|
69
|
+
* The class supports:
|
|
70
|
+
* - Verification of BLS signatures for partial exit messages
|
|
71
|
+
* - Verification of ECDSA signatures for exit payload authorization
|
|
72
|
+
* - Validation of exit blobs against cluster configuration
|
|
73
|
+
* - Duplicate detection and epoch validation
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const exit = new Exit(1, provider); // Mainnet with provider
|
|
78
|
+
*
|
|
79
|
+
* // Verify a partial exit signature
|
|
80
|
+
* const isValid = await exit.verifyPartialExitSignature(
|
|
81
|
+
* publicShareKey,
|
|
82
|
+
* signedExitMessage,
|
|
83
|
+
* forkVersion,
|
|
84
|
+
* genesisValidatorsRoot
|
|
85
|
+
* );
|
|
86
|
+
*
|
|
87
|
+
* // Validate exit blobs for a cluster
|
|
88
|
+
* const validBlobs = await exit.validateExitBlobs(
|
|
89
|
+
* clusterConfig,
|
|
90
|
+
* exitsPayload,
|
|
91
|
+
* beaconNodeApiUrl,
|
|
92
|
+
* existingBlobData
|
|
93
|
+
* );
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
class Exit {
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new Exit instance for validator exit operations.
|
|
99
|
+
*
|
|
100
|
+
* @param chainId - The Ethereum chain ID (e.g., 1 for mainnet, 5 for goerli)
|
|
101
|
+
* @param provider - Optional Ethereum provider for blockchain interactions
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // For mainnet with a provider
|
|
106
|
+
* const exit = new Exit(1, provider);
|
|
107
|
+
*
|
|
108
|
+
* // For goerli testnet without provider
|
|
109
|
+
* const exit = new Exit(5, null);
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
constructor(chainId, provider) {
|
|
113
|
+
this.chainId = chainId;
|
|
114
|
+
this.provider = provider;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Safely parse a string integer to number, using BigInt to avoid precision loss
|
|
118
|
+
* for large values beyond JavaScript's safe integer limit (2^53 - 1).
|
|
119
|
+
* Throws an error if the value exceeds the safe range for a 64-bit unsigned integer.
|
|
120
|
+
*/
|
|
121
|
+
static safeParseInt(value) {
|
|
122
|
+
const bigIntValue = BigInt(value);
|
|
123
|
+
// Check if value is within the range of a 64-bit unsigned integer
|
|
124
|
+
const MAX_UINT64 = BigInt('0xFFFFFFFFFFFFFFFF');
|
|
125
|
+
if (bigIntValue < 0 || bigIntValue > MAX_UINT64) {
|
|
126
|
+
throw new Error(`Value ${value} is outside the valid range for a 64-bit unsigned integer`);
|
|
127
|
+
}
|
|
128
|
+
// Convert to number - SSZ library should handle values even if they exceed JS safe integer limits
|
|
129
|
+
return Number(bigIntValue);
|
|
130
|
+
}
|
|
131
|
+
static computePartialExitMessageRoot(msg) {
|
|
132
|
+
const sszValue = SSZExitMessageType.defaultValue();
|
|
133
|
+
sszValue.epoch = Exit.safeParseInt(msg.epoch);
|
|
134
|
+
sszValue.validator_index = Exit.safeParseInt(msg.validator_index);
|
|
135
|
+
return Buffer.from(SSZExitMessageType.hashTreeRoot(sszValue).buffer);
|
|
136
|
+
}
|
|
137
|
+
static computeExitPayloadRoot(exits) {
|
|
138
|
+
// Remove sorting since SSZ list ordering guarantees order
|
|
139
|
+
// This eliminates the O(n log n) sort and improves performance
|
|
140
|
+
const sszValue = SSZPartialExitsPayloadType.defaultValue();
|
|
141
|
+
sszValue.partial_exits = exits.partial_exits.map(pe => ({
|
|
142
|
+
public_key: (0, ssz_1.fromHexString)(pe.public_key),
|
|
143
|
+
signed_exit_message: {
|
|
144
|
+
message: {
|
|
145
|
+
epoch: Exit.safeParseInt(pe.signed_exit_message.message.epoch),
|
|
146
|
+
validator_index: Exit.safeParseInt(pe.signed_exit_message.message.validator_index),
|
|
147
|
+
},
|
|
148
|
+
signature: (0, ssz_1.fromHexString)(pe.signed_exit_message.signature),
|
|
149
|
+
},
|
|
150
|
+
}));
|
|
151
|
+
sszValue.share_idx = exits.share_idx;
|
|
152
|
+
return Buffer.from(SSZPartialExitsPayloadType.hashTreeRoot(sszValue).buffer).toString('hex');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Verifies a partial exit signature from a distributed validator operator.
|
|
156
|
+
*
|
|
157
|
+
* This method validates that a partial exit signature was correctly signed by the
|
|
158
|
+
* operator's share of the distributed validator's private key. It performs BLS
|
|
159
|
+
* signature verification using the appropriate fork version and genesis validators root.
|
|
160
|
+
*
|
|
161
|
+
* @param publicShareKey - The operator's public share key (BLS public key, hex string with or without 0x prefix)
|
|
162
|
+
* @param signedExitMessage - The signed exit message containing the exit details and signature
|
|
163
|
+
* @param forkVersion - The Ethereum fork version (e.g., "0x00000000" for mainnet)
|
|
164
|
+
* @param genesisValidatorsRootString - The genesis validators root for the network (hex string)
|
|
165
|
+
*
|
|
166
|
+
* @returns Promise resolving to true if the signature is valid, false otherwise
|
|
167
|
+
*
|
|
168
|
+
* @throws {Error} When unable to determine the Capella fork version for the given network
|
|
169
|
+
* @throws {Error} When BLS library initialization or verification fails
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const isValid = await exit.verifyPartialExitSignature(
|
|
174
|
+
* "0x1234...abcd", // operator's public share key
|
|
175
|
+
* {
|
|
176
|
+
* message: { epoch: "12345", validator_index: "67890" },
|
|
177
|
+
* signature: "0xabcd...1234"
|
|
178
|
+
* },
|
|
179
|
+
* "0x00000000", // mainnet fork version
|
|
180
|
+
* "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"
|
|
181
|
+
* );
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
verifyPartialExitSignature(publicShareKey, signedExitMessage, forkVersion, genesisValidatorsRootString) {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
yield (0, bls_1.init)('herumi');
|
|
187
|
+
const capellaForkVersionString = yield (0, ethUtils_1.getCapellaFork)(forkVersion);
|
|
188
|
+
if (!capellaForkVersionString) {
|
|
189
|
+
throw new Error(`Could not determine Capella fork version for base fork: ${forkVersion}`);
|
|
190
|
+
}
|
|
191
|
+
const partialExitMessageBuffer = Exit.computePartialExitMessageRoot(signedExitMessage.message);
|
|
192
|
+
const exitDomain = (0, verificationHelpers_1.computeDomain)((0, ssz_1.fromHexString)(DOMAIN_VOLUNTARY_EXIT), capellaForkVersionString, (0, ssz_1.fromHexString)(genesisValidatorsRootString));
|
|
193
|
+
const messageSigningRoot = (0, verificationHelpers_1.signingRoot)(exitDomain, partialExitMessageBuffer);
|
|
194
|
+
return (0, bls_1.verify)((0, ssz_1.fromHexString)(publicShareKey), messageSigningRoot, (0, ssz_1.fromHexString)(signedExitMessage.signature));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Verifies the exit payload signature using the operator's ENR.
|
|
199
|
+
*
|
|
200
|
+
* This method validates that an exit payload was signed by the correct operator
|
|
201
|
+
* using ECDSA signature verification. The signature is verified against the
|
|
202
|
+
* operator's public key extracted from their ENR (Ethereum Node Record).
|
|
203
|
+
*
|
|
204
|
+
* @param enrString - The operator's ENR string containing their identity and public key
|
|
205
|
+
* @param exitsPayload - The exit validation payload containing partial exits and operator signature
|
|
206
|
+
*
|
|
207
|
+
* @returns Promise resolving to true if the payload signature is valid, false otherwise
|
|
208
|
+
*
|
|
209
|
+
* @throws {Error} When the ENR string is invalid or cannot be decoded
|
|
210
|
+
* @throws {Error} When the signature format is invalid (must be 130 hex characters)
|
|
211
|
+
* @throws {Error} When signature verification encounters an error
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* const isValid = await exit.verifyExitPayloadSignature(
|
|
216
|
+
* "enr:-LK4QFo_n0dUm4PKejSOXf8JkSWq5EINV0XhG1zY00d...", // operator ENR
|
|
217
|
+
* {
|
|
218
|
+
* partial_exits: [exitBlob1, exitBlob2],
|
|
219
|
+
* share_idx: 1,
|
|
220
|
+
* signature: "0x1234...abcd" // ECDSA signature (130 hex chars)
|
|
221
|
+
* }
|
|
222
|
+
* );
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
verifyExitPayloadSignature(enrString, exitsPayload) {
|
|
226
|
+
var _a, _b;
|
|
227
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
const partialExitsDtoHashRoot = Exit.computeExitPayloadRoot(exitsPayload);
|
|
229
|
+
const ec = new elliptic.ec('secp256k1');
|
|
230
|
+
let pubKeyHex;
|
|
231
|
+
try {
|
|
232
|
+
pubKeyHex = discv5_1.ENR.decodeTxt(enrString).publicKey.toString('hex');
|
|
233
|
+
}
|
|
234
|
+
catch (e) {
|
|
235
|
+
throw new Error(`Invalid ENR string: ${enrString}. Error: ${(_a = e.message) !== null && _a !== void 0 ? _a : String(e)}`);
|
|
236
|
+
}
|
|
237
|
+
const sigHex = exitsPayload.signature.startsWith('0x')
|
|
238
|
+
? exitsPayload.signature.substring(2)
|
|
239
|
+
: exitsPayload.signature;
|
|
240
|
+
if (sigHex.length !== 130) {
|
|
241
|
+
throw new Error(`Invalid signature length. Expected 130 hex chars (r + s), got ${sigHex.length}`);
|
|
242
|
+
}
|
|
243
|
+
const r = sigHex.slice(0, 64);
|
|
244
|
+
const s = sigHex.slice(64, 128);
|
|
245
|
+
const enrSignature = { r, s };
|
|
246
|
+
try {
|
|
247
|
+
return ec
|
|
248
|
+
.keyFromPublic(pubKeyHex, 'hex')
|
|
249
|
+
.verify(partialExitsDtoHashRoot, enrSignature);
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
throw new Error(`Signature verification failed: ${(_b = e.message) !== null && _b !== void 0 ? _b : String(e)}`);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
validateOperatorAndPayload(clusterConfig, exitsPayload) {
|
|
257
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
258
|
+
const operatorIndex = exitsPayload.share_idx - 1;
|
|
259
|
+
if (operatorIndex < 0 ||
|
|
260
|
+
operatorIndex >= clusterConfig.definition.operators.length) {
|
|
261
|
+
throw new Error(`Invalid share_idx ${exitsPayload.share_idx} for ${clusterConfig.definition.operators.length} operators.`);
|
|
262
|
+
}
|
|
263
|
+
const operatorEnr = clusterConfig.definition.operators[operatorIndex].enr;
|
|
264
|
+
const isPayloadSignatureValid = yield this.verifyExitPayloadSignature(operatorEnr, exitsPayload);
|
|
265
|
+
if (!isPayloadSignatureValid) {
|
|
266
|
+
throw new Error('Incorrect payload signature for partial exits.');
|
|
267
|
+
}
|
|
268
|
+
return operatorEnr;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
getNetworkParameters(forkVersion, beaconNodeApiUrl) {
|
|
272
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
273
|
+
const genesisValidatorsRootString = yield (0, ethUtils_1.getGenesisValidatorsRoot)(beaconNodeApiUrl);
|
|
274
|
+
if (!genesisValidatorsRootString) {
|
|
275
|
+
throw new Error('Could not retrieve genesis validators root.');
|
|
276
|
+
}
|
|
277
|
+
const capellaForkVersionString = yield (0, ethUtils_1.getCapellaFork)(forkVersion);
|
|
278
|
+
if (!capellaForkVersionString) {
|
|
279
|
+
throw new Error(`Unsupported network: Could not determine Capella fork for ${forkVersion}`);
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
genesisValidatorsRoot: genesisValidatorsRootString,
|
|
283
|
+
capellaForkVersion: capellaForkVersionString,
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
findValidatorInCluster(clusterConfig, publicKey, operatorIndex) {
|
|
288
|
+
const validatorInCluster = clusterConfig.distributed_validators.find(dv => (dv.distributed_public_key.startsWith('0x')
|
|
289
|
+
? dv.distributed_public_key
|
|
290
|
+
: '0x' + dv.distributed_public_key).toLowerCase() ===
|
|
291
|
+
(publicKey.startsWith('0x')
|
|
292
|
+
? publicKey
|
|
293
|
+
: '0x' + publicKey).toLowerCase());
|
|
294
|
+
if (!validatorInCluster) {
|
|
295
|
+
throw new Error(`Public key ${publicKey} not found in the cluster's distributed validators.`);
|
|
296
|
+
}
|
|
297
|
+
const publicShareForOperator = validatorInCluster.public_shares[operatorIndex];
|
|
298
|
+
if (!publicShareForOperator) {
|
|
299
|
+
throw new Error(`Public share for operator index ${operatorIndex} not found for validator ${publicKey}`);
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
validator: validatorInCluster,
|
|
303
|
+
publicShare: publicShareForOperator,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
validateExistingBlobData(exitBlob, existingBlob, operatorIndex) {
|
|
307
|
+
var _a, _b, _c;
|
|
308
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
309
|
+
if (!existingBlob) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
// Check if existing blob data is for this public key
|
|
313
|
+
const normalizeKey = (key) => (key.startsWith('0x') ? key : '0x' + key).toLowerCase();
|
|
314
|
+
if (normalizeKey(existingBlob.public_key) !==
|
|
315
|
+
normalizeKey(exitBlob.public_key)) {
|
|
316
|
+
return false; // Existing blob data is for a different validator
|
|
317
|
+
}
|
|
318
|
+
if (existingBlob.validator_index !==
|
|
319
|
+
exitBlob.signed_exit_message.message.validator_index) {
|
|
320
|
+
throw new Error(`Validator index mismatch for already processed exit for public key ${exitBlob.public_key}. Expected ${existingBlob.validator_index}, got ${exitBlob.signed_exit_message.message.validator_index}.`);
|
|
321
|
+
}
|
|
322
|
+
const currentEpoch = Exit.safeParseInt(exitBlob.signed_exit_message.message.epoch);
|
|
323
|
+
const existingEpoch = Exit.safeParseInt(existingBlob.epoch);
|
|
324
|
+
if (currentEpoch < existingEpoch) {
|
|
325
|
+
throw new Error(`New exit epoch ${currentEpoch} is not greater than existing exit epoch ${existingEpoch} for validator ${exitBlob.public_key}.`);
|
|
326
|
+
}
|
|
327
|
+
else if (currentEpoch === existingEpoch) {
|
|
328
|
+
const operatorShareIndexString = String(operatorIndex);
|
|
329
|
+
if (((_c = (_b = (_a = existingBlob.shares_exit_data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b[operatorShareIndexString]) === null || _c === void 0 ? void 0 : _c.partial_exit_signature) &&
|
|
330
|
+
existingBlob.shares_exit_data[0][operatorShareIndexString]
|
|
331
|
+
.partial_exit_signature !== exitBlob.signed_exit_message.signature) {
|
|
332
|
+
throw new Error(`Signature mismatch for validator ${exitBlob.public_key}, operator index ${operatorIndex} at epoch ${currentEpoch}. Received different signature than existing.`);
|
|
333
|
+
}
|
|
334
|
+
return true; // Already processed
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
processExitBlob(exitBlob, clusterConfig, operatorIndex, genesisValidatorsRoot, existingBlobData) {
|
|
340
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
341
|
+
const { publicShare } = this.findValidatorInCluster(clusterConfig, exitBlob.public_key, operatorIndex);
|
|
342
|
+
const alreadyProcessed = yield this.validateExistingBlobData(exitBlob, existingBlobData, operatorIndex);
|
|
343
|
+
if (alreadyProcessed) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const isPartialSignatureValid = yield this.verifyPartialExitSignature(publicShare, exitBlob.signed_exit_message, clusterConfig.definition.fork_version, genesisValidatorsRoot);
|
|
347
|
+
if (!isPartialSignatureValid) {
|
|
348
|
+
throw new Error(`Invalid partial exit signature for validator ${exitBlob.public_key} by operator index ${operatorIndex}.`);
|
|
349
|
+
}
|
|
350
|
+
return exitBlob;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Validates exit blobs against cluster configuration and existing data.
|
|
355
|
+
*
|
|
356
|
+
* This method performs comprehensive validation of exit blobs including:
|
|
357
|
+
* - Operator authorization and payload signature verification
|
|
358
|
+
* - Network parameter validation (genesis root, fork version)
|
|
359
|
+
* - Public key validation against cluster configuration
|
|
360
|
+
* - Partial signature verification for each exit blob
|
|
361
|
+
* - Duplicate detection and epoch progression validation
|
|
362
|
+
* - Signature consistency checks for existing exits
|
|
363
|
+
*
|
|
364
|
+
* @param clusterConfig - The cluster configuration containing operators and distributed validators
|
|
365
|
+
* @param exitsPayload - The exit validation payload with partial exits and operator info
|
|
366
|
+
* @param beaconNodeApiUrl - The beacon node API URL for network parameter retrieval
|
|
367
|
+
* @param existingBlobData - Existing exit blob data for duplicate detection, or null if none exists
|
|
368
|
+
*
|
|
369
|
+
* @returns Promise resolving to an array of validated, non-duplicate exit blobs
|
|
370
|
+
*
|
|
371
|
+
* @throws {Error} When share_idx is invalid or out of bounds for the cluster operators
|
|
372
|
+
* @throws {Error} When payload signature verification fails
|
|
373
|
+
* @throws {Error} When network parameters cannot be retrieved or are invalid
|
|
374
|
+
* @throws {Error} When a public key is not found in the cluster's distributed validators
|
|
375
|
+
* @throws {Error} When a partial exit signature is invalid
|
|
376
|
+
* @throws {Error} When exit epoch validation fails (new epoch not greater than existing)
|
|
377
|
+
* @throws {Error} When validator index mismatches with existing data
|
|
378
|
+
* @throws {Error} When signature mismatches for the same epoch and operator
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```typescript
|
|
382
|
+
* const validExitBlobs = await exit.validateExitBlobs(
|
|
383
|
+
* {
|
|
384
|
+
* definition: {
|
|
385
|
+
* operators: [{ enr: "enr:-LK4Q..." }],
|
|
386
|
+
* fork_version: "0x00000000",
|
|
387
|
+
* threshold: 1
|
|
388
|
+
* },
|
|
389
|
+
* distributed_validators: [{
|
|
390
|
+
* distributed_public_key: "0x1234...abcd",
|
|
391
|
+
* public_shares: ["0x5678...efgh"]
|
|
392
|
+
* }]
|
|
393
|
+
* },
|
|
394
|
+
* {
|
|
395
|
+
* partial_exits: [exitBlob],
|
|
396
|
+
* share_idx: 1,
|
|
397
|
+
* signature: "0x1234...abcd"
|
|
398
|
+
* },
|
|
399
|
+
* "http://localhost:5052",
|
|
400
|
+
* existingBlobData // or null for new exits
|
|
401
|
+
* );
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
validateExitBlobs(clusterConfig, exitsPayload, beaconNodeApiUrl, existingBlobData) {
|
|
405
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
406
|
+
yield this.validateOperatorAndPayload(clusterConfig, exitsPayload);
|
|
407
|
+
const { genesisValidatorsRoot } = yield this.getNetworkParameters(clusterConfig.definition.fork_version, beaconNodeApiUrl);
|
|
408
|
+
const operatorIndex = exitsPayload.share_idx - 1;
|
|
409
|
+
const validNonDuplicateBlobs = [];
|
|
410
|
+
for (const currentExitBlob of exitsPayload.partial_exits) {
|
|
411
|
+
const processedBlob = yield this.processExitBlob(currentExitBlob, clusterConfig, operatorIndex, genesisValidatorsRoot, existingBlobData);
|
|
412
|
+
if (processedBlob) {
|
|
413
|
+
validNonDuplicateBlobs.push(processedBlob);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return validNonDuplicateBlobs;
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
exports.Exit = Exit;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.signingRoot = exports.computeDomain = exports.GENESIS_VALIDATOR_ROOT_HEX_STRING = void 0;
|
|
4
|
+
const ssz_1 = require("@chainsafe/ssz");
|
|
5
|
+
// From obol-api/src/verification/common.ts
|
|
6
|
+
exports.GENESIS_VALIDATOR_ROOT_HEX_STRING = '0x0000000000000000000000000000000000000000000000000000000000000000'; // Added 0x prefix
|
|
7
|
+
const ForkDataType = new ssz_1.ContainerType({
|
|
8
|
+
currentVersion: new ssz_1.ByteVectorType(4),
|
|
9
|
+
genesisValidatorsRoot: new ssz_1.ByteVectorType(32),
|
|
10
|
+
});
|
|
11
|
+
const SigningRootType = new ssz_1.ContainerType({
|
|
12
|
+
objectRoot: new ssz_1.ByteVectorType(32),
|
|
13
|
+
domain: new ssz_1.ByteVectorType(32),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_fork_data_root
|
|
17
|
+
* @param currentVersion - Uint8Array of the current fork version.
|
|
18
|
+
* @param genesisValidatorsRoot - Uint8Array of the genesis validators root.
|
|
19
|
+
* @returns Uint8Array - The 32-byte fork data root.
|
|
20
|
+
*/
|
|
21
|
+
function computeForkDataRoot(currentVersion, genesisValidatorsRoot) {
|
|
22
|
+
const forkDataVal = ForkDataType.defaultValue();
|
|
23
|
+
forkDataVal.currentVersion = currentVersion;
|
|
24
|
+
forkDataVal.genesisValidatorsRoot = genesisValidatorsRoot;
|
|
25
|
+
return Buffer.from(ForkDataType.hashTreeRoot(forkDataVal).buffer);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Computes the domain for a given domain type, fork version, and genesis state.
|
|
29
|
+
* https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_domain
|
|
30
|
+
* @param domainType - Uint8Array representing the domain type (e.g., DOMAIN_VOLUNTARY_EXIT).
|
|
31
|
+
* @param forkVersionString - Hex string of the fork version (e.g., Capella fork version).
|
|
32
|
+
* @param genesisValidatorsRootOverride - Optional Uint8Array to override the default genesis validators root.
|
|
33
|
+
* @returns Uint8Array - The 32-byte domain.
|
|
34
|
+
*/
|
|
35
|
+
function computeDomain(domainType, // Should be 4 bytes
|
|
36
|
+
forkVersionString, // Hex string, e.g., '0x03000000'
|
|
37
|
+
genesisValidatorsRootOverride) {
|
|
38
|
+
const forkVersionBytes = (0, ssz_1.fromHexString)(forkVersionString.substring(2, forkVersionString.length));
|
|
39
|
+
if (forkVersionBytes.length !== 4) {
|
|
40
|
+
throw new Error('Fork version must be 4 bytes');
|
|
41
|
+
}
|
|
42
|
+
if (domainType.length !== 4) {
|
|
43
|
+
throw new Error('Domain type must be 4 bytes');
|
|
44
|
+
}
|
|
45
|
+
const actualGenesisValidatorsRoot = genesisValidatorsRootOverride !== null && genesisValidatorsRootOverride !== void 0 ? genesisValidatorsRootOverride : (0, ssz_1.fromHexString)(exports.GENESIS_VALIDATOR_ROOT_HEX_STRING.substring(2));
|
|
46
|
+
if (actualGenesisValidatorsRoot.length !== 32) {
|
|
47
|
+
throw new Error('genesisValidatorsRoot must be 32 bytes');
|
|
48
|
+
}
|
|
49
|
+
const forkDataRoot = computeForkDataRoot(forkVersionBytes, actualGenesisValidatorsRoot);
|
|
50
|
+
const domain = new Uint8Array(32);
|
|
51
|
+
domain.set(domainType);
|
|
52
|
+
domain.set(forkDataRoot.subarray(0, 28), 4); // Set the remaining 28 bytes from forkDataRoot
|
|
53
|
+
return domain;
|
|
54
|
+
}
|
|
55
|
+
exports.computeDomain = computeDomain;
|
|
56
|
+
/**
|
|
57
|
+
* Computes the signing root for an SSZ object and a domain.
|
|
58
|
+
* https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_signing_root
|
|
59
|
+
* @param sszObjectRoot - Uint8Array of the hash tree root of the SSZ object.
|
|
60
|
+
* @param domain - Uint8Array of the domain.
|
|
61
|
+
* @returns Uint8Array - The 32-byte signing root.
|
|
62
|
+
*/
|
|
63
|
+
function computeSigningRoot(sszObjectRoot, // Should be 32 bytes
|
|
64
|
+
domain) {
|
|
65
|
+
if (sszObjectRoot.length !== 32) {
|
|
66
|
+
throw new Error('SSZ object root must be 32 bytes');
|
|
67
|
+
}
|
|
68
|
+
if (domain.length !== 32) {
|
|
69
|
+
throw new Error('Domain must be 32 bytes');
|
|
70
|
+
}
|
|
71
|
+
const val = SigningRootType.defaultValue();
|
|
72
|
+
val.objectRoot = sszObjectRoot;
|
|
73
|
+
val.domain = domain;
|
|
74
|
+
return Buffer.from(SigningRootType.hashTreeRoot(val).buffer);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Convenience wrapper for computeSigningRoot.
|
|
78
|
+
* @param domain - Uint8Array of the domain.
|
|
79
|
+
* @param messageBuffer - Buffer of the message (SSZ object root).
|
|
80
|
+
* @returns Uint8Array - The signing root.
|
|
81
|
+
*/
|
|
82
|
+
function signingRoot(domain, messageBuffer) {
|
|
83
|
+
const sszObjectRootU8 = Uint8Array.from(messageBuffer);
|
|
84
|
+
return computeSigningRoot(sszObjectRootU8, domain);
|
|
85
|
+
}
|
|
86
|
+
exports.signingRoot = signingRoot;
|
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isClaimedFromMerkleDistributor = exports.claimIncentivesFromMerkleDistributor = void 0;
|
|
13
13
|
const ethers_1 = require("ethers");
|
|
14
|
-
const MerkleDistributorWithDeadline_1 = require("
|
|
14
|
+
const MerkleDistributorWithDeadline_1 = require("../abi/MerkleDistributorWithDeadline");
|
|
15
15
|
const claimIncentivesFromMerkleDistributor = (incentivesData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
16
|
try {
|
|
17
17
|
const contract = new ethers_1.Contract(incentivesData.contractAddress, MerkleDistributorWithDeadline_1.MerkleDistributorABI.abi, incentivesData.signer);
|
|
@@ -10,10 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Incentives = void 0;
|
|
13
|
-
const utils_1 = require("
|
|
14
|
-
const types_1 = require("
|
|
13
|
+
const utils_1 = require("../utils");
|
|
14
|
+
const types_1 = require("../types");
|
|
15
15
|
const incentiveHelpers_1 = require("./incentiveHelpers");
|
|
16
|
-
const constants_1 = require("
|
|
16
|
+
const constants_1 = require("../constants");
|
|
17
17
|
/**
|
|
18
18
|
* Incentives can be used for fetching and claiming Obol incentives.
|
|
19
19
|
* @class
|