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-eigenlayer
yarn add @stakefish/sdk-eigenlayer

Type 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 URL
  • network (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 staker
  • strategies: Array of strategy contract addresses
  • shares: 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 staker
  • withdrawalRoot: The withdrawal root hash from queueWithdrawal
  • tokens: Array of token addresses matching withdrawal strategies
  • receiveAsTokens: 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 staker
  • revertIfNoBalance (optional): Revert if no balance to checkpoint. Defaults to false.

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 staker
  • tokens: 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 address
  • eigenPod: EigenPod contract address (or null)
  • isDelegated: Whether delegated to an operator
  • delegatedTo: Operator address (or null)
  • sharesGwei: Restaked shares in gwei
  • activeValidatorCount: Number of verified validators
  • unprovenValidatorCount: Number of unproven validators
  • canStartCheckpoint: Whether a checkpoint can be started
  • needsCheckpointVerify: Whether checkpoint proofs need verification
  • validators: 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 from sign()
  • checkInclusion (optional): Whether to wait for transaction inclusion. Defaults to false.
  • timeoutMs (optional): Maximum time to wait for inclusion in milliseconds. Defaults to 60000.
  • pollIntervalMs (optional): Interval between inclusion checks in milliseconds. Defaults to 2000.

Returns: EigenLayerBroadcastResult object containing:

  • txId: The transaction hash
  • success: Boolean indicating if the transaction was successful
  • error: 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 -- verifyWithdrawalCredentials and verifyCheckpointProofs return 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