RISE Logo-Light

Quickstart

Build your first realtime application in 15 minutes

Quickstart

Build a realtime payment application using RISE's Shred API in under 15 minutes. This tutorial will demonstrate instant transaction confirmations and realtime balance updates.

What We'll Build

A simple payment application that:

  • Sends instant payments with immediate confirmation
  • Displays realtime balance updates
  • Shows live transaction history
  • Demonstrates shred subscriptions

Prerequisites

  • Node.js 16+ installed
  • Basic TypeScript/JavaScript knowledge
  • A code editor (VS Code recommended)

Project Setup

Create a New Project

mkdir rise-quickstart
cd rise-quickstart
npm init -y

Install Dependencies

npm install shreds viem dotenv
npm install -D typescript tsx @types/node
pnpm add shreds viem dotenv
pnpm add -D typescript tsx @types/node
yarn add shreds viem dotenv
yarn add -D typescript tsx @types/node
bun add shreds viem dotenv
bun add -D typescript tsx @types/node

Configure TypeScript

Create tsconfig.json:

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  }
}

Set Up Environment

Create .env:

.env
# RISE Testnet RPC endpoints
RISE_RPC_URL=https://testnet.riselabs.xyz
RISE_WS_URL=wss://testnet.riselabs.xyz/ws

# Test wallet (has testnet tokens)
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

This is a well-known test private key. Never use it for real funds!

Build the Application

Create quickstart.ts:

quickstart.ts
import 'dotenv/config'
import { createWalletClient, createPublicClient, http, webSocket, parseEther, formatEther } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { riseTestnet } from 'viem/chains'
import { shredActions } from 'shreds/viem'

// Setup account from private key
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
console.log('Wallet address:', account.address)

// Create client and extend with shred actions
const client = createWalletClient({
  account,
  chain: riseTestnet,
  transport: http(process.env.RISE_RPC_URL)
}).extend(shredActions)

// Recipient address
const recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'

async function main() {
  console.log('[START] RISE Quickstart\n')

  // Step 1: Check initial balances
  console.log('[INFO] Checking initial balances...')
  const senderBalance = await client.getBalance({ address: account.address })
  const recipientBalance = await client.getBalance({ address: recipient })

  console.log(`Sender: ${formatEther(senderBalance)} ETH`)
  console.log(`Recipient: ${formatEther(recipientBalance)} ETH\n`)

  // Step 2: Subscribe to shreds for realtime updates
  console.log('[SUBSCRIBE] Subscribing to realtime updates...\n')

  const wsClient = createPublicClient({
    chain: riseTestnet,
    transport: webSocket(process.env.RISE_WS_URL)
  }).extend(shredActions)

  const unwatch = wsClient.watchShreds({
    onShred: (shred) => {
      console.log(`[SHRED] New shred detected!`)
      console.log(`   Index: ${shred.shredIndex}`)
      console.log(`   Transactions: ${shred.transactions.length}`)
      console.log(`   Timestamp: ${new Date(Number(shred.blockTimestamp) * 1000).toLocaleTimeString()}\n`)
    }
  })

  // Step 3: Send instant transaction
  console.log('[SEND] Sending 0.1 ETH with instant confirmation...')
  const startTime = Date.now()

  try {
    const hash = await client.sendTransactionSync({
      to: recipient,
      value: parseEther('0.1'),
    })

    const confirmTime = Date.now() - startTime
    console.log(`[SUCCESS] Transaction confirmed in ${confirmTime}ms!`)
    console.log(`[HASH] ${hash}\n`)

    // Step 4: Check updated balances immediately
    console.log('[INFO] Checking updated balances...')
    const newSenderBalance = await client.getBalance({ address: account.address })
    const newRecipientBalance = await client.getBalance({ address: recipient })

    console.log(`Sender: ${formatEther(newSenderBalance)} ETH`)
    console.log(`Recipient: ${formatEther(newRecipientBalance)} ETH`)

  } catch (error) {
    console.error('[ERROR] Transaction failed:', error.message)
  }

  // Keep watching for a few seconds
  console.log('\n[WATCH] Watching for more shreds (10 seconds)...')

  setTimeout(() => {
    unwatch()
    console.log('\n[COMPLETE] Quickstart complete!')
    process.exit(0)
  }, 10000)
}

// Run the quickstart
main().catch(console.error)

Run the Application

Execute the quickstart:

npx tsx quickstart.ts

You should see output like:

[START] RISE Quickstart

[INFO] Checking initial balances...
Sender: 10000.0 ETH
Recipient: 10000.0 ETH

[SUBSCRIBE] Subscribing to realtime updates...

[SEND] Sending 0.1 ETH with instant confirmation...
[SHRED] New shred detected!
   Index: 1234
   Transactions: 1
   Timestamp: 2:34:56 PM

[SUCCESS] Transaction confirmed in 4ms!
[HASH] 0x...

[INFO] Checking updated balances...
Sender: 9999.899... ETH
Recipient: 10000.1 ETH

[WATCH] Watching for more shreds (10 seconds)...

What Just Happened?

  1. Instant Confirmation: The transaction confirmed in ~4ms, not minutes
  2. Realtime Updates: Balances reflected the change immediately
  3. Shred Subscription: We received the shred notification in realtime
  4. Full Compatibility: Standard viem methods worked seamlessly

Next Steps

Enhance the Application

Try these modifications:

  1. Batch Transactions: Send multiple transactions rapidly
  2. Event Monitoring: Watch for specific smart contract events
  3. Balance Streaming: Display continuously updating balances
  4. Error Recovery: Add retry logic for failed transactions

Learn More