EigenLayer (native Ether restaking)
@stakefish/sdk-eigenlayer
The @stakefish/sdk-eigenlayer is a JavaScript/TypeScript library that provides a unified interface for EigenLayer restaking operations with stake.fish. It allows developers to manage EigenPods, delegate to operators, manage withdrawals, handle checkpoints, and claim rewards.
Table of Contents
Installation
npm install @stakefish/sdk-eigenlayeryarn add @stakefish/sdk-eigenlayerType Imports
import { EigenLayer } from '@stakefish/sdk-eigenlayer';
import type {
EigenLayerConfig,
EigenLayerAddress,
EigenLayerQueueWithdrawal,
EigenLayerCompleteWithdrawal,
EigenLayerStartCheckpoint,
EigenLayerClaimRewards,
EigenLayerSign,
EigenLayerBroadcast,
EigenLayerUnsignedTransaction,
EigenLayerSignedTransaction,
EigenLayerBroadcastResult,
EigenLayerStakerResponseDto,
EigenLayerWithdrawalsResponseDto,
EigenLayerRewardsResponseDto,
} from '@stakefish/sdk-eigenlayer';API Reference
Constructor
import type { EigenLayerConfig } from '@stakefish/sdk-eigenlayer';
new EigenLayer({ ethereumRpcUrl: 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID' }: EigenLayerConfig)Creates a new EigenLayer SDK instance.
Parameters:
ethereumRpcUrl: Ethereum RPC endpoint URLnetwork(optional): Network to use,'mainnet'or'hoodi'. Defaults to'mainnet'.
Note: Contract addresses, stakefish operator, and service URLs are automatically configured per network.
Create Pod
import type { EigenLayerAddress, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
createPod({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to deploy an EigenPod for the staker. Must be called before any other staking operations.
Parameters:
delegatorAddress: The Ethereum address of the staker
Delegation
import type { EigenLayerAddress, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
delegate({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to delegate to the stake.fish operator. Requires an existing EigenPod.
Parameters:
delegatorAddress: The Ethereum address of the staker
Undelegation
import type { EigenLayerAddress, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
undelegate({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to undelegate from the operator. This starts a ~14-day escrow period.
Parameters:
delegatorAddress: The Ethereum address of the staker
Queue Withdrawal
import type { EigenLayerQueueWithdrawal, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
queueWithdrawal({ delegatorAddress: '0x...', strategies: ['0x...'], shares: ['32000000000'] }: EigenLayerQueueWithdrawal): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to queue a withdrawal of restaked shares.
Parameters:
delegatorAddress: The Ethereum address of the stakerstrategies: Array of strategy contract addressesshares: Array of share amounts (must match strategies length)
Complete Withdrawal
import type { EigenLayerCompleteWithdrawal, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
completeWithdrawal({ delegatorAddress: '0x...', withdrawalRoot: '0x...', tokens: ['0x...'], receiveAsTokens: true }: EigenLayerCompleteWithdrawal): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to complete a queued withdrawal after the escrow period.
Parameters:
delegatorAddress: The Ethereum address of the stakerwithdrawalRoot: The withdrawal root hash from queueWithdrawaltokens: Array of token addresses matching withdrawal strategiesreceiveAsTokens: Whether to receive as tokens (true) or restake (false)
Start Checkpoint
import type { EigenLayerStartCheckpoint, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
startCheckpoint({ delegatorAddress: '0x...', revertIfNoBalance: false }: EigenLayerStartCheckpoint): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to start a checkpoint on the staker's EigenPod.
Parameters:
delegatorAddress: The Ethereum address of the stakerrevertIfNoBalance(optional): Revert if no balance to checkpoint. Defaults tofalse.
Verify Withdrawal Credentials
import type { EigenLayerAddress, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
verifyWithdrawalCredentials({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerUnsignedTransaction[]>Returns an array of unsigned transactions to verify withdrawal credentials for unproven validators. Transactions are batched by the proof generation service and must be submitted in order.
Parameters:
delegatorAddress: The Ethereum address of the staker
Verify Checkpoint Proofs
import type { EigenLayerAddress, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
verifyCheckpointProofs({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerUnsignedTransaction[]>Returns an array of unsigned transactions to verify checkpoint proofs for an active checkpoint. Transactions are batched and must be submitted in order.
Parameters:
delegatorAddress: The Ethereum address of the staker
Claim Rewards
import type { EigenLayerClaimRewards, EigenLayerUnsignedTransaction } from '@stakefish/sdk-eigenlayer';
claimRewards({ delegatorAddress: '0x...', tokens: ['0x...'] }: EigenLayerClaimRewards): Promise<EigenLayerUnsignedTransaction>Creates an unsigned transaction to claim accumulated rewards with Merkle proof.
Parameters:
delegatorAddress: The Ethereum address of the stakertokens: Array of reward token addresses to claim
Get Staker Info
import type { EigenLayerAddress, EigenLayerStakerResponseDto } from '@stakefish/sdk-eigenlayer';
getStaker({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerStakerResponseDto>Gets the staker profile including delegation status, validator info, and checkpoint state.
Parameters:
delegatorAddress: The Ethereum address of the staker
Returns: Object containing:
address: Staker addresseigenPod: EigenPod contract address (or null)isDelegated: Whether delegated to an operatordelegatedTo: Operator address (or null)sharesGwei: Restaked shares in gweiactiveValidatorCount: Number of verified validatorsunprovenValidatorCount: Number of unproven validatorscanStartCheckpoint: Whether a checkpoint can be startedneedsCheckpointVerify: Whether checkpoint proofs need verificationvalidators: List of validator details
Get Withdrawals
import type { EigenLayerAddress, EigenLayerWithdrawalsResponseDto } from '@stakefish/sdk-eigenlayer';
getWithdrawals({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerWithdrawalsResponseDto>Gets queued and completable withdrawals for the staker.
Parameters:
delegatorAddress: The Ethereum address of the staker
Get Rewards
import type { EigenLayerAddress, EigenLayerRewardsResponseDto } from '@stakefish/sdk-eigenlayer';
getRewards({ delegatorAddress: '0x...' }: EigenLayerAddress): Promise<EigenLayerRewardsResponseDto>Gets summarized rewards per token for the staker.
Parameters:
delegatorAddress: The Ethereum address of the staker
Signing
import type { EigenLayerSign, EigenLayerUnsignedTransaction, EigenLayerSignedTransaction } from '@stakefish/sdk-eigenlayer';
sign({ privateKeyHex: '0x...', unsignedTx }: EigenLayerSign): Promise<EigenLayerSignedTransaction>Signs an unsigned transaction using the provided private key. This operation works completely offline.
Parameters:
privateKeyHex: The private key in hexadecimal format (with or without '0x' prefix)unsignedTx: The unsigned transaction object from any prepare method
Broadcasting
import type { EigenLayerBroadcast, EigenLayerSignedTransaction, EigenLayerBroadcastResult } from '@stakefish/sdk-eigenlayer';
broadcast({ signedTx, checkInclusion: true, timeoutMs: 60000, pollIntervalMs: 2000 }: EigenLayerBroadcast): Promise<EigenLayerBroadcastResult>Broadcasts the signed transaction to the Ethereum network.
Parameters:
signedTx: The signed transaction object fromsign()checkInclusion(optional): Whether to wait for transaction inclusion. Defaults tofalse.timeoutMs(optional): Maximum time to wait for inclusion in milliseconds. Defaults to60000.pollIntervalMs(optional): Interval between inclusion checks in milliseconds. Defaults to2000.
Returns: EigenLayerBroadcastResult object containing:
txId: The transaction hashsuccess: Boolean indicating if the transaction was successfulerror: Error message if the transaction failed (optional)
Examples
Basic Setup
import { EigenLayer } from '@stakefish/sdk-eigenlayer';
import type { EigenLayerConfig } from '@stakefish/sdk-eigenlayer';
// or: const { EigenLayer } = require('@stakefish/sdk-eigenlayer');
const config: EigenLayerConfig = {
ethereumRpcUrl: 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID',
};
const eigenlayer = new EigenLayer(config);Create Pod and Delegate
import type { EigenLayerUnsignedTransaction, EigenLayerSignedTransaction, EigenLayerBroadcastResult } from '@stakefish/sdk-eigenlayer';
const staker = '0x...';
const privateKeyHex = process.env.PRIVATE_KEY!;
// 1. Create EigenPod
const createPodTx: EigenLayerUnsignedTransaction = await eigenlayer.createPod({ delegatorAddress: staker });
const signedCreatePod: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: createPodTx });
const createPodResult: EigenLayerBroadcastResult = await eigenlayer.broadcast({ signedTx: signedCreatePod, checkInclusion: true });
console.log(`Pod created: ${createPodResult.txId}`);
// 2. Delegate to stake.fish
const delegateTx: EigenLayerUnsignedTransaction = await eigenlayer.delegate({ delegatorAddress: staker });
const signedDelegate: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: delegateTx });
const delegateResult: EigenLayerBroadcastResult = await eigenlayer.broadcast({ signedTx: signedDelegate, checkInclusion: true });
console.log(`Delegated: ${delegateResult.txId}`);Check Staker Status
import type { EigenLayerStakerResponseDto } from '@stakefish/sdk-eigenlayer';
const stakerInfo: EigenLayerStakerResponseDto = await eigenlayer.getStaker({ delegatorAddress: '0x...' });
console.log(`Delegated: ${stakerInfo.isDelegated}`);
console.log(`Operator: ${stakerInfo.delegatedTo}`);
console.log(`Active validators: ${stakerInfo.activeValidatorCount}`);Checkpoint Workflow
import type { EigenLayerUnsignedTransaction, EigenLayerSignedTransaction } from '@stakefish/sdk-eigenlayer';
// 1. Start checkpoint
const checkpointTx: EigenLayerUnsignedTransaction = await eigenlayer.startCheckpoint({ delegatorAddress: staker });
const signedCheckpoint: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: checkpointTx });
await eigenlayer.broadcast({ signedTx: signedCheckpoint, checkInclusion: true });
// 2. Verify credential proofs (batched, submit in order)
const credentialTxs: EigenLayerUnsignedTransaction[] = await eigenlayer.verifyWithdrawalCredentials({ delegatorAddress: staker });
for (const tx of credentialTxs) {
const signed: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: tx });
await eigenlayer.broadcast({ signedTx: signed, checkInclusion: true });
}
// 3. Verify checkpoint proofs (batched, submit in order)
const checkpointProofTxs: EigenLayerUnsignedTransaction[] = await eigenlayer.verifyCheckpointProofs({ delegatorAddress: staker });
for (const tx of checkpointProofTxs) {
const signed: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: tx });
await eigenlayer.broadcast({ signedTx: signed, checkInclusion: true });
}Withdrawal Workflow
import type { EigenLayerUnsignedTransaction, EigenLayerSignedTransaction, EigenLayerWithdrawalsResponseDto } from '@stakefish/sdk-eigenlayer';
// 1. Queue withdrawal
const queueTx: EigenLayerUnsignedTransaction = await eigenlayer.queueWithdrawal({
delegatorAddress: staker,
strategies: ['0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0'],
shares: ['32000000000'],
});
const signedQueue: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: queueTx });
await eigenlayer.broadcast({ signedTx: signedQueue, checkInclusion: true });
// 2. Check withdrawal status (after ~14 day escrow)
const withdrawals: EigenLayerWithdrawalsResponseDto = await eigenlayer.getWithdrawals({ delegatorAddress: staker });
const completable = withdrawals.withdrawals.filter(w => w.isCompletable);
// 3. Complete withdrawal
if (completable.length > 0) {
const completeTx: EigenLayerUnsignedTransaction = await eigenlayer.completeWithdrawal({
delegatorAddress: staker,
withdrawalRoot: completable[0].withdrawalRoot,
tokens: ['0x0000000000000000000000000000000000000000'], // ETH
receiveAsTokens: true,
});
const signedComplete: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: completeTx });
await eigenlayer.broadcast({ signedTx: signedComplete, checkInclusion: true });
}Claim Rewards
import type { EigenLayerUnsignedTransaction, EigenLayerSignedTransaction, EigenLayerRewardsResponseDto } from '@stakefish/sdk-eigenlayer';
const rewards: EigenLayerRewardsResponseDto = await eigenlayer.getRewards({ delegatorAddress: staker });
const claimableTokens = rewards.rewards
.filter(r => BigInt(r.claimable) > 0n)
.map(r => r.token);
if (claimableTokens.length > 0) {
const claimTx: EigenLayerUnsignedTransaction = await eigenlayer.claimRewards({
delegatorAddress: staker,
tokens: claimableTokens,
});
const signedClaim: EigenLayerSignedTransaction = await eigenlayer.sign({ privateKeyHex, unsignedTx: claimTx });
await eigenlayer.broadcast({ signedTx: signedClaim, checkInclusion: true });
}Hoodi Testnet
const eigenlayer = new EigenLayer({
ethereumRpcUrl: 'https://hoodi.infura.io/v3/YOUR_PROJECT_ID',
network: 'hoodi',
});Notes
- EigenPod is required -- you must create an EigenPod before delegating or performing other operations
- Staking happens on Ethereum -- you need ETH to pay gas fees
- Delegation is to operators -- EigenLayer delegates to operators (like stake.fish), not validators directly
- Undelegation has a ~14-day escrow -- queued withdrawals can only be completed after the escrow period
- Batch transactions must be ordered --
verifyWithdrawalCredentialsandverifyCheckpointProofsreturn arrays that must be submitted sequentially - All signing operations are performed locally -- keys are never transmitted over the network
- Private keys can be provided in hexadecimal format with or without the '0x' prefix
Updated 1 day ago
