This repo contains the Obol Software Development Kit, for creating Distributed Validators with the help of the Obol API.
import { Client } from "@obolnetwork/obol-sdk";
import { Wallet } from "ethers";
// 1. Create a client (Holesky testnet)
const signer = new Wallet(process.env.PRIVATE_KEY!);
const client = new Client({ chainId: 17000 }, signer);
// 2. Accept terms and conditions (required once per address)
await client.acceptObolLatestTermsAndConditions();
// 3. Create a cluster definition
const configHash = await client.createClusterDefinition({
name: "my-cluster",
operators: [{ address: "0xOperator1..." }, { address: "0xOperator2..." }],
validators: [{
fee_recipient_address: "0xFeeRecipient...",
withdrawal_address: "0xWithdrawal...",
}],
});
// 4. Retrieve the definition / lock (read-only, no signer needed)
const definition = await client.getClusterDefinition(configHash);
const lock = await client.getClusterLock(configHash);Checkout our docs, examples, and SDK reference. Further guides and walkthroughs coming soon.
To use obol-sdk and in order to be able to create a cluster definition or accept an invite to join a cluster, you must accept the latest Obol terms and conditions by calling acceptObolLatestTermsAndConditions.
If you're integrating this SDK with a backend (e.g., in Node.js), and you store a private key for executing splitter transactions, handle it with extreme caution. Ensure that:
- The private key is securely stored (e.g., in an
.envfile). - Never commit or push your
.envfile containing the private key to version control.
When integrating the Obol SDK with a Safe Wallet, you can either pass an RPC URL OR provide the RPC_MAINNET or RPC_HOLESKY or RPC_GNOSIS or RPC_SEPOLIA or RPC_HOODI environment variable, pointing to the correct network's RPC URL. This is required to interact with Safe kit.
Please review the following guidelines:
If you encounter a bug or unexpected behavior, please follow these steps to report it:
- Go to the "Issues" tab of this repository.
- Click on the "Get started" button in the Bug report section.
- Provide a clear title and description of the issue following the format provided.
If you'd like to propose improvements or new features, please follow these steps:
- Fork this repository.
- Create a new branch for your changes.
- Make your changes and commit them with clear messages.
- Open a pull request with a detailed description of the changes.
All contributions are reviewed before they are merged into the main branch. Please address any feedback provided during the review process.
Thank you for contributing to Obol-SDK!
If you are an LLM, code-generation agent, or tool using this SDK programmatically, follow these guidelines to avoid common mistakes:
- Primary entrypoint:
Clientfrom@obolnetwork/obol-sdk. - Constructor:
new Client({ chainId }, signer?, provider?)–chainIddefaults to17000(Holesky). - All operations are namespaced under the client instance. Do not construct HTTP requests manually.
| Namespace | Method | Description |
|---|---|---|
| (root) | acceptObolLatestTermsAndConditions() |
Accept T&C (required once before writes) |
| (root) | createClusterDefinition(payload) |
Create a new cluster → returns config_hash |
| (root) | acceptClusterDefinition(operatorPayload, configHash) |
Join a cluster as an operator |
| (root) | getClusterDefinition(configHash) |
Fetch cluster definition (read-only) |
| (root) | getClusterLock(configHash) |
Fetch cluster lock by config hash (read-only) |
| (root) | getClusterLockByHash(lockHash) |
Fetch cluster lock by lock hash (read-only) |
| (root) | createObolRewardsSplit(payload) |
Deploy OWR + splitter (rewards-only split) |
| (root) | createObolTotalSplit(payload) |
Deploy splitter (total split) |
| (root) | getOWRTranches(owrAddress) |
Read OWR tranche info (read-only on-chain) |
client.splits |
createValidatorManagerAndRewardsSplit(payload) |
Deploy OVM + SplitV2 (rewards-only) |
client.splits |
createValidatorManagerAndTotalSplit(payload) |
Deploy OVM + SplitV2 (total) |
client.splits |
requestWithdrawal(payload) |
Request withdrawal from OVM |
client.splits |
deposit(payload) |
Deposit to OVM contract |
client.incentives |
getIncentivesByAddress(address) |
Fetch claimable incentives (read-only) |
client.incentives |
isClaimed(contractAddress, index) |
Check if incentives claimed (read-only) |
client.incentives |
claimIncentives(address) |
Claim incentives on-chain |
client.eoa |
requestWithdrawal(payload) |
Request EOA withdrawal |
client.eoa |
deposit(payload) |
Batch deposit validators |
client.exit |
verifyPartialExitSignature(...) |
Verify BLS partial exit signature |
client.exit |
verifyExitPayloadSignature(...) |
Verify ECDSA exit payload signature |
client.exit |
validateExitBlobs(...) |
Validate exit blobs against cluster config |
client.exit |
recombineExitBlobs(...) |
Aggregate partial exit signatures |
| Export | Description |
|---|---|
validateClusterLock(lock, safeRpcUrl?) |
Verify a cluster lock's cryptographic validity |
- All request/response types are exported from the package root.
- Error types exported:
ConflictError,SignerRequiredError,UnsupportedChainError. - Methods that write (create, deploy, claim) require a
signer. Methods that read (get, fetch) do not. - Supported chain IDs:
1(Mainnet),560048(Hoodi). - Splitter/OVM deployment is only supported on chains 1, 17000, and 560048.
If using this SDK in Next.js or other SSR frameworks, add this minimal config to your next.config.js:
webpack: (config, { isServer, webpack }) => {
if (!isServer) {
config.plugins.push(
new webpack.DefinePlugin({
'process.stdout.isTTY': 'false',
'process.stderr.isTTY': 'false',
})
);
} else {
// Server: Externalize native dependencies
config.externals = config.externals || [];
config.externals.push({
'@chainsafe/bls': 'commonjs @chainsafe/bls',
'@chainsafe/blst': 'commonjs @chainsafe/blst',
'bcrypto': 'commonjs bcrypto',
});
}
// Ignore .node files
config.plugins.push(
new webpack.IgnorePlugin({ resourceRegExp: /\.node$/ })
);
return config;
}