Polygon

@stakefish/sdk-polygon

The @stakefish/sdk-polygon is a JavaScript/TypeScript library that provides a unified interface for staking operations on the Polygon PoS blockchain with stake.fish validators. It allows developers to easily delegate stakes, undelegate, claim 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-polygon
yarn add @stakefish/sdk-polygon

API Reference

Constructor

interface PolygonConfig {
  ethereumRpcUrl: string;
}

export class Polygon {
  constructor(config: PolygonConfig)
...
  • ethereumRpcUrl (required): Ethereum RPC endpoint URL for transaction broadcasting

Delegation

delegate(params: { delegatorAddress: string; amount: string; token?: 'POL' | 'MATIC' }): Promise<PolygonUnsignedTransaction>

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

Parameters:

  • params.delegatorAddress: The Ethereum address of the delegator (e.g., '0x...')
  • params.amount: The amount to delegate in wei (1 POL = 10^18 wei, 1 MATIC = 10^18 wei)
  • params.token (optional): Token to delegate ('POL' or 'MATIC'), defaults to 'POL'

Undelegation

undelegate(params: { delegatorAddress: string; amount: string }): Promise<PolygonUnsignedTransaction>

Creates an unsigned transaction for undelegating a specified amount from the stake.fish validator. This starts a 3-4 day unbonding period.

Parameters:

  • params.delegatorAddress: The Ethereum address of the delegator
  • params.amount: The amount to undelegate in wei (1 POL = 10^18 wei)

Claim Rewards

claimRewards(delegatorAddress: string): Promise<PolygonUnsignedTransaction>

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

Parameters:

  • delegatorAddress: The Ethereum address of the delegator

Claim Unstaked

claimUnstaked(delegatorAddress: string, unbondNonce: string): Promise<PolygonUnsignedTransaction>

Creates an unsigned transaction for claiming unstaked tokens after the unbonding period has completed.

Parameters:

  • delegatorAddress: The Ethereum address of the delegator
  • unbondNonce: The nonce from the undelegation transaction

Restake Rewards

restakeRewards(delegatorAddress: string): Promise<PolygonUnsignedTransaction>

Creates an unsigned transaction for compounding POL staking rewards into delegation.

Parameters:

  • delegatorAddress: The Ethereum address of the delegator

Approve Tokens

approve(params: { delegatorAddress: string; amount: string; token?: 'POL' | 'MATIC' }): Promise<PolygonUnsignedTransaction>

Creates an unsigned transaction to approve the StakeManager contract to spend tokens on behalf of the delegator. Important: Approval must be given to the StakeManager contract, NOT the ValidatorShare contract.

Parameters:

  • params.delegatorAddress: The Ethereum address of the delegator
  • params.amount: The amount to approve in wei
  • params.token (optional): Token to approve ('POL' or 'MATIC'), defaults to 'POL'

Get Balance

getBalance(address: string, token?: 'POL' | 'MATIC'): Promise<string>

Gets the token balance for a given address.

Parameters:

  • address: The Ethereum address to check balance for
  • token (optional): Token to check balance for ('POL' or 'MATIC'), defaults to 'POL'

Returns: Token balance in wei as a string

Get Allowance

getAllowance(delegatorAddress: string, token?: 'POL' | 'MATIC'): Promise<string>

Gets the amount of tokens that the StakeManager contract is approved to spend on behalf of the delegator.

Parameters:

  • delegatorAddress: The Ethereum address of the delegator
  • token (optional): Token to check allowance for ('POL' or 'MATIC'), defaults to 'POL'

Returns: Approved amount in wei as a string

Signing

sign(privateKeyHex: string, unsignedTx: PolygonUnsignedTransaction): Promise<PolygonSignedTransaction>

Signs the unsigned transaction using the provided private key. This operation works completely offline and does not require network connectivity.

Parameters:

  • privateKeyHex: The private key in hexadecimal format (with or without '0x' prefix)
  • unsignedTx: The unsigned transaction object from delegate(), undelegate(), claimRewards(), claimUnstaked(), or restakeRewards()

Broadcasting

broadcast(signedTransaction: PolygonSignedTransaction, checkInclusion?: boolean, timeoutMs?: number, pollIntervalMs?: number): Promise<PolygonTransactionBroadcastResult>

Broadcasts the signed transaction to the Ethereum network and optionally waits for inclusion confirmation.

Parameters:

  • signedTransaction: The signed transaction object from sign()
  • checkInclusion (optional): Whether to wait for transaction inclusion (default: false)
  • timeoutMs (optional): Maximum time to wait for inclusion in milliseconds (default: 60000)
  • pollIntervalMs (optional): Interval between inclusion checks in milliseconds (default: 2000)

Returns: PolygonTransactionBroadcastResult object containing:

  • txId: The transaction hash
  • success: Boolean indicating if the transaction was successful
  • error: Error message if the transaction failed (optional)

Examples

Full delegation example

import { Polygon } from '@stakefish/sdk-polygon';
// or: const { Polygon } = require('@stakefish/sdk-polygon');

const delegator = process.env.ETHEREUM_ADDRESS;
const rpcUrl = process.env.ETHEREUM_RPC_URL;
const privateKey = process.env.ETHEREUM_PRIVATE_KEY;

async function main() {
  const polygon = new Polygon({
    ethereumRpcUrl: rpcUrl,
  });

  // Delegation (1 POL = 10^18 wei)
  const tx = await polygon.delegate({
    delegatorAddress: delegator,
    amount: '1000000000000000000',
  });

  // Sign
  const signedTx = await polygon.sign(privateKey, tx);

  // Broadcast
  const result = await polygon.broadcast(signedTx);
  console.log('Broadcast result:', JSON.stringify(result));
}

void main().catch(console.error);

MATIC Delegation

// Delegation with MATIC tokens (1 MATIC = 10^18 wei)
const tx = await polygon.delegate({
  delegatorAddress: delegator,
  amount: '1000000000000000000',
  token: 'MATIC',
});

Undelegation

const tx = await polygon.undelegate({
  delegatorAddress: delegator,
  amount: '1000000000000000000',
});

Claim Rewards

const tx = await polygon.claimRewards(delegator);

Claim Unstaked

const tx = await polygon.claimUnstaked(delegator, '12345');

Restake Rewards

const tx = await polygon.restakeRewards(delegator);

Approve POL Tokens

// Approve StakeManager contract to spend 1 POL
const tx = await polygon.approve({
  delegatorAddress: delegator,
  amount: '1000000000000000000',
  token: 'POL',
});

Approve MATIC Tokens

// Approve StakeManager contract to spend 1 MATIC
const tx = await polygon.approve({
  delegatorAddress: delegator,
  amount: '1000000000000000000',
  token: 'MATIC',
});

Check POL Balance

const balance = await polygon.getBalance(delegator, 'POL');
console.log(`POL Balance: ${balance} wei`);

Check MATIC Balance

const balance = await polygon.getBalance(delegator, 'MATIC');
console.log(`MATIC Balance: ${balance} wei`);

Check POL Allowance

const allowance = await polygon.getAllowance(delegator, 'POL');
console.log(`POL Allowance: ${allowance} wei`);

Check MATIC Allowance

const allowance = await polygon.getAllowance(delegator, 'MATIC');
console.log(`MATIC Allowance: ${allowance} wei`);

Configuration

The SDK requires configuration during instantiation with Ethereum RPC endpoint:

const polygon = new Polygon({
  ethereumRpcUrl: 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID',
});

Notes

  • Staking happens on Ethereum mainnet, not on Polygon chain
  • You need ETH to pay gas fees for staking transactions
  • POL tokens must be on Ethereum mainnet to stake
  • Use Polygon Portal to bridge POL from Polygon to Ethereum if needed
  • All amounts in the SDK are specified in wei, the smallest unit of POL (1 POL = 10^18 wei)
  • The SDK automatically handles gas estimation and fee calculation for transactions
  • Private keys can be provided in hexadecimal format with or without the '0x' prefix
  • Private keys should be kept secure and never committed to version control