Selendra

Documentation

Build with Selendra SDK

Practical examples using Selendra SDK

Working code examples for building dApps with Selendra SDK.

Staking Dashboard

Display Staking Info

import { Connection, keypair_from_string } from "@selendrajs/sdk";

async function getStakingInfo(address: string) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const info = await conn.get_staking_info(address);

  return {
    bonded: info.bonded,
    minimumValidatorCount: info.minimum_validator_count,
    sessionsPerEra: info.sessions_per_era,
  };
}

Bond and Nominate

async function bondTokens(amount: bigint) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  // Bond 100 SEL (12 decimals)
  return await signed.stake_bond(100_000_000_000_000n);
}

async function nominateValidators(validators: string[]) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  return await signed.stake_nominate(validators);
}

async function becomeValidator(commission: number) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  return await signed.stake_validate(commission); // 10% = 10
}

async function stopStaking() {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  return await signed.stake_chill();
}

Complete React Component

import { useStaking, useSelendra } from "@selendrajs/sdk/react";
import { useState } from "react";

function StakingDashboard() {
  const { isConnected } = useSelendra();
  const { bond, nominate, validate, chill, loading } = useStaking();
  const [amount, setAmount] = useState("");
  const [validators, setValidators] = useState<string[]>([]);

  if (!isConnected) return <div>Connecting...</div>;

  return (
    <div>
      <h2>Staking Dashboard</h2>

      <section>
        <h3>Bond Tokens</h3>
        <input
          type="number"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="Amount in SEL"
        />
        <button
          onClick={() => bond(BigInt(amount) * 1_000_000_000_000n)}
          disabled={loading}
        >
          Bond
        </button>
      </section>

      <section>
        <h3>Nominate Validators</h3>
        <button onClick={() => nominate(validators)} disabled={loading}>
          Nominate {validators.length} Validators
        </button>
      </section>

      <section>
        <button onClick={() => validate(10)} disabled={loading}>
          Register (10% commission)
        </button>
        <button onClick={chill} disabled={loading}>
          Stop Staking
        </button>
      </section>
    </div>
  );
}

Governance

Query Treasury

async function getTreasuryInfo() {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");

  return {
    proposalsCount: await conn.get_treasury_proposals_count(),
    approvals: await conn.get_treasury_approvals(),
  };
}

Propose Spend

async function proposeTreasurySpend(value: bigint, beneficiary: string) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  return await signed.treasury_propose_spend(value, beneficiary);
}

Get Validators

async function getValidators() {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");

  return {
    current: await conn.get_current_era_validators(),
    next: await conn.get_next_era_reserved_validators(),
  };
}

Unified Accounts

Convert Addresses

import { UnifiedAccountManager } from "@selendrajs/sdk";

async function convertAddress(api, substrateAddress: string) {
  const manager = new UnifiedAccountManager(api);

  const evmAddress = manager.substrateToEvm(substrateAddress);
  const backToSubstrate = manager.evmToSubstrate(evmAddress);

  return { evmAddress, backToSubstrate };
}

Check and Claim Mapping

async function checkMapping(api, address: string) {
  const manager = new UnifiedAccountManager(api);

  const hasMapping = await manager.hasMappingOnChain(address);

  if (hasMapping) {
    return await manager.getEvmAddressFromMapping(address);
  }

  return null;
}

async function claimEvmAddress(api, signer) {
  const manager = new UnifiedAccountManager(api);
  return await manager.claimDefaultEvmAddress(signer);
}

Account Queries

Get Balance

async function getBalance(address?: string) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = address
    ? keypair_from_string(address)
    : keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  const balance = await signed.get_balance();
  console.log(`Balance: ${Number(balance) / 1e12} SEL`);

  return balance;
}

Get Account Details

async function getAccountDetails() {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  const info = await signed.get_account_info();

  return {
    nonce: info.nonce,
    consumers: info.consumers,
    providers: info.providers,
    free: info.data.free,
    reserved: info.data.reserved,
    frozen: info.data.free_frozen,
  };
}

Transfer Tokens

async function transferTokens(to: string, amount: bigint) {
  const conn = await Connection.new("wss://rpc-testnet.selendra.org");
  const keypair = keypair_from_string("//Alice");
  const signed = conn.sign(keypair);

  return await signed.transfer(to, amount);
}

// Usage
await transferTokens(
  "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
  1_000_000_000_000n // 1 SEL
);

React Wallet Component

import { useBalance, useTransaction, useSelendra } from "@selendrajs/sdk/react";
import { useState } from "react";

function Wallet({ address }: { address: string }) {
  const { isConnected } = useSelendra();
  const { balance, loading: balanceLoading } = useBalance(address);
  const { sendTransaction, status } = useTransaction();
  const [recipient, setRecipient] = useState("");
  const [amount, setAmount] = useState("");

  const handleSend = async () => {
    if (!recipient || !amount) return;

    await sendTransaction({
      to: recipient,
      amount: (BigInt(amount) * 1_000_000_000_000n).toString(),
    });

    setRecipient("");
    setAmount("");
  };

  if (!isConnected) return <div>Connecting...</div>;
  if (balanceLoading) return <div>Loading balance...</div>;

  return (
    <div className="wallet">
      <div className="balance">
        <h3>Balance</h3>
        <p>{Number(balance) / 1e12} SEL</p>
        <p className="address">{address}</p>
      </div>

      <div className="send">
        <h3>Send Tokens</h3>
        <input
          type="text"
          placeholder="Recipient address"
          value={recipient}
          onChange={(e) => setRecipient(e.target.value)}
        />
        <input
          type="number"
          placeholder="Amount (SEL)"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
        />
        <button onClick={handleSend} disabled={status === "pending"}>
          {status === "pending" ? "Sending..." : "Send"}
        </button>
      </div>

      {status === "success" && <div className="success">Transaction sent!</div>}
      {status === "error" && <div className="error">Transaction failed</div>}
    </div>
  );
}

Error Handling

async function safeTransaction() {
  try {
    const conn = await Connection.new("wss://rpc-testnet.selendra.org");
    const keypair = keypair_from_string("//Alice");
    const signed = conn.sign(keypair);

    const txHash = await signed.transfer(recipient, amount);
    return { success: true, txHash };
  } catch (error) {
    if (error.message.includes("Inability to pay")) {
      return { success: false, error: "Insufficient balance" };
    } else if (error.message.includes("Invalid")) {
      return { success: false, error: "Invalid address format" };
    } else {
      return { success: false, error: error.message };
    }
  }
}

Complete Example

Full staking dApp with React:

import {
  SelendraProvider,
  useStaking,
  useBalance,
} from "@selendrajs/sdk/react";
import { useState } from "react";

function App() {
  return (
    <SelendraProvider endpoint="wss://rpc-testnet.selendra.org">
      <StakingApp />
    </SelendraProvider>
  );
}

function StakingApp() {
  const { bond, nominate, validate, chill, loading } = useStaking();
  const { balance } = useBalance("5GrwvaEF...");
  const [bondAmount, setBondAmount] = useState("");

  const validators = [
    "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
    "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
  ];

  return (
    <div>
      <h1>Selendra Staking</h1>

      <section className="balance">
        <h2>Account Balance</h2>
        <p>{Number(balance) / 1e12} SEL</p>
      </section>

      <section className="bond">
        <h2>Bond Tokens</h2>
        <input
          type="number"
          value={bondAmount}
          onChange={(e) => setBondAmount(e.target.value)}
          placeholder="Amount to bond"
        />
        <button
          onClick={() => bond(BigInt(bondAmount) * 1_000_000_000_000n)}
          disabled={loading}
        >
          {loading ? "Bonding..." : "Bond Tokens"}
        </button>
      </section>

      <section className="nominate">
        <h2>Nominate Validators</h2>
        <ul>
          {validators.map((v) => (
            <li key={v}>{v}</li>
          ))}
        </ul>
        <button onClick={() => nominate(validators)} disabled={loading}>
          {loading ? "Nominating..." : "Nominate"}
        </button>
      </section>

      <section className="actions">
        <button onClick={() => validate(10)} disabled={loading}>
          Become Validator (10%)
        </button>
        <button onClick={chill} disabled={loading}>
          Stop Staking
        </button>
      </section>
    </div>
  );
}

export default App;

Testing

// Use testnet for development
const TESTNET = "wss://rpc-testnet.selendra.org";
const MAINNET = "wss://rpc.selendra.org";

const conn = await Connection.new(TESTNET);
// const conn = await Connection.new(MAINNET); // Production

Next Steps

Full API Reference | React Hooks Guide | Tutorials

Contribute

Found an issue or want to contribute?

Help us improve this documentation by editing this page on GitHub.

Edit this page on GitHub