Skip to content
SORA Codex Community-curated documentation

Getting Started with SORA Development

Welcome to SORA development! This guide will help you set up your environment, understand the ecosystem, and start building applications on the SORA network.

DApp Developer

Build applications that interact with SORA, Polkaswap, or TONSWAP.

Smart Contract Developer

Write WASM contracts for SORA v3 (Iroha).

Integration Developer

Connect existing systems to SORA via APIs and bridges.

Infrastructure

Run nodes, validators, or indexing services.


TopicLevelWhy
JavaScript/TypeScriptIntermediateFrontend + SDK usage
Blockchain BasicsBeginnerTransaction concepts
React (optional)BeginnerFrontend development
Rust (optional)BeginnerSmart contracts (v3)
Terminal window
# Node.js 18+
node --version # v18.0.0 or higher
# pnpm (recommended) or npm
pnpm --version # 8.0.0 or higher
# Git
git --version

The fastest way to integrate with SORA:

  1. Install the SDK

    Terminal window
    pnpm add @sora-substrate/sdk @polkadot/api
  2. Initialize Connection

    import { Api } from '@sora-substrate/sdk';
    const api = new Api();
    await api.connect('wss://mof3.sora.org');
    console.log('Connected to SORA mainnet');
  3. Query Data

    // Get XOR balance
    const balance = await api.query.assets.freeBalance(
    XOR_ASSET_ID,
    accountAddress
    );
    console.log('XOR Balance:', balance.toString());
  4. Submit Transaction

    // Transfer XOR
    const tx = api.tx.assets.transfer(
    XOR_ASSET_ID,
    recipientAddress,
    amount
    );
    await tx.signAndSend(keypair);

Connect to user wallets:

import { web3Accounts, web3Enable } from '@polkadot/extension-dapp';
// Request wallet access
const extensions = await web3Enable('My SORA App');
if (extensions.length === 0) {
throw new Error('No wallet found. Install Fearless or Polkadot.js');
}
// Get accounts
const accounts = await web3Accounts();
console.log('Available accounts:', accounts);

Connect to SORA testnet for development:

const TESTNET_WS = 'wss://ws.stage.sora2.soramitsu.co.jp';
const api = new Api();
await api.connect(TESTNET_WS);

Get test tokens:

  1. Join SORA Discord
  2. Use #faucet channel
  3. Request test XOR

import { XOR, VAL, PSWAP } from '@sora-substrate/sdk/assets';
// Get asset info
const xorInfo = await api.query.assets.assetInfos(XOR);
console.log('XOR Decimals:', xorInfo.precision.toString());
// Get all balances
const balances = await api.query.assets.freeBalance.multi([
[XOR, account],
[VAL, account],
[PSWAP, account],
]);
// Register new asset (requires permission)
const registerTx = api.tx.assets.register(
symbol,
name,
totalSupply,
isMintable,
isIndivisible,
optionalContent
);
// Get quote for swap
const quote = await api.rpc.liquidityProxy.quote(
dexId,
inputAssetId,
outputAssetId,
amount,
'WithDesiredInput', // or 'WithDesiredOutput'
[], // filter sources
'Disabled' // filter mode
);
console.log('Expected output:', quote.amount.toString());
console.log('Price impact:', quote.priceImpact.toString());
// Execute swap
const swapTx = api.tx.liquidityProxy.swap(
dexId,
inputAssetId,
outputAssetId,
swapAmount,
minAmountOut,
[], // sources
'Disabled'
);
await swapTx.signAndSend(keypair);
// Add liquidity to XYK pool
const addLiquidityTx = api.tx.poolXYK.depositLiquidity(
dexId,
assetA,
assetB,
amountADesired,
amountBDesired,
amountAMin,
amountBMin
);
// Remove liquidity
const removeLiquidityTx = api.tx.poolXYK.withdrawLiquidity(
dexId,
assetA,
assetB,
markerAssetId,
liquidityAmount,
amountAMin,
amountBMin
);

// Request outgoing transfer to Ethereum
const bridgeTx = api.tx.ethBridge.transferToSidechain(
assetId,
ethRecipientAddress,
amount,
bridgeNetworkId
);
// Listen for bridge events
api.query.system.events((events) => {
events.forEach(({ event }) => {
if (event.section === 'ethBridge') {
console.log('Bridge event:', event.method, event.data);
}
});
});
// TONSWAP uses its own SDK
import { TonSwapSDK } from '@tonswap/sdk';
const tonswap = new TonSwapSDK();
// Bridge XOR to TON
await tonswap.bridge.toTon({
asset: 'XOR',
amount: '100',
tonRecipient: 'EQ...',
});

try {
const result = await tx.signAndSend(keypair, ({ status, events }) => {
if (status.isInBlock) {
console.log('Transaction in block:', status.asInBlock.toHex());
// Check for errors in events
events.forEach(({ event }) => {
if (api.events.system.ExtrinsicFailed.is(event)) {
const [error] = event.data;
if (error.isModule) {
const decoded = api.registry.findMetaError(error.asModule);
console.error('Error:', decoded.name, decoded.docs);
}
}
});
}
});
} catch (error) {
console.error('Transaction failed:', error.message);
}
PracticeReason
Batch queriesReduce RPC calls
Cache responsesAvoid redundant requests
Use subscriptionsReal-time updates
Handle reconnectsNetwork reliability
// Batch multiple queries
const [balance1, balance2, balance3] = await Promise.all([
api.query.assets.freeBalance(asset1, account),
api.query.assets.freeBalance(asset2, account),
api.query.assets.freeBalance(asset3, account),
]);
// Subscribe to changes
const unsub = await api.query.assets.freeBalance(
XOR,
account,
(balance) => {
console.log('Balance updated:', balance.toString());
}
);
// Cleanup
unsub();
  • ✅ Use wallet extensions for signing
  • ✅ Validate all user inputs
  • ✅ Handle transaction errors gracefully
  • ✅ Test on testnet first
  • ❌ Don’t hardcode private keys
  • ❌ Don’t trust client-side data

LibraryPurposeInstall
@sora-substrate/sdkCore SORA SDKpnpm add @sora-substrate/sdk
@polkadot/apiSubstrate basepnpm add @polkadot/api
@polkadot/extension-dappWallet integrationpnpm add @polkadot/extension-dapp