Monad

@stakefish/sdk-monad

The @stakefish/sdk-monad is a JavaScript/TypeScript library that provides a unified interface for staking operations on the Monad blockchain with stake.fish validators. It allows developers to easily delegate stakes, undelegate, withdraw, claim rewards, compound rewards, sign transactions, and broadcast them to the network.

Table of Contents

Installation

To use this SDK in your project, install it via npm or yarn:

npm install @stakefish/sdk-monad
yarn add @stakefish/sdk-monad

API Reference

Constructor

interface MonadConfig {
  rpcUrl: string;
}

export class Monad {
  constructor(config: MonadConfig)
  // ...
}
  • rpcUrl (required): Monad RPC endpoint URL for transaction broadcasting

Note: The SDK is hardcoded to use the stake.fish validator (ID: 152, Address: 0x606052370B215b346864724F10723dBd5e1423e8). All staking operations will be directed to this validator.

Delegation

delegate(params: { delegatorAddress: string; amount: string }): Promise<MonadUnsignedTransaction>

Creates an unsigned transaction for delegating a specified amount of MON tokens (in wei, the smallest unit) from the delegator's address to the stake.fish validator.

Parameters:

  • params.delegatorAddress: The Monad address of the delegator (e.g., '0x...')
  • params.amount: The amount to delegate in wei (1 MON = 10^18 wei)

Minimum Delegation: 0.1 MON (100000000000000000 wei)

Reserve Balance: You must maintain at least 10 MON in your wallet for gas fees

Undelegation

undelegate(params: { delegatorAddress: string; amount: string; withdrawId?: string }): Promise<MonadUnsignedTransaction>

Creates an unsigned transaction for undelegating a specified amount from the stake.fish validator. This starts the unbonding period before you can withdraw the tokens.

Parameters:

  • params.delegatorAddress: The Monad address of the delegator
  • params.amount: The amount to undelegate in wei
  • params.withdrawId (optional): Withdrawal identifier (0-255), defaults to '0'. Each (validator, delegator) pair can have up to 256 in-flight withdrawal requests.

Withdraw

withdraw(params: { delegatorAddress: string; withdrawId: string }): Promise<MonadUnsignedTransaction>

Creates an unsigned transaction for withdrawing previously undelegated tokens after the unbonding period has completed.

Parameters:

  • params.delegatorAddress: The Monad address of the delegator
  • params.withdrawId: The withdrawal identifier (must match the ID used in undelegate)

Claim Rewards

claimRewards(delegatorAddress: string): Promise<MonadUnsignedTransaction>

Creates an unsigned transaction for claiming MON staking rewards from the stake.fish validator.

Parameters:

  • delegatorAddress: The Monad address of the delegator

Compound

compound(delegatorAddress: string): Promise<MonadUnsignedTransaction>

Creates an unsigned transaction for compounding (restaking) MON staking rewards back into delegation.

Parameters:

  • delegatorAddress: The Monad address of the delegator

Get Balance

getBalance(address: string): Promise<string>

Gets the native MON balance for a given address.

Parameters:

  • address: The Monad address to check balance for

Returns: MON balance in wei as a string

Signing

sign(privateKeyHex: string, unsignedTx: MonadUnsignedTransaction): Promise<MonadSignedTransaction>

Signs an unsigned transaction using a private key. This can be done offline.

Parameters:

  • privateKeyHex: The private key in hexadecimal format (with or without '0x' prefix)
  • unsignedTx: The unsigned transaction returned from prepare methods

Returns: A signed transaction object

Broadcasting

broadcast(
  signedTx: MonadSignedTransaction,
  checkInclusion?: boolean,
  timeoutMs?: number,
  pollIntervalMs?: number
): Promise<MonadTransactionBroadcastResult>

Broadcasts a signed transaction to the Monad network.

Parameters:

  • signedTx: The signed transaction from sign()
  • checkInclusion (optional): Whether to wait for transaction inclusion (default: false)
  • timeoutMs (optional): Timeout in milliseconds (default: 60000)
  • pollIntervalMs (optional): Polling interval in milliseconds (default: 1000)

Returns: Transaction broadcast result with transaction hash

Examples

Complete Delegation Flow

import { Monad } from '@stakefish/sdk-monad';

// Initialize SDK
const monad = new Monad({
  rpcUrl: 'https://monad-rpc-url',
});

async function delegateExample() {
  // 1. Check balance
  const address = '0xCEB64AcfB2d993A9689B454AB3039AE155d6b607';
  const balance = await monad.getBalance(address);
  console.log('Balance:', balance, 'wei');

  // 2. Prepare delegation transaction (delegate 1 MON)
  const unsignedTx = await monad.delegate({
    delegatorAddress: address,
    amount: '1000000000000000000', // 1 MON in wei
  });

  // 3. Sign transaction offline
  const privateKey = 'your-private-key-here';
  const signedTx = await monad.sign(privateKey, unsignedTx);

  // 4. Broadcast transaction
  const result = await monad.broadcast(signedTx, true); // Wait for inclusion
  console.log('Transaction hash:', result.txId);
  console.log('Status:', result.status);
}

Undelegation and Withdrawal Flow

import { Monad } from '@stakefish/sdk-monad';

const monad = new Monad({
  rpcUrl: 'https://monad-rpc-url',
});

async function undelegateExample() {
  const address = '0xCEB64AcfB2d993A9689B454AB3039AE155d6b607';
  const privateKey = 'your-private-key-here';
  const withdrawId = '0'; // Use same ID for undelegate and withdraw

  // 1. Undelegate 0.5 MON
  const undelegateTx = await monad.undelegate({
    delegatorAddress: address,
    amount: '500000000000000000', // 0.5 MON
    withdrawId: withdrawId,
  });

  const signedUndelegate = await monad.sign(privateKey, undelegateTx);
  const undelegateResult = await monad.broadcast(signedUndelegate, true);
  console.log('Undelegation tx:', undelegateResult.txId);

  // 2. Wait for unbonding period to complete (varies by network)
  // ...

  // 3. Withdraw undelegated tokens
  const withdrawTx = await monad.withdraw({
    delegatorAddress: address,
    withdrawId: withdrawId, // Must match the ID used in undelegate
  });

  const signedWithdraw = await monad.sign(privateKey, withdrawTx);
  const withdrawResult = await monad.broadcast(signedWithdraw, true);
  console.log('Withdrawal tx:', withdrawResult.txId);
}

Compound Rewards

import { Monad } from '@stakefish/sdk-monad';

const monad = new Monad({
  rpcUrl: 'https://monad-rpc-url',
});

async function compoundExample() {
  const address = '0xCEB64AcfB2d993A9689B454AB3039AE155d6b607';
  const privateKey = 'your-private-key-here';

  // Compound rewards back into delegation
  const compoundTx = await monad.compound(address);

  const signedTx = await monad.sign(privateKey, compoundTx);
  const result = await monad.broadcast(signedTx, true);
  console.log('Compound tx:', result.txId);
}

Notes

Important Constraints

  • Minimum Delegation: 0.1 MON (100000000000000000 wei)
  • Reserve Balance: Must maintain at least 10 MON in wallet for gas fees
  • WithdrawId Range: 0-255 (each validator-delegator pair supports up to 256 concurrent withdrawal requests)
  • Amount Format: All amounts must be in wei (1 MON = 10^18 wei) as positive integer strings

Native Token

Monad uses the native MON token for staking. Unlike ERC20 tokens, you do NOT need to approve the StakeManager contract before delegating.

Gas Fees

All transactions require MON for gas fees. Ensure your wallet has sufficient balance beyond your delegation amount to cover gas costs.

Withdrawal Process

  1. Call undelegate() with a withdrawId (0-255)
  2. Wait for the unbonding period to complete
  3. Call withdraw() with the same withdrawId to claim your tokens

Error Handling

The SDK will throw errors for:

  • Invalid addresses (must be 0x followed by 40 hex characters)
  • Invalid amounts (must be positive integer strings)
  • Amounts below minimum delegation (0.1 MON)
  • Insufficient balance (including reserve requirement)
  • Invalid withdrawId (must be 0-255)