@obolnetwork/obol-sdk 2.11.8 → 2.11.10

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,35 +1,35 @@
1
1
  import type { ProviderType, ExitClusterConfig, ExitValidationPayload, ExitValidationBlob, SignedExitValidationMessage, ExistingExitValidationBlobData, FullExitBlob } from '../types.js';
2
2
  /**
3
- * Exit validation and verification class for Obol distributed validators.
3
+ * Validates and verifies voluntary exit signatures for Obol distributed validators.
4
4
  *
5
- * This class provides functionality to validate and verify voluntary exit signatures
6
- * for distributed validators in an Obol cluster. It handles both partial exit signatures
7
- * from individual operators and payload signatures that authorize exit operations.
5
+ * Do not instantiate directly; access via `client.exit`.
8
6
  *
9
- * The class supports:
10
- * - Verification of BLS signatures for partial exit messages
11
- * - Verification of ECDSA signatures for exit payload authorization
12
- * - Validation of exit blobs against cluster configuration
13
- * - Duplicate detection and epoch validation
7
+ * Available methods:
8
+ * - {@link Exit.verifyPartialExitSignature} verify a BLS partial exit signature
9
+ * - {@link Exit.verifyExitPayloadSignature} verify an ECDSA exit payload signature
10
+ * - {@link Exit.validateExitBlobs} – validate exit blobs against cluster config
11
+ * - {@link Exit.recombineExitBlobs} aggregate partial signatures into a full exit blob
12
+ *
13
+ * All methods are read-only (no on-chain transactions). No signer is required.
14
14
  *
15
15
  * @example
16
16
  * ```typescript
17
- * const exit = new Exit(1, provider); // Mainnet with provider
17
+ * const client = new Client({ chainId: 1 }, signer, provider);
18
18
  *
19
19
  * // Verify a partial exit signature
20
- * const isValid = await exit.verifyPartialExitSignature(
20
+ * const isValid = await client.exit.verifyPartialExitSignature(
21
21
  * publicShareKey,
22
22
  * signedExitMessage,
23
23
  * forkVersion,
24
- * genesisValidatorsRoot
24
+ * genesisValidatorsRoot,
25
25
  * );
26
26
  *
27
27
  * // Validate exit blobs for a cluster
28
- * const validBlobs = await exit.validateExitBlobs(
28
+ * const validBlobs = await client.exit.validateExitBlobs(
29
29
  * clusterConfig,
30
30
  * exitsPayload,
31
31
  * beaconNodeApiUrl,
32
- * existingBlobData
32
+ * existingBlobData,
33
33
  * );
34
34
  * ```
35
35
  */
@@ -44,11 +44,8 @@ export declare class Exit {
44
44
  *
45
45
  * @example
46
46
  * ```typescript
47
- * // For mainnet with a provider
48
- * const exit = new Exit(1, provider);
49
- *
50
- * // For goerli testnet without provider
51
- * const exit = new Exit(5, null);
47
+ * const client = new Client({ chainId: 1 }, signer, provider);
48
+ * const exit = client.exit; // Access via client, do not instantiate directly
52
49
  * ```
53
50
  */
54
51
  constructor(chainId: number, provider: ProviderType | undefined | null);
@@ -1,11 +1,23 @@
1
1
  import { type ClaimableIncentives, type ETH_ADDRESS, type ProviderType, type SignerType, type ClaimIncentivesResponse } from '../types.js';
2
2
  /**
3
- * Incentives can be used for fetching and claiming Obol incentives.
4
- * @class
5
- * @internal Access it through Client.incentives.
3
+ * Manages Obol incentive rewards querying eligibility and claiming from
4
+ * on-chain Merkle Distributor contracts.
5
+ *
6
+ * Do not instantiate directly; access via `client.incentives`.
7
+ *
6
8
  * @example
7
- * const obolClient = new Client(config);
8
- * await obolClient.incentives.claimIncentives(address);
9
+ * ```typescript
10
+ * const client = new Client({ chainId: 560048 }, signer);
11
+ *
12
+ * // Check claimable incentives
13
+ * const data = await client.incentives.getIncentivesByAddress("0xOperator...");
14
+ *
15
+ * // Check if already claimed
16
+ * const claimed = await client.incentives.isClaimed(data.contract_address, data.index);
17
+ *
18
+ * // Claim (sends on-chain transaction)
19
+ * const { txHash } = await client.incentives.claimIncentives("0xOperator...");
20
+ * ```
9
21
  */
10
22
  export declare class Incentives {
11
23
  private readonly signer;
@@ -14,44 +26,61 @@ export declare class Incentives {
14
26
  readonly provider: ProviderType | undefined | null;
15
27
  constructor(signer: SignerType | undefined, chainId: number, request: (endpoint: string, options?: RequestInit) => Promise<any>, provider: ProviderType | undefined | null);
16
28
  /**
17
- * Claims Obol incentives from a Merkle Distributor contract using an address.
29
+ * Claims Obol incentives from a Merkle Distributor contract for the given address.
18
30
  *
19
- * This method automatically fetches incentive data and verifies whether the incentives have already been claimed.
20
- * If `txHash` is `null`, it indicates that the incentives were already claimed.
31
+ * Automatically fetches incentive data, checks if already claimed, and submits
32
+ * the on-chain claim transaction. Returns `{ txHash: null }` if incentives were
33
+ * already claimed (idempotent).
21
34
  *
22
- * Note: This method is not yet enabled and will throw an error if called.
35
+ * @param address - The operator's Ethereum address to claim incentives for.
36
+ * @returns The transaction hash, or `{ txHash: null }` if already claimed.
37
+ * @throws {SignerRequiredError} If no signer was provided.
38
+ * @throws {Error} If no incentives are found for the address.
23
39
  *
24
- * @remarks
25
- * **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
26
- * and not pushed to version control.
27
- *
28
- * @param {string} address - The address to claim incentives for
29
- * @returns {Promise<ClaimIncentivesResponse>} The transaction hash or already claimed status
30
- * @throws Will throw an error if the incentives data is not found or the claim fails
31
- *
32
- * An example of how to use claimIncentives:
33
- * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L281)
40
+ * @example
41
+ * ```typescript
42
+ * const { txHash } = await client.incentives.claimIncentives("0xOperator...");
43
+ * if (txHash) {
44
+ * console.log("Claimed:", txHash);
45
+ * } else {
46
+ * console.log("Already claimed");
47
+ * }
48
+ * ```
34
49
  */
35
50
  claimIncentives(address: string): Promise<ClaimIncentivesResponse>;
36
51
  /**
37
- * Read isClaimed.
52
+ * Checks whether incentives have already been claimed for a given operator index.
38
53
  *
39
- * @param {ETH_ADDRESS} contractAddress - Address of the Merkle Distributor Contract
40
- * @param {ETH_ADDRESS} index - operator index in merkle tree
41
- * @returns {Promise<boolean>} true if incentives are already claime
54
+ * Read-only on-chain call no transaction sent.
42
55
  *
56
+ * @param contractAddress - Address of the Merkle Distributor contract.
57
+ * @param index - The operator's index in the Merkle tree (from {@link Incentives.getIncentivesByAddress}).
58
+ * @returns `true` if the incentives at that index have already been claimed.
43
59
  *
44
- * An example of how to use isClaimed:
45
- * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L266)
60
+ * @example
61
+ * ```typescript
62
+ * const claimed = await client.incentives.isClaimed(
63
+ * "0xMerkleDistributor...",
64
+ * 42,
65
+ * );
66
+ * ```
46
67
  */
47
68
  isClaimed(contractAddress: ETH_ADDRESS, index: number): Promise<boolean>;
48
69
  /**
49
- * @param address - Operator address
50
- * @returns {Promise<IncentivesType>} The matched incentives from DB
51
- * @throws On not found if address not found.
70
+ * Fetches claimable incentive data for an operator address from the Obol API.
71
+ *
72
+ * The returned data includes the Merkle proof, amount, and contract address
73
+ * needed to claim on-chain. This is a read-only API call.
74
+ *
75
+ * @param address - The operator's Ethereum address.
76
+ * @returns Claimable incentive data including amount, Merkle proof, and contract address.
77
+ * @throws {Error} If no incentives are found for the given address (404).
52
78
  *
53
- * An example of how to use getIncentivesByAddress:
54
- * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L250)
79
+ * @example
80
+ * ```typescript
81
+ * const incentives = await client.incentives.getIncentivesByAddress("0xOperator...");
82
+ * console.log(incentives.amount, incentives.contract_address);
83
+ * ```
55
84
  */
56
85
  getIncentivesByAddress(address: string): Promise<ClaimableIncentives>;
57
86
  }
@@ -9,150 +9,336 @@ export * from './services.js';
9
9
  export * from './verification/signature-validator.js';
10
10
  export * from './verification/common.js';
11
11
  export * from './constants.js';
12
+ export { ConflictError, SignerRequiredError, UnsupportedChainError, } from './errors.js';
12
13
  export { Incentives } from './incentives/incentives.js';
13
14
  export { Exit } from './exits/exit.js';
14
15
  export { ObolSplits } from './splits/splits.js';
15
16
  export { EOA } from './eoa/eoa.js';
16
17
  /**
17
- * Obol sdk Client can be used for creating, managing and activating distributed validators.
18
+ * Primary entrypoint for the Obol SDK. Use this class to create, manage,
19
+ * and activate Distributed Validators via the Obol API.
20
+ *
21
+ * All operations are namespaced under the client instance:
22
+ * - `client.incentives.*` – claim and query Obol incentives
23
+ * - `client.exit.*` – validate and recombine voluntary exit signatures
24
+ * - `client.splits.*` – deploy OVM / SplitV2 reward-splitting contracts
25
+ * - `client.eoa.*` – EOA withdrawals and batch deposits
26
+ *
27
+ * Cluster lifecycle helpers live directly on the client:
28
+ * - {@link Client.acceptObolLatestTermsAndConditions}
29
+ * - {@link Client.createClusterDefinition}
30
+ * - {@link Client.acceptClusterDefinition}
31
+ * - {@link Client.getClusterDefinition}
32
+ * - {@link Client.getClusterLock}
33
+ * - {@link Client.getClusterLockByHash}
34
+ * - {@link Client.createObolRewardsSplit}
35
+ * - {@link Client.createObolTotalSplit}
36
+ * - {@link Client.getOWRTranches}
37
+ *
38
+ * Supported networks (by `chainId`): Mainnet (1), Hoodi (560048),
39
+ * Gnosis (100), Sepolia (11155111).
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { Client } from "@obolnetwork/obol-sdk";
44
+ * import { Wallet } from "ethers";
45
+ *
46
+ * const signer = new Wallet(process.env.PRIVATE_KEY!);
47
+ * const client = new Client({ chainId: 560048 }, signer);
48
+ *
49
+ * // 1. Accept terms (required once before creating/updating data)
50
+ * await client.acceptObolLatestTermsAndConditions();
51
+ *
52
+ * // 2. Create a cluster definition
53
+ * const configHash = await client.createClusterDefinition({
54
+ * name: "my-cluster",
55
+ * operators: [{ address: "0x..." }],
56
+ * validators: [{
57
+ * fee_recipient_address: "0x...",
58
+ * withdrawal_address: "0x...",
59
+ * }],
60
+ * });
61
+ *
62
+ * // 3. Retrieve the definition
63
+ * const def = await client.getClusterDefinition(configHash);
64
+ * ```
18
65
  */
19
66
  export declare class Client extends Base {
20
- /**
21
- * The signer used for signing transactions.
22
- */
23
67
  private readonly signer;
24
68
  /**
25
- * The incentives module, responsible for managing Obol tokens distribution.
26
- * @type {Incentives}
69
+ * Incentives module claim and query Obol incentive rewards.
70
+ *
71
+ * @see {@link Incentives}
27
72
  */
28
73
  incentives: Incentives;
29
74
  /**
30
- * The exit module, responsible for managing exit validation.
31
- * @type {Exit}
75
+ * Exit module verify partial exit signatures and recombine exit blobs
76
+ * for distributed validator voluntary exits.
77
+ *
78
+ * @see {@link Exit}
32
79
  */
33
80
  exit: Exit;
34
81
  /**
35
- * The splits module, responsible for managing splits.
36
- * @type {ObolSplits}
82
+ * Splits module deploy OVM and SplitV2 contracts for reward/principal
83
+ * splitting, request withdrawals, and deposit to OVM contracts.
84
+ *
85
+ * @see {@link ObolSplits}
37
86
  */
38
87
  splits: ObolSplits;
39
88
  /**
40
- * The eoa module, responsible for managing EOA operations.
41
- * @type {EOA}
89
+ * EOA module request withdrawals and batch-deposit validators
90
+ * via Externally Owned Account contracts.
91
+ *
92
+ * @see {@link EOA}
42
93
  */
43
94
  eoa: EOA;
44
95
  /**
45
- * The blockchain provider, used to interact with the network.
46
- * It can be null, undefined, or a valid provider instance and defaults to the Signer provider if Signer is passed.
96
+ * The blockchain provider used for on-chain reads.
97
+ * Defaults to `signer.provider` when a signer is supplied.
47
98
  */
48
99
  provider: ProviderType | undefined | null;
49
100
  /**
50
- * @param config - Client configurations
51
- * @param config.baseUrl - obol-api url
52
- * @param config.chainId - Blockchain network ID
53
- * @param signer - ethersJS Signer
54
- * @returns Obol-SDK Client instance
55
- *
56
- * An example of how to instantiate obol-sdk Client:
57
- * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L29)
101
+ * Creates a new Obol SDK client.
102
+ *
103
+ * @param config - Client configuration object.
104
+ * @param config.baseUrl - Obol API base URL. Defaults to `https://api.obol.tech`.
105
+ * @param config.chainId - Target chain ID. Defaults to `560048` (Hoodi).
106
+ * Supported: 1 (Mainnet), 560048 (Hoodi), 100 (Gnosis), 11155111 (Sepolia).
107
+ * @param signer - An ethers `Wallet` or `JsonRpcSigner`. Required for any
108
+ * write operation (creating clusters, deploying splits, claiming incentives).
109
+ * Read-only operations (`getClusterDefinition`, `getClusterLock`) work without a signer.
110
+ * @param provider - An ethers `Provider`. If omitted, falls back to `signer.provider`.
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * // Minimal read-only client (no signer)
115
+ * const readClient = new Client({ chainId: 1 });
116
+ * const def = await readClient.getClusterDefinition(configHash);
117
+ *
118
+ * // Full client with signer for write operations
119
+ * const client = new Client({ chainId: 560048 }, signer);
120
+ * ```
58
121
  */
59
122
  constructor(config: {
60
123
  baseUrl?: string;
61
124
  chainId?: number;
62
125
  }, signer?: SignerType, provider?: ProviderType);
63
126
  /**
64
- * Accepts Obol terms and conditions to be able to create or update data.
65
- * @returns {Promise<string>} terms and conditions acceptance success message.
66
- * @throws On unverified signature or wrong hash.
127
+ * Accepts the latest Obol terms and conditions.
128
+ *
129
+ * **Must be called once** before any write operation
130
+ * (`createClusterDefinition`, `acceptClusterDefinition`, etc.).
131
+ * Calling it again after acceptance throws a {@link ConflictError}.
132
+ *
133
+ * - Requires a `signer` to be provided at client construction.
134
+ * - Signs an EIP-712 typed-data message; no on-chain transaction is sent.
135
+ * - Idempotent per signer address: once accepted, subsequent calls throw `ConflictError`.
67
136
  *
68
- * An example of how to use acceptObolLatestTermsAndConditions:
69
- * [acceptObolLatestTermsAndConditions](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L44)
137
+ * @returns A success message string from the Obol API.
138
+ * @throws {SignerRequiredError} If no signer was provided to the client.
139
+ * @throws {ConflictError} If the terms were already accepted by this address.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const message = await client.acceptObolLatestTermsAndConditions();
144
+ * console.log(message); // "Terms and conditions accepted successfully"
145
+ * ```
70
146
  */
71
147
  acceptObolLatestTermsAndConditions(): Promise<string>;
72
148
  /**
73
- * Deploys OWR and Splitter Proxy.
149
+ * Deploys an Optimistic Withdrawal Recipient (OWR) and a Splitter Proxy contract.
150
+ *
151
+ * Use this when the **principal** goes to a single address and only **rewards**
152
+ * are split among recipients. For splitting both principal and rewards, see
153
+ * {@link Client.createObolTotalSplit}.
74
154
  *
75
- * @remarks
76
- * **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
77
- * and not pushed to version control.
155
+ * - Requires a `signer` with ETH to pay deployment gas.
156
+ * - Sends one or more on-chain transactions (irreversible).
157
+ * - Automatically appends the Obol Retroactive Funding (RAF) recipient at the
158
+ * configured percentage (default 1%).
159
+ * - Only supported on Mainnet (1) and Hoodi (560048).
78
160
  *
79
- * @param {RewardsSplitPayload} rewardsSplitPayload - Data needed to deploy owr and splitter.
80
- * @returns {Promise<ClusterValidator>} owr address as withdrawal address and splitter as fee recipient
161
+ * @param rewardsSplitPayload - Configuration for the OWR and splitter deployment.
162
+ * @returns The deployed OWR address as `withdrawal_address` and the
163
+ * splitter proxy address as `fee_recipient_address`.
164
+ * @throws {SignerRequiredError} If no signer was provided.
165
+ * @throws {UnsupportedChainError} If the chain does not support splitters.
81
166
  *
82
- * An example of how to use createObolRewardsSplit:
83
- * [createObolRewardsSplit](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L141)
167
+ * @example
168
+ * ```typescript
169
+ * const { withdrawal_address, fee_recipient_address } =
170
+ * await client.createObolRewardsSplit({
171
+ * splitRecipients: [
172
+ * { account: "0xOperator1...", percentAllocation: 50 },
173
+ * { account: "0xOperator2...", percentAllocation: 49 },
174
+ * ],
175
+ * principalRecipient: "0xPrincipalAddress...",
176
+ * etherAmount: 32,
177
+ * });
178
+ * ```
84
179
  */
85
180
  createObolRewardsSplit({ splitRecipients, principalRecipient, etherAmount, ObolRAFSplit, distributorFee, controllerAddress, recoveryAddress, }: RewardsSplitPayload): Promise<ClusterValidator>;
86
181
  /**
87
- * Deploys Splitter Proxy.
182
+ * Deploys a Splitter Proxy contract that splits **both principal and rewards**.
183
+ *
184
+ * Unlike {@link Client.createObolRewardsSplit}, this method does not deploy an
185
+ * OWR – the splitter address is used as both `withdrawal_address` and
186
+ * `fee_recipient_address`.
88
187
  *
89
- * @remarks
90
- * **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
91
- * and not pushed to version control.
188
+ * - Requires a `signer` with ETH to pay deployment gas.
189
+ * - Sends an on-chain transaction if the splitter is not already deployed (irreversible).
190
+ * - If the predicted splitter is already deployed, returns the existing address without
191
+ * sending a transaction (idempotent).
192
+ * - Automatically appends the Obol RAF recipient.
193
+ * - Only supported on Mainnet (1) and Hoodi (560048).
92
194
  *
93
- * @param {TotalSplitPayload} totalSplitPayload - Data needed to deploy splitter if it doesnt exist.
94
- * @returns {Promise<ClusterValidator>} splitter address as withdrawal address and splitter as fee recipient too
195
+ * @param totalSplitPayload - Configuration for the splitter deployment.
196
+ * @returns The splitter address as both `withdrawal_address` and `fee_recipient_address`.
197
+ * @throws {SignerRequiredError} If no signer was provided.
198
+ * @throws {UnsupportedChainError} If the chain does not support splitters.
199
+ * @throws {Error} If required on-chain factory contracts are unavailable.
95
200
  *
96
- * An example of how to use createObolTotalSplit:
97
- * [createObolTotalSplit](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L168)
201
+ * @example
202
+ * ```typescript
203
+ * const { withdrawal_address, fee_recipient_address } =
204
+ * await client.createObolTotalSplit({
205
+ * splitRecipients: [
206
+ * { account: "0xOperator1...", percentAllocation: 50 },
207
+ * { account: "0xOperator2...", percentAllocation: 49.9 },
208
+ * ],
209
+ * });
210
+ * // withdrawal_address === fee_recipient_address (same splitter)
211
+ * ```
98
212
  */
99
213
  createObolTotalSplit({ splitRecipients, ObolRAFSplit, distributorFee, controllerAddress, }: TotalSplitPayload): Promise<ClusterValidator>;
100
214
  /**
101
- * Read OWR Tranches.
215
+ * Reads the tranches of a deployed Optimistic Withdrawal Recipient (OWR) contract.
102
216
  *
103
- * @remarks
104
- * **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
105
- * and not pushed to version control.
217
+ * Returns the principal recipient, reward recipient, and staked principal amount
218
+ * configured in the OWR. This is a read-only on-chain call (no transaction sent).
106
219
  *
107
- * @param {ETH_ADDRESS} owrAddress - Address of the Deployed OWR Contract
108
- * @returns {Promise<OWRTranches>} owr tranch information about principal and reward reciepient, as well as the principal amount
220
+ * - Requires a `signer` (for provider access).
109
221
  *
222
+ * @param owrAddress - The Ethereum address of the deployed OWR contract.
223
+ * @returns The OWR tranche data: `principalRecipient`, `rewardRecipient`, and `amountOfPrincipalStake`.
224
+ * @throws {SignerRequiredError} If no signer was provided.
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * const tranches = await client.getOWRTranches("0xOWRAddress...");
229
+ * console.log(tranches.principalRecipient);
230
+ * console.log(tranches.rewardRecipient);
231
+ * console.log(tranches.amountOfPrincipalStake);
232
+ * ```
110
233
  */
111
234
  getOWRTranches(owrAddress: ETH_ADDRESS): Promise<OWRTranches>;
112
235
  /**
113
- * Creates a cluster definition which contains cluster configuration.
114
- * @param {ClusterPayload} newCluster - The new unique cluster.
115
- * @returns {Promise<string>} config_hash.
116
- * @throws On duplicate entries, missing or wrong cluster keys.
236
+ * Creates a new cluster definition and registers it with the Obol API.
237
+ *
238
+ * A cluster definition describes the operators, validators, and configuration
239
+ * for a Distributed Key Generation (DKG) ceremony. After creation, each operator
240
+ * must call {@link Client.acceptClusterDefinition} to join.
241
+ *
242
+ * - Requires {@link Client.acceptObolLatestTermsAndConditions} to have been called first.
243
+ * - Requires a `signer`.
244
+ * - The `config_hash` returned is the unique identifier for this cluster.
245
+ * - Threshold is automatically set to `ceil(2/3 * operators.length)`.
246
+ * - Creating a duplicate cluster throws a {@link ConflictError}.
247
+ *
248
+ * @param newCluster - The cluster configuration payload.
249
+ * @returns The `config_hash` string that uniquely identifies this cluster definition.
250
+ * @throws {SignerRequiredError} If no signer was provided.
251
+ * @throws {ConflictError} If an identical cluster definition already exists.
117
252
  *
118
- * An example of how to use createClusterDefinition:
119
- * [createObolCluster](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L59)
253
+ * @example
254
+ * ```typescript
255
+ * const configHash = await client.createClusterDefinition({
256
+ * name: "my-dvt-cluster",
257
+ * operators: [{ address: "0xOp1..." }, { address: "0xOp2..." }],
258
+ * validators: [{
259
+ * fee_recipient_address: "0xFeeRecipient...",
260
+ * withdrawal_address: "0xWithdrawal...",
261
+ * }],
262
+ * });
263
+ * console.log("Cluster created:", configHash);
264
+ * ```
120
265
  */
121
266
  createClusterDefinition(newCluster: ClusterPayload): Promise<string>;
122
267
  /**
123
- * Approves joining a cluster with specific configuration.
124
- * @param {OperatorPayload} operatorPayload - The operator data including signatures.
125
- * @param {string} configHash - The config hash of the cluster which the operator confirms joining to.
126
- * @returns {Promise<ClusterDefinition>} The cluster definition.
127
- * @throws On unauthorized, duplicate entries, missing keys, not found cluster or invalid data.
128
- *
129
- * An example of how to use acceptClusterDefinition:
130
- * [acceptClusterDefinition](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L106)
268
+ * Accepts (joins) an existing cluster definition as an operator.
269
+ *
270
+ * Each operator in the cluster must call this method with their ENR and the
271
+ * cluster's `config_hash` to signal readiness for the DKG ceremony.
272
+ *
273
+ * - Requires a `signer` (the operator's address is derived from it).
274
+ * - Signs two EIP-712 messages: one for the config hash, one for the ENR.
275
+ * - The operator must be listed in the cluster definition's `operators` array.
276
+ *
277
+ * @param operatorPayload - Operator data. Must include `enr` (Ethereum Node Record)
278
+ * and `version` at minimum.
279
+ * @param configHash - The `config_hash` returned by {@link Client.createClusterDefinition}.
280
+ * @returns The updated cluster definition with the operator's acceptance recorded.
281
+ * @throws {SignerRequiredError} If no signer was provided.
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * const updatedDef = await client.acceptClusterDefinition(
286
+ * { enr: "enr:-LK4Q...", version: "v1.10.0" },
287
+ * configHash,
288
+ * );
289
+ * ```
131
290
  */
132
291
  acceptClusterDefinition(operatorPayload: OperatorPayload, configHash: string): Promise<ClusterDefinition>;
133
292
  /**
134
- * @param configHash - The configuration hash returned in createClusterDefinition
135
- * @returns {Promise<ClusterDefinition>} The cluster definition for config hash
136
- * @throws On not found config hash.
293
+ * Retrieves a cluster definition by its configuration hash.
294
+ *
295
+ * This is a read-only API call – no signer is required.
137
296
  *
138
- * An example of how to use getClusterDefinition:
139
- * [getObolClusterDefinition](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L74)
297
+ * @param configHash - The `config_hash` returned by {@link Client.createClusterDefinition}.
298
+ * @returns The full cluster definition including operators, validators, and metadata.
299
+ * @throws {Error} If no cluster definition exists for the given hash (404).
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * const definition = await client.getClusterDefinition(configHash);
304
+ * console.log(definition.name, definition.operators.length);
305
+ * ```
140
306
  */
141
307
  getClusterDefinition(configHash: string): Promise<ClusterDefinition>;
142
308
  /**
143
- * @param configHash - The configuration hash in cluster-definition
144
- * @returns {Promise<ClusterLock>} The matched cluster details (lock) from DB
145
- * @throws On not found cluster definition or lock.
309
+ * Retrieves a cluster lock by the cluster's configuration hash.
310
+ *
311
+ * A cluster lock is generated after a successful DKG ceremony and contains
312
+ * the distributed validators, their public key shares, and deposit data.
313
+ * This is a read-only API call – no signer is required.
146
314
  *
147
- * An example of how to use getClusterLock:
148
- * [getObolClusterLock](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L89)
315
+ * @param configHash - The `config_hash` from the cluster definition.
316
+ * @returns The cluster lock including distributed validators and deposit data.
317
+ * @throws {Error} If no lock exists for the given config hash (DKG not yet complete, or not found).
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * const lock = await client.getClusterLock(configHash);
322
+ * console.log(lock.distributed_validators.length);
323
+ * console.log(lock.lock_hash);
324
+ * ```
149
325
  */
150
326
  getClusterLock(configHash: string): Promise<ClusterLock>;
151
327
  /**
152
- * @param lockHash - The configuration hash in cluster-definition
153
- * @returns {Promise<ClusterLock>} The matched cluster details (lock) from DB
154
- * @throws On not found cluster definition or lock.
328
+ * Retrieves a cluster lock by its lock hash (as opposed to config hash).
329
+ *
330
+ * Use this when you have the `lock_hash` directly rather than the `config_hash`.
331
+ * This is a read-only API call – no signer is required.
332
+ *
333
+ * @param lockHash - The `lock_hash` from a cluster lock.
334
+ * @returns The cluster lock including distributed validators and deposit data.
335
+ * @throws {Error} If no lock exists for the given lock hash (not found).
155
336
  *
337
+ * @example
338
+ * ```typescript
339
+ * const lock = await client.getClusterLockByHash(lockHash);
340
+ * console.log(lock.cluster_definition.name);
341
+ * ```
156
342
  */
157
343
  getClusterLockByHash(lockHash: string): Promise<ClusterLock>;
158
344
  }
@@ -1,12 +1,26 @@
1
1
  import { type SafeRpcUrl, type ClusterLock } from './types.js';
2
2
  /**
3
- * Verifies Cluster Lock's validity.
4
- * @param lock - cluster lock
5
- * @param safeRpcUrl - optional safeRpcUrl for safe wallet verification
6
- * @returns {Promise<{ result: boolean }> } boolean result to indicate if lock is valid
7
- * @throws on missing keys or values.
8
- *
9
- * An example of how to use validateClusterLock:
10
- * [validateClusterLock](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L127)
3
+ * Verifies the cryptographic validity of a cluster lock.
4
+ *
5
+ * Checks all operator signatures, config hashes, and definition hashes within
6
+ * the lock. Supports both EOA and Safe Wallet signatures.
7
+ *
8
+ * This is a standalone utility – it does **not** require a `Client` instance.
9
+ *
10
+ * @param lock - The cluster lock object (e.g. from {@link Client.getClusterLock}).
11
+ * @param safeRpcUrl - Optional RPC URL for Safe Wallet signature verification.
12
+ * If omitted, falls back to the `RPC_MAINNET` / `RPC_HOODI` / etc. env vars.
13
+ * @returns `true` if the lock is cryptographically valid; `false` if invalid
14
+ * (e.g. missing keys, invalid signatures, hash mismatches) or on any error.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { validateClusterLock, Client } from "@obolnetwork/obol-sdk";
19
+ *
20
+ * const configHash = "0x..."; // your cluster config hash
21
+ * const client = new Client({ chainId: 560048 });
22
+ * const lock = await client.getClusterLock(configHash);
23
+ * const isValid = await validateClusterLock(lock);
24
+ * ```
11
25
  */
12
26
  export declare const validateClusterLock: (lock: ClusterLock, safeRpcUrl?: SafeRpcUrl) => Promise<boolean>;