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
Contribute
Found an issue or want to contribute?
Help us improve this documentation by editing this page on GitHub.
