sdk

package
v1.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 18, 2026 License: MIT Imports: 18 Imported by: 0

README

Go Reference

Nitronode Go SDK

Renamed from "Clearnode" in v1.3.0. See MIGRATION-NITRONODE.md for image / env-var / DNS changes.

Go SDK for Nitronode payment channels providing both high-level and low-level operations in a unified client:

  • State Operations: Deposit, Withdraw, Transfer, CloseHomeChannel, Acknowledge - build and co-sign states off-chain
  • Blockchain Settlement: Checkpoint - the single entry point for all on-chain transactions
  • Low-Level Operations: Direct RPC access for custom flows and advanced use cases

Method Cheat Sheet

State Operations (Off-Chain)
client.Deposit(ctx, blockchainID, asset, amount)      // Prepare deposit state
client.Withdraw(ctx, blockchainID, asset, amount)     // Prepare withdrawal state
client.Transfer(ctx, recipientWallet, asset, amount)  // Prepare transfer state
client.CloseHomeChannel(ctx, asset)                   // Prepare finalize state
client.Acknowledge(ctx, asset)                        // Acknowledge received state
Blockchain Settlement
client.Checkpoint(ctx, asset)                         // Settle latest state on-chain
client.Challenge(ctx, state)                          // Submit on-chain challenge
client.ApproveToken(ctx, chainID, asset, amount)      // Approve ChannelHub to spend tokens
client.GetOnChainBalance(ctx, chainID, asset, wallet) // Query on-chain token balance
Node Information
client.Ping(ctx)                    // Health check
client.GetConfig(ctx)               // Node configuration
client.GetBlockchains(ctx)          // Supported blockchains
client.GetAssets(ctx, blockchainID) // Supported assets
User Queries
client.GetBalances(ctx, wallet)             // User balances
client.GetTransactions(ctx, wallet, opts)   // Transaction history
Channel Queries
client.GetHomeChannel(ctx, wallet, asset)       // Home channel info
client.GetEscrowChannel(ctx, escrowChannelID)   // Escrow channel info
client.GetLatestState(ctx, wallet, asset, onlySigned) // Latest state
App Sessions
client.GetAppSessions(ctx, opts)                              // List sessions
client.GetAppDefinition(ctx, appSessionID)                    // Session definition
client.CreateAppSession(ctx, definition, sessionData, sigs)   // Create session
client.CreateAppSession(ctx, def, data, sigs, opts)           // Create with owner approval
client.SubmitAppSessionDeposit(ctx, update, sigs, asset, amount) // Deposit to session
client.SubmitAppState(ctx, update, sigs)                      // Update session
Session Keys — App Sessions
client.SignSessionKeyState(state)                                   // Wallet UserSig over app session key state
sdk.SignAppSessionKeyOwnership(state, sessionKeySigner)             // Session-key holder's SessionKeySig
client.SubmitAppSessionKeyState(ctx, state)                         // Register/update app session key (both sigs required)
client.GetLastAppKeyStates(ctx, userAddress, opts)                  // Get app session key states (active-only by default; opts.IncludeInactive=true to include expired)
Session Keys — Channels
client.SignChannelSessionKeyState(state)                            // Wallet UserSig over channel session key state
sdk.SignChannelSessionKeyOwnership(state, sessionKeySigner)         // Session-key holder's SessionKeySig
client.SubmitChannelSessionKeyState(ctx, state)                     // Register/update channel session key (both sigs required)
client.GetLastChannelKeyStates(ctx, userAddress, opts)              // Get channel session key states (active-only by default; opts.IncludeInactive=true to include expired)
Shared Utilities
client.Close()                          // Close connection
client.WaitCh()                         // Connection monitor channel
client.SignState(state)                 // Sign a state (advanced)
client.GetUserAddress()                 // Get signer's address
client.SetHomeBlockchain(asset, chainID) // Set default blockchain for asset

Quick Start

Unified Client (High-Level + Low-Level)
package main

import (
    "context"
    "github.com/layer-3/nitrolite/pkg/core"
    "github.com/layer-3/nitrolite/pkg/sign"
    sdk "github.com/layer-3/nitrolite/sdk/go"
    "github.com/shopspring/decimal"
)

func main() {
    // Create signers from private key
    msgSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex)
    stateSigner, _ := core.NewChannelDefaultSigner(msgSigner)
    txSigner, _ := sign.NewEthereumRawSigner(privateKeyHex)

    // Create unified client
    client, _ := sdk.NewClient(
        "wss://nitronode-sandbox.yellow.org/v1/ws",
        stateSigner,
        txSigner,
        sdk.WithBlockchainRPC(80002, "https://polygon-amoy.alchemy.com/v2/KEY"),
    )
    defer client.Close()

    ctx := context.Background()

    // Step 1: Build and co-sign states off-chain
    state, _ := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100))
    fmt.Printf("Deposit state version: %d\n", state.Version)

    // Step 2: Settle on-chain via Checkpoint
    txHash, _ := client.Checkpoint(ctx, "usdc")
    fmt.Printf("On-chain tx: %s\n", txHash)

    // Transfer (off-chain only, no Checkpoint needed for existing channels)
    state, _ = client.Transfer(ctx, "0xRecipient...", "usdc", decimal.NewFromInt(50))

    // Low-level operations - same client
    config, _ := client.GetConfig(ctx)
    balances, _ := client.GetBalances(ctx, walletAddress)
}

Architecture

sdk/go/
├── client.go         # Core client, constructors, high-level operations
├── node.go           # Node information methods
├── user.go           # User query methods
├── channel.go        # Channel and state management
├── app_session.go    # App session methods
├── asset_cache.go    # Asset lookup and caching
├── config.go         # Configuration options
├── doc.go            # Package documentation
└── utils.go          # Type conversions

Client API

Creating a Client
// Step 1: Create signers from private key
msgSigner, err := sign.NewEthereumMsgSigner("0x1234...")
if err != nil {
    log.Fatal(err)
}

// Wrap with ChannelDefaultSigner (prepends 0x00 type byte)
stateSigner, err := core.NewChannelDefaultSigner(msgSigner)
if err != nil {
    log.Fatal(err)
}

txSigner, err := sign.NewEthereumRawSigner("0x1234...")
if err != nil {
    log.Fatal(err)
}

// Step 2: Create unified client
client, err := sdk.NewClient(
    wsURL,
    stateSigner,  // core.ChannelSigner for signing channel states
    txSigner,     // sign.Signer for signing blockchain transactions
    sdk.WithBlockchainRPC(chainID, rpcURL), // Required for Checkpoint
    sdk.WithHandshakeTimeout(10*time.Second),
    sdk.WithPingInterval(5*time.Second),
)

// Step 3: (Optional) Set home blockchain for assets
// Required for Transfer operations that may trigger channel creation
err = client.SetHomeBlockchain("usdc", 80002)
if err != nil {
    log.Fatal(err)
}
Configuring Home Blockchain
SetHomeBlockchain(asset, blockchainID) error

Sets the default blockchain network for a specific asset. This is required for Transfer() operations that may trigger channel creation, as Transfer doesn't accept a blockchain ID parameter.

err := client.SetHomeBlockchain("usdc", 80002)
if err != nil {
    log.Fatal(err)
}

Important Notes:

  • This mapping is immutable once set for the client instance
  • The asset must be supported on the specified blockchain
  • Required before calling Transfer() on a new channel
State Operations

All state operations build and co-sign a state off-chain. They return (*core.State, error). Use Checkpoint to settle the state on-chain.

Deposit(ctx, blockchainID, asset, amount) (*core.State, error)

Prepares a deposit state. Creates a new channel if none exists, otherwise advances the existing state.

state, err := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100))
txHash, err := client.Checkpoint(ctx, "usdc") // settle on-chain

Requirements:

  • Sufficient token balance (checked on-chain during Checkpoint)
Withdraw(ctx, blockchainID, asset, amount) (*core.State, error)

Prepares a withdrawal state to remove funds from the channel.

state, err := client.Withdraw(ctx, 80002, "usdc", decimal.NewFromInt(25))
txHash, err := client.Checkpoint(ctx, "usdc") // settle on-chain

Requirements:

  • Existing channel with sufficient balance
Transfer(ctx, recipientWallet, asset, amount) (*core.State, error)

Prepares an off-chain transfer to another wallet. For existing channels, no Checkpoint is needed.

state, err := client.Transfer(ctx, "0xRecipient...", "usdc", decimal.NewFromInt(50))

Requirements:

  • Existing channel with sufficient balance OR
  • Home blockchain configured via SetHomeBlockchain() (for new channels)
CloseHomeChannel(ctx, asset) (*core.State, error)

Prepares a finalize state to close the user's channel.

state, err := client.CloseHomeChannel(ctx, "usdc")
txHash, err := client.Checkpoint(ctx, "usdc") // close on-chain

Requirements:

  • Existing channel (user must have deposited first)
Acknowledge(ctx, asset) (*core.State, error)

Acknowledges a received state (e.g., after receiving a transfer).

state, err := client.Acknowledge(ctx, "usdc")

Requirements:

  • Home blockchain configured via SetHomeBlockchain() when no channel exists
Blockchain Settlement
Checkpoint(ctx, asset) (txHash, error)

Settles the latest co-signed state on-chain. This is the single entry point for all blockchain transactions. Based on the transition type and on-chain channel status, it calls the appropriate blockchain method:

  • Channel not on-chain (status Void): Creates the channel
  • Deposit/Withdrawal on existing channel: Checkpoints the state
  • Finalize: Closes the channel
txHash, err := client.Checkpoint(ctx, "usdc")

Confirmation delay. Checkpoint returns when the transaction is mined, not when the node has credited the result to your off-chain balance. The node applies a per-chain confirmation gate before committing any on-chain event: it waits ConfirmationDelaySecs seconds (from the matching core.Blockchain in GetConfig/GetBlockchains) after the event is observed, to guard against chain reorganizations. Until that window elapses, GetBalances will not reflect a freshly deposited amount. This is typically a few seconds where the gate is enabled, and may be set as high as the chain's hard-finality time (~13 min on Ethereum L1). A ConfirmationDelaySecs of 0 means the gate is disabled and credit is immediate. Off-chain Transfer is never gated. To wait for the credit, use client.WaitForCheckpoint(ctx, asset, txHash, nil) or poll GetBalances.

Requirements:

  • Blockchain RPC configured via WithBlockchainRPC
  • A co-signed state must exist (call Deposit, Withdraw, etc. first)
  • Sufficient gas for the blockchain transaction
Challenge(ctx, state) (txHash, error)

Submits an on-chain challenge for a channel using a co-signed state. A challenge initiates a dispute period on-chain. If the counterparty does not respond with a higher-versioned state before the challenge period expires, the channel can be closed with the challenged state.

state, err := client.GetLatestState(ctx, wallet, "usdc", true)
txHash, err := client.Challenge(ctx, *state)

Requirements:

  • Blockchain RPC configured via WithBlockchainRPC
  • State must have both user and node signatures
  • State must have a HomeChannelID
ApproveToken(ctx, chainID, asset, amount) (txHash, error)

Approves the ChannelHub contract to spend ERC-20 tokens on behalf of the user. This is required before depositing ERC-20 tokens. Native tokens (e.g., ETH) do not require approval.

txHash, err := client.ApproveToken(ctx, 80002, "usdc", decimal.NewFromInt(1000))

Requirements:

  • Blockchain RPC configured via WithBlockchainRPC
GetOnChainBalance(ctx, chainID, asset, wallet) (decimal.Decimal, error)

Queries the on-chain token balance (ERC-20 or native) for a wallet on a specific blockchain. The returned value is already adjusted for token decimals.

balance, err := client.GetOnChainBalance(ctx, 80002, "usdc", "0x1234...")
fmt.Printf("On-chain balance: %s\n", balance)

Requirements:

  • Blockchain RPC configured via WithBlockchainRPC

Low-Level API

All low-level RPC methods are available on the same Client instance.

Node Information
err := client.Ping(ctx)
config, err := client.GetConfig(ctx)
blockchains, err := client.GetBlockchains(ctx)
assets, err := client.GetAssets(ctx, &blockchainID) // or nil for all

Each core.Blockchain returned by GetConfig/GetBlockchains carries ConfirmationDelaySecs — the number of seconds the node waits after observing an on-chain event before crediting it to off-chain balances (0 = gate disabled). Use it to show users the expected wait after Checkpoint.

User Data
balances, err := client.GetBalances(ctx, wallet)
txs, meta, err := client.GetTransactions(ctx, wallet, opts)
Channel Queries
channel, err := client.GetHomeChannel(ctx, wallet, asset)
channel, err := client.GetEscrowChannel(ctx, escrowChannelID)
state, err := client.GetLatestState(ctx, wallet, asset, onlySigned)

Note: State submission and channel creation are handled internally by state operations (Deposit, Withdraw, Transfer). On-chain settlement is handled by Checkpoint.

App Sessions (Low-Level)
sessions, meta, err := client.GetAppSessions(ctx, opts)
def, err := client.GetAppDefinition(ctx, appSessionID)
sessionID, version, status, err := client.CreateAppSession(ctx, def, data, sigs)
nodeSig, err := client.SubmitAppSessionDeposit(ctx, update, sigs, asset, amount)
err := client.SubmitAppState(ctx, update, sigs)
Owner Approval for App Session Creation

When an app is registered with creationApprovalNotRequired: false, the app owner must sign the session creation request. Pass the owner's signature via CreateAppSessionOptions:

// Owner signs the create request using their app session signer
ownerSig, _ := ownerAppSessionSigner.Sign(createRequest)

sessionID, _, _, err := client.CreateAppSession(ctx, def, data, sigs,
    sdk.CreateAppSessionOptions{OwnerSig: ownerSig.String()},
)
App Session Signers (pkg/app)

App session operations require signatures with a type byte prefix, similar to channel signers:

Type Byte Constructor Usage
Wallet 0xA1 app.NewAppSessionWalletSignerV1(msgSigner) Main wallet signs app session operations
Session Key 0xA2 app.NewAppSessionKeySignerV1(msgSigner) Delegated session key signs on behalf of wallet
// Create app session wallet signer
msgSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex)
appSessionSigner, _ := app.NewAppSessionWalletSignerV1(msgSigner)

// Sign app session operations (create, deposit, state updates, etc.)
sig, _ := appSessionSigner.Sign(packedRequest)
Session Keys — App Sessions

Registration requires two signatures: the wallet's UserSig (authorizing the delegation) and the session-key holder's SessionKeySig (proving possession of the key being registered). The node rejects submits that lack a valid SessionKeySig.

// sessionKeySigner must be a *sign.EthereumMsgSigner (raw EIP-191 signer)
// whose address equals state.SessionKey — not a wrapped sign.Signer, because
// the node recovers SessionKeySig as a raw 65-byte Ethereum message signature.
sessionKeySigner, _ := sign.NewEthereumMsgSigner(sessionKeyPrivHex)
state := app.AppSessionKeyStateV1{
    UserAddress:    client.GetUserAddress(),
    SessionKey:     "0xSessionKey...",
    Version:        1,
    ApplicationIDs: []string{"app1"},
    AppSessionIDs:  []string{},
    ExpiresAt:      time.Now().Add(24 * time.Hour),
}
state.UserSig, _ = client.SignSessionKeyState(state)
state.SessionKeySig, _ = sdk.SignAppSessionKeyOwnership(state, sessionKeySigner)
err := client.SubmitAppSessionKeyState(ctx, state)

// Query app session key states (active-only by default)
states, err := client.GetLastAppKeyStates(ctx, userAddress, nil)
states, err := client.GetLastAppKeyStates(ctx, userAddress, &sdk.GetLastKeyStatesOptions{
    SessionKey: &sessionKeyAddr,
})

// Include expired/revoked latest states (e.g. for rotation flows that need the prior version)
includeInactive := true
states, err = client.GetLastAppKeyStates(ctx, userAddress, &sdk.GetLastKeyStatesOptions{
    SessionKey:      &sessionKeyAddr,
    IncludeInactive: &includeInactive,
})
Session Keys — Channels
// sessionKeySigner must be a *sign.EthereumMsgSigner (raw EIP-191 signer)
// whose address equals state.SessionKey — not a wrapped sign.Signer, because
// the node recovers SessionKeySig as a raw 65-byte Ethereum message signature.
sessionKeySigner, _ := sign.NewEthereumMsgSigner(sessionKeyPrivHex)
state := core.ChannelSessionKeyStateV1{
    UserAddress: client.GetUserAddress(),
    SessionKey:  "0xSessionKey...",
    Version:     1,
    Assets:      []string{"usdc", "weth"},
    ExpiresAt:   time.Now().Add(24 * time.Hour),
}
state.UserSig, _ = client.SignChannelSessionKeyState(state)
state.SessionKeySig, _ = sdk.SignChannelSessionKeyOwnership(state, sessionKeySigner)
err := client.SubmitChannelSessionKeyState(ctx, state)

// Query channel session key states (active-only by default)
states, err := client.GetLastChannelKeyStates(ctx, userAddress, nil)
states, err := client.GetLastChannelKeyStates(ctx, userAddress, &sdk.GetLastChannelKeyStatesOptions{
    SessionKey: &sessionKeyAddr,
})

// Include expired/revoked latest states (e.g. for rotation flows that need the prior version)
includeInactive := true
states, err = client.GetLastChannelKeyStates(ctx, userAddress, &sdk.GetLastChannelKeyStatesOptions{
    SessionKey:      &sessionKeyAddr,
    IncludeInactive: &includeInactive,
})

Key Concepts

State Management

Payment channels use versioned states signed by both user and node. The SDK uses a two-step pattern:

// Step 1: Build and co-sign state off-chain
state, _ := client.Deposit(...)   // Returns *core.State
state, _ = client.Withdraw(...)   // Returns *core.State
state, _ = client.Transfer(...)   // Returns *core.State

// Step 2: Settle on-chain (when needed)
txHash, _ := client.Checkpoint(ctx, "usdc")

State Flow (Internal):

  1. Get latest state with GetLatestState()
  2. Create next state with state.NextState()
  3. Apply transition (deposit, withdraw, transfer, etc.)
  4. Sign state with SignState()
  5. Submit to node for co-signing
  6. Return co-signed state

On-chain settlement is handled separately by Checkpoint.

Signing

States are signed using ECDSA with EIP-155 via pkg/sign:

// Create signers from private key
stateSigner, err := sign.NewEthereumMsgSigner(privateKeyHex)  // For channel states
txSigner, err := sign.NewEthereumRawSigner(privateKeyHex)     // For blockchain transactions

// Get address
address := txSigner.PublicKey().Address().String()

Signing Process:

  1. State -> ABI Encode (via core.PackState)
  2. Packed State -> Keccak256 Hash
  3. Hash -> ECDSA Sign (via signer.Sign)
  4. Result: 65-byte signature (R || S || V)

Two Signer Types:

  • EthereumMsgSigner: Signs channel state updates (off-chain signatures)
  • EthereumRawSigner: Signs blockchain transactions (on-chain operations)
Channel Signers (pkg/core)

The SDK wraps raw signers with a ChannelSigner interface that prepends a type byte to every signature. This allows the on-chain contract to dispatch signature verification to the correct validator.

// ChannelSigner interface (in pkg/core)
type ChannelSigner interface {
    sign.Signer
    Type() ChannelSignerType
}

Two channel signer types:

Type Byte Struct Usage
Default 0x00 core.ChannelDefaultSigner Main wallet signs directly. Signature = 0x00 || EIP-191 sig.
Session Key 0x01 core.ChannelSessionKeySignerV1 Delegated session key signs on behalf of main wallet. Signature = 0x01 || ABI-encoded auth + session key sig.

Creating a channel signer:

// Default signer (wraps EthereumMsgSigner with 0x00 prefix)
msgSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex)
channelSigner, _ := core.NewChannelDefaultSigner(msgSigner)

// Pass to NewClient as the stateSigner parameter
client, _ := sdk.NewClient(wsURL, channelSigner, txSigner, opts...)

The NewClient constructor expects a core.ChannelSigner for the stateSigner parameter. When using sign.NewEthereumMsgSigner directly, it must first be wrapped with core.NewChannelDefaultSigner (or core.ChannelSessionKeySignerV1 for session key operation).

Channel Lifecycle
  1. Void: No channel exists
  2. Create: Deposit creates channel on-chain
  3. Open: Channel active, can deposit/withdraw/transfer
  4. Challenged: Dispute initiated (advanced)
  5. Closed: Channel finalized (advanced)

When to Use State Operations vs Low-Level Operations

Use State Operations When:
  • Building user-facing applications
  • Need simple deposit/withdraw/transfer
  • Want automatic state management with two-step pattern
  • Don't need custom flows
Use Low-Level Operations When:
  • Building infrastructure/tooling
  • Implementing custom state transitions
  • Need fine-grained control
  • Working with app sessions directly

Error Handling

All errors include context:

state, err := client.Deposit(ctx, 80002, "usdc", amount)
if err != nil {
    log.Printf("State error: %v", err)
}

txHash, err := client.Checkpoint(ctx, "usdc")
if err != nil {
    // Error: "failed to create channel on blockchain: insufficient balance"
    log.Printf("Checkpoint error: %v", err)
}

Common errors:

  • "home blockchain not set for asset" - Missing SetHomeBlockchain for new channel creation
  • "blockchain RPC not configured for chain" - Missing WithBlockchainRPC (for Checkpoint)
  • "no channel exists for asset" - Checkpoint called without a co-signed state
  • "insufficient balance" - Not enough funds in channel/wallet
  • "failed to sign state" - Invalid private key or state
  • "transition type ... does not require a blockchain operation" - Checkpoint called on unsupported transition

Configuration Options

sdk.WithBlockchainRPC(chainID, rpcURL)    // Configure blockchain RPC (required for Checkpoint)
sdk.WithHandshakeTimeout(duration)         // Connection timeout (default: 5s)
sdk.WithPingInterval(duration)             // Keepalive interval (default: 5s)
sdk.WithErrorHandler(func(error))          // Connection error handler

Examples

App Sessions Example

Comprehensive example demonstrating app session lifecycle and operations.

See examples/app_sessions/lifecycle.go

cd examples/app_sessions
go run lifecycle.go

This example demonstrates:

  • Creating app sessions with single and multiple participants
  • Owner approval for app session creation
  • Session key delegation for app session participants
  • Depositing assets into app sessions
  • Operating on app session state (redistributing allocations)
  • Withdrawing from app sessions
  • Closing app sessions

The example walks through a complete multi-party app session scenario with three wallets.

Types

All types are imported from pkg/core and pkg/app:

// Core types
core.State           // Channel state
core.Channel         // Channel info
core.Transition      // State transition
core.Transaction     // Transaction record
core.Asset           // Asset info
core.Token           // Token implementation
core.Blockchain      // Blockchain info

// Core channel session key types
core.ChannelSessionKeyStateV1  // Channel session key state
// Fields: UserAddress, SessionKey, Version (uint64), Assets []string,
//         ExpiresAt (time.Time), UserSig string

// App session types
app.AppSessionInfoV1      // Session info
app.AppDefinitionV1       // Session definition
app.AppStateUpdateV1      // Session update
app.AppSessionKeyStateV1  // App session key state
// Fields: UserAddress, SessionKey, Version (uint64), ApplicationIDs []string,
//         AppSessionIDs []string, ExpiresAt (time.Time), UserSig string

Operation Internals

For understanding how operations work under the hood:

Deposit Flow (New Channel)
  1. Create channel definition
  2. Create void state
  3. Set home ledger (token, chain)
  4. Apply deposit transition
  5. Sign state
  6. Request channel creation from node (co-sign)
  7. Return co-signed state
Deposit Flow (Existing Channel)
  1. Get latest state
  2. Create next state
  3. Apply deposit transition
  4. Sign state
  5. Submit to node (co-sign)
  6. Return co-signed state
Withdraw Flow
  1. Get latest state
  2. Create next state
  3. Apply withdrawal transition
  4. Sign state
  5. Submit to node (co-sign)
  6. Return co-signed state
Transfer Flow
  1. Get latest state
  2. Create next state
  3. Apply transfer transition
  4. Sign state
  5. Submit to node (co-sign)
  6. Return co-signed state
CloseHomeChannel Flow
  1. Get latest state
  2. Verify channel exists
  3. Create next state
  4. Apply finalize transition
  5. Sign state
  6. Submit to node (co-sign)
  7. Return co-signed state
Checkpoint Flow
  1. Get latest signed state (both signatures)
  2. Determine blockchain ID from state's home ledger
  3. Get on-chain channel status
  4. Route based on transition type + status:
    • Void channel -> blockchainClient.Create()
    • Existing channel -> blockchainClient.Checkpoint()
    • Finalize -> blockchainClient.Close()
  5. Return transaction hash

Requirements

  • Go 1.21+
  • Running Nitronode instance
  • Blockchain RPC endpoint (for Checkpoint settlement)

License

Part of the Nitrolite project.

Documentation

Overview

Package sdk provides the official Go client for the Nitrolite Nitronode API.

The SDK offers a unified interface for interacting with Nitronode payment channels, supporting both high-level state operations and low-level RPC access. It simplifies the process of managing channel states, performing off-chain transactions, and settling on-chain when necessary.

Key Features

  • Unified Client: A single `Client` struct for all operations.
  • State Operations: High-level methods (`Deposit`, `Withdraw`, `Transfer`, `CloseHomeChannel`, `Acknowledge`) to build and co-sign channel states off-chain.
  • Blockchain Settlement: A single `Checkpoint` method to settle the latest state on-chain (creating channels, depositing, withdrawing, or finalizing).
  • Low-Level Access: Direct access to Nitronode RPC methods for advanced use cases (e.g., querying node config, balances, channel info).
  • App Sessions: Comprehensive support for creating and managing application sessions.
  • Session Keys: Support for registering and using session keys for delegated signing.

Usage

To use the SDK, create a `Client` instance with your Nitronode WebSocket URL and signers. You can configure blockchain RPCs for on-chain operations.

package main

import (
	"context"
	"log"
	"time"

	"github.com/layer-3/nitrolite/pkg/sign"
	sdk "github.com/layer-3/nitrolite/sdk/go"
	"github.com/shopspring/decimal"
)

func main() {
	// Initialize signers
	privateKeyHex := "YOUR_PRIVATE_KEY_HEX"
	stateSigner, err := sign.NewEthereumMsgSigner(privateKeyHex)
	if err != nil {
		log.Fatal(err)
	}
	txSigner, err := sign.NewEthereumRawSigner(privateKeyHex)
	if err != nil {
		log.Fatal(err)
	}

	// Create Client
	client, err := sdk.NewClient(
		"wss://nitronode-sandbox.yellow.org/v1/ws",
		stateSigner,
		txSigner,
		sdk.WithBlockchainRPC(80002, "https://rpc-endpoint.example.com"),
		sdk.WithHandshakeTimeout(10*time.Second),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	ctx := context.Background()

	// 1. Deposit (Off-chain state preparation)
	// This creates a new state reflecting the deposit intent.
	state, err := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100))
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Deposit prepared. New state version: %d", state.Version)

	// 2. Settlement (On-chain execution)
	// This submits the transaction to the blockchain to create the channel or deposit funds.
	txHash, err := client.Checkpoint(ctx, "usdc")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("On-chain transaction hash: %s", txHash)

	// 3. Transfer (Off-chain transaction)
	// Transfers are instant and don't require immediate on-chain settlement.
	_, err = client.Transfer(ctx, "0xRecipientAddress...", "usdc", decimal.NewFromInt(50))
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Transfer completed off-chain")
}

Client Configuration

The `NewClient` function accepts variadic `ClientOption` functions to customize behavior:

  • `WithBlockchainRPC(chainID, url)`: Registers an RPC endpoint for on-chain settlement.
  • `WithHandshakeTimeout(duration)`: Sets the timeout for the initial WebSocket handshake.
  • `WithPingInterval(duration)`: Sets the interval for WebSocket ping/pong keepalives.
  • `WithErrorHandler(func(error))`: Sets a callback for handling background connection errors.

Error Handling

The SDK methods return standard Go errors. Common errors to check for include connection issues, insufficient balances, or invalid state transitions. Errors from RPC calls often contain detailed messages from the Nitronode server.

Index

Constants

View Source
const (
	// DefaultChallengePeriod is the default challenge period for channels (1 day in seconds)
	DefaultChallengePeriod = 86400
)
View Source
const DefaultCheckpointPollInterval = 3 * time.Second

DefaultCheckpointPollInterval is the default interval between balance polls in WaitForCheckpoint.

View Source
const DefaultCheckpointTimeout = 2 * time.Minute

DefaultCheckpointTimeout is the default overall poll timeout in WaitForCheckpoint (applied after the confirmation-delay lower-bound wait).

Variables

View Source
var DefaultConfig = Config{
	HandshakeTimeout: 5 * time.Second,
	PingTimeout:      15 * time.Second,
	ErrorHandler:     defaultErrorHandler,
}

DefaultConfig returns the default configuration with sensible defaults.

Functions

func SignAppSessionKeyOwnership added in v1.3.0

func SignAppSessionKeyOwnership(state app.AppSessionKeyStateV1, sessionKeySigner *sign.EthereumMsgSigner) (string, error)

SignAppSessionKeyOwnership produces the session-key holder's ownership signature over the packed app-session key state. The signer must be the holder of the session key being registered; the resulting hex-encoded signature is intended to populate state.SessionKeySig before submitting via SubmitAppSessionKeyState. The packed state already binds user_address, so replay across wallets is not possible.

The parameter is narrowed to *sign.EthereumMsgSigner because the server recovers SessionKeySig under sign.TypeEthereumMsg — a broader signer interface could produce a signature without the EIP-191 prefix (or with extra wrapper bytes) that the server rejects.

func SignChannelSessionKeyOwnership added in v1.3.0

func SignChannelSessionKeyOwnership(state core.ChannelSessionKeyStateV1, sessionKeySigner *sign.EthereumMsgSigner) (string, error)

SignChannelSessionKeyOwnership produces the session-key holder's ownership signature for a channel session key registration. The signer must hold the session key; the returned hex string populates state.SessionKeySig before submit. The signed payload binds session_key into the metadata hash so a signature minted for one key cannot be replayed for another.

The parameter is narrowed to *sign.EthereumMsgSigner because the server recovers SessionKeySig under sign.TypeEthereumMsg — a broader signer interface could produce a signature without the EIP-191 prefix (or with extra wrapper bytes) that the server rejects.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client provides a unified interface for interacting with Nitronode. It combines state-building operations (Deposit, Withdraw, Transfer) with a single Checkpoint method for blockchain settlement, plus low-level RPC access for advanced use cases.

The two-step pattern for blockchain operations:

  1. Build and co-sign the state off-chain (Deposit, Withdraw, CloseHomeChannel, etc.)
  2. Settle on-chain via Checkpoint

High-level example:

stateSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex)
txSigner, _ := sign.NewEthereumRawSigner(privateKeyHex)
client, _ := sdk.NewClient(
    "wss://nitronode-sandbox.yellow.org/v1/ws",
    stateSigner,
    txSigner,
    sdk.WithBlockchainRPC(80002, "https://polygon-amoy.alchemy.com/v2/KEY"),
)
defer client.Close()

// Deposit: build state, then settle on-chain
state, _ := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100))
txHash, _ := client.Checkpoint(ctx, "usdc")

// Transfer: off-chain only, no Checkpoint needed for existing channels
state, _ = client.Transfer(ctx, "0xRecipient...", "usdc", decimal.NewFromInt(50))

// Low-level operations
config, _ := client.GetConfig(ctx)
balances, _ := client.GetBalances(ctx, walletAddress)

func NewClient

func NewClient(wsURL string, stateSigner core.ChannelSigner, rawSigner sign.Signer, opts ...Option) (*Client, error)

NewClient creates a new Nitronode client with both high-level and low-level methods. This is the recommended constructor for most use cases.

Parameters:

  • wsURL: WebSocket URL of the Nitronode server (e.g., "wss://nitronode-sandbox.yellow.org/v1/ws")
  • stateSigner: core.ChannelSigner for signing channel states (use sign.NewEthereumMsgSigner)
  • txSigner: sign.Signer for signing blockchain transactions (use sign.NewEthereumRawSigner)
  • opts: Optional configuration (WithBlockchainRPC, WithHandshakeTimeout, etc.)

Returns:

  • Configured Client ready for operations
  • Error if connection or initialization fails

Example:

stateSigner, _ := sign.NewEthereumMsgSigner(privateKeyHex)
txSigner, _ := sign.NewEthereumRawSigner(privateKeyHex)
client, err := sdk.NewClient(
    "wss://nitronode-sandbox.yellow.org/v1/ws",
    stateSigner,
    txSigner,
    sdk.WithBlockchainRPC(80002, "https://polygon-amoy.alchemy.com/v2/KEY"),
)

func (*Client) Acknowledge

func (c *Client) Acknowledge(ctx context.Context, asset string) (*core.State, error)

Acknowledge prepares an acknowledgement state for the given asset. This is used when a user receives a transfer but hasn't yet acknowledged the state, or to acknowledge channel creation without a deposit.

This method handles two scenarios automatically:

  1. If no channel exists: Creates a new channel with the acknowledgement transition
  2. If channel exists: Advances the state with an acknowledgement transition

The returned state is signed by both the user and the node.

Parameters:

  • ctx: Context for the operation
  • asset: The asset symbol to acknowledge (e.g., "usdc")

Returns:

  • The co-signed state with the acknowledgement transition applied
  • Error if the operation fails

Requirements:

  • Home blockchain must be set for the asset (use SetHomeBlockchain) when no channel exists

Example:

state, err := client.Acknowledge(ctx, "usdc")

func (*Client) ApproveToken

func (c *Client) ApproveToken(ctx context.Context, chainID uint64, asset string, amount decimal.Decimal) (string, error)

ApproveToken approves the ChannelHub contract to spend tokens on behalf of the user. This is required before depositing ERC-20 tokens. Native tokens (e.g., ETH) do not require approval and will return an error if attempted.

Parameters:

  • ctx: Context for the operation
  • chainID: The blockchain network ID (e.g., 11155111 for Sepolia)
  • asset: The asset symbol to approve (e.g., "usdc")
  • amount: The amount to approve for spending

Returns:

  • Transaction hash of the approval transaction
  • Error if the operation fails or the asset is a native token

func (*Client) Challenge

func (c *Client) Challenge(ctx context.Context, state core.State) (string, error)

Challenge submits an on-chain challenge for a channel using a co-signed state. The state must have both user and node signatures, which are validated before the challenge transaction is submitted.

A challenge initiates a dispute period on-chain. If the counterparty does not respond with a higher-versioned state before the challenge period expires, the channel can be closed with the challenged state.

Parameters:

  • ctx: Context for the operation
  • state: A co-signed state (both UserSig and NodeSig must be present)

Returns:

  • Transaction hash of the on-chain challenge transaction
  • Error if validation or submission fails

Requirements:

  • Blockchain RPC must be configured for the chain (use WithBlockchainRPC)
  • State must have both user and node signatures
  • State must have a HomeChannelID

Example:

state, err := client.GetLatestState(ctx, wallet, "usdc", true)
txHash, err := client.Challenge(ctx, *state)
fmt.Printf("Challenge transaction: %s\n", txHash)

func (*Client) Checkpoint

func (c *Client) Checkpoint(ctx context.Context, asset string) (string, error)

Checkpoint executes the blockchain transaction for the latest signed state. It fetches the latest co-signed state and, based on the transition type and on-chain channel status, calls the appropriate blockchain method.

This is the only method that interacts with the blockchain. It should be called after any state-building method (Deposit, Withdraw, CloseHomeChannel, etc.) to settle the state on-chain. It can also be used as a recovery mechanism if a previous blockchain transaction failed (e.g., due to gas issues or network problems).

Blockchain method mapping:

  • Channel not yet on-chain (status Void): Creates the channel via blockchainClient.Create
  • HomeDeposit/HomeWithdrawal on existing channel: Checkpoints via blockchainClient.Checkpoint
  • Finalize: Closes the channel via blockchainClient.Close

Parameters:

  • ctx: Context for the operation
  • asset: The asset symbol (e.g., "usdc")

Returns:

  • Transaction hash of the blockchain transaction
  • Error if the operation fails or no blockchain operation is needed

Requirements:

  • Blockchain RPC must be configured for the chain (use WithBlockchainRPC)
  • A co-signed state must exist (call Deposit, Withdraw, etc. first)

Example:

state, err := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100))
txHash, err := client.Checkpoint(ctx, "usdc")
fmt.Printf("On-chain transaction: %s\n", txHash)

func (*Client) Close

func (c *Client) Close() error

Close cleanly shuts down the client connection. It's recommended to defer this call after creating the client.

Example:

client, err := NewClient(...)
if err != nil {
    log.Fatal(err)
}
defer client.Close()

func (*Client) CloseHomeChannel

func (c *Client) CloseHomeChannel(ctx context.Context, asset string) (*core.State, error)

CloseHomeChannel prepares a finalize state to close the user's channel for a specific asset. This creates a final state with zero user balance and submits it to the node.

The returned state is signed by both the user and the node, but has not yet been submitted to the blockchain. Use Checkpoint to execute the on-chain close.

Parameters:

  • ctx: Context for the operation
  • asset: The asset symbol to close (e.g., "usdc")

Returns:

  • The co-signed finalize state ready for on-chain close
  • Error if the operation fails

Errors:

  • Returns error if no channel exists for the asset
  • Returns error if state signing or submission fails

Example:

state, err := client.CloseHomeChannel(ctx, "usdc")
txHash, err := client.Checkpoint(ctx, "usdc")
fmt.Printf("Close transaction: %s\n", txHash)

func (*Client) CreateAppSession

func (c *Client) CreateAppSession(ctx context.Context, definition app.AppDefinitionV1, sessionData string, quorumSigs []string, opts ...CreateAppSessionOptions) (string, string, string, error)

CreateAppSession creates a new application session between participants.

Parameters:

  • definition: The app definition with participants, quorum, application ID
  • sessionData: Optional JSON stringified session data
  • quorumSigs: Participant signatures for the app session creation
  • opts: Optional parameters (owner signature for apps requiring approval)

Returns:

  • AppSessionID of the created session
  • Initial version of the session
  • Status of the session
  • Error if the request fails

Example:

def := app.AppDefinitionV1{
    Application: "chess-v1",
    Participants: []app.AppParticipantV1{...},
    Quorum: 2,
    Nonce: 1,
}
sessionID, version, status, err := client.CreateAppSession(ctx, def, "{}", []string{"sig1", "sig2"})

func (*Client) Deposit

func (c *Client) Deposit(ctx context.Context, blockchainID uint64, asset string, amount decimal.Decimal) (*core.State, error)

Deposit prepares a deposit state for the user's channel. This method handles two scenarios automatically:

  1. If no channel exists: Creates a new channel with the initial deposit
  2. If channel exists: Advances the state with a deposit transition

The returned state is signed by both the user and the node, but has not yet been submitted to the blockchain. Use Checkpoint to execute the on-chain transaction.

Parameters:

  • ctx: Context for the operation
  • blockchainID: The blockchain network ID (e.g., 80002 for Polygon Amoy)
  • asset: The asset symbol to deposit (e.g., "usdc")
  • amount: The amount to deposit

Returns:

  • The co-signed state ready for on-chain checkpoint
  • Error if the operation fails

Example:

state, err := client.Deposit(ctx, 80002, "usdc", decimal.NewFromInt(100))
txHash, err := client.Checkpoint(ctx, "usdc")
fmt.Printf("Deposit transaction: %s\n", txHash)

func (*Client) GetAppDefinition

func (c *Client) GetAppDefinition(ctx context.Context, appSessionID string) (*app.AppDefinitionV1, error)

GetAppDefinition retrieves the definition for a specific app session.

Returns (nil, nil) when no app session exists for the given ID — absence is a successful response, not an error.

Parameters:

  • appSessionID: The application session ID

Returns:

  • app.AppDefinitionV1 with participants, quorum, and application info, or nil if absent
  • Error if the request fails

Example:

def, err := client.GetAppDefinition(ctx, "session123")
if err != nil { return err }
if def == nil { /* not found */ }

func (*Client) GetAppSessions

GetAppSessions retrieves application sessions with optional filtering.

Parameters:

  • opts: Optional filters (pass nil for no filters)

Returns:

  • Slice of AppSession
  • core.PaginationMetadata with pagination information
  • Error if the request fails

Example:

sessions, meta, err := client.GetAppSessions(ctx, nil)
for _, session := range sessions {
    fmt.Printf("Session %s: %d participants\n", session.AppSessionID, len(session.Participants))
}

func (*Client) GetAssets

func (c *Client) GetAssets(ctx context.Context, blockchainID *uint64) ([]core.Asset, error)

GetAssets retrieves all supported assets with optional blockchain filter.

Parameters:

  • blockchainID: Optional blockchain ID to filter assets (pass nil for all assets)

Returns:

  • Slice of Asset containing asset information and token implementations
  • Error if the request fails

Example:

assets, err := client.GetAssets(ctx, nil)
for _, asset := range assets {
    fmt.Printf("%s (%s): %d tokens\n", asset.Name, asset.Symbol, len(asset.Tokens))
}

func (*Client) GetBalances

func (c *Client) GetBalances(ctx context.Context, wallet string) ([]core.BalanceEntry, error)

GetBalances retrieves the balance information for a user.

Parameters:

  • wallet: The user's wallet address

Returns:

  • Slice of Balance containing asset balances
  • Error if the request fails

Example:

balances, err := client.GetBalances(ctx, "0x1234567890abcdef...")
for _, balance := range balances {
    fmt.Printf("%s: %s\n", balance.Asset, balance.Balance)
}

func (*Client) GetBlockchains

func (c *Client) GetBlockchains(ctx context.Context) ([]core.Blockchain, error)

GetBlockchains retrieves the list of supported blockchain networks. This is a convenience method that calls GetConfig and extracts the blockchains list.

Returns:

  • Slice of Blockchain containing name, chain ID, and contract address for each network
  • Error if the request fails

Example:

blockchains, err := client.GetBlockchains(ctx)
for _, bc := range blockchains {
    fmt.Printf("%s: %s\n", bc.Name, bc.ChannelHubAddress)
}

func (*Client) GetConfig

func (c *Client) GetConfig(ctx context.Context) (*core.NodeConfig, error)

GetConfig retrieves the nitronode configuration including node identity and supported blockchains.

Returns:

  • NodeConfig containing the node address, version, and list of supported blockchain networks
  • Error if the request fails

Example:

config, err := client.GetConfig(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Node: %s (v%s)\n", config.NodeAddress, config.NodeVersion)

func (*Client) GetConfirmationDelay added in v1.4.0

func (c *Client) GetConfirmationDelay(ctx context.Context, chainID uint64) (uint32, error)

GetConfirmationDelay returns the confirmation-gate delay, in seconds, that the node applies before crediting an on-chain event for the given chain. A return value of 0 means the gate is disabled and events are credited immediately.

This fetches the node config on each call (config is not cached on the client).

Parameters:

  • chainID: The blockchain network ID (e.g., 1 for Ethereum mainnet)

Returns:

  • Delay in seconds before off-chain credit lands; 0 if the gate is disabled
  • Error if the request fails or the chain is not present in the node config

func (*Client) GetEscrowChannel

func (c *Client) GetEscrowChannel(ctx context.Context, escrowChannelID string) (*core.Channel, error)

GetEscrowChannel retrieves escrow channel information for a specific channel ID.

Returns (nil, nil) when no escrow channel exists for the given ID — absence is a successful response, not an error.

Parameters:

  • escrowChannelID: The escrow channel ID to query

Returns:

  • Channel information for the escrow channel, or nil if absent
  • Error if the request fails

Note: when the escrow channel has been closed by the on-chain purge queue (no signed FINALIZE_ESCROW_DEPOSIT was received before expiry), StateVersion on the returned channel reflects the initiate version (N) and does not advance to the finalize version (N+1).

Example:

channel, err := client.GetEscrowChannel(ctx, "0x1234...")
if err != nil { return err }
if channel == nil { /* not found */ }

func (*Client) GetHomeChannel

func (c *Client) GetHomeChannel(ctx context.Context, wallet, asset string) (*core.Channel, error)

GetHomeChannel retrieves home channel information for a user's asset.

Returns (nil, nil) when no home channel exists for the wallet/asset pair — absence is a successful response, not an error.

Parameters:

  • wallet: The user's wallet address
  • asset: The asset symbol

Returns:

  • Channel information for the home channel, or nil if absent
  • Error if the request fails

Example:

channel, err := client.GetHomeChannel(ctx, "0x1234...", "usdc")
if err != nil { return err }
if channel == nil { /* no channel yet */ }

func (*Client) GetLastAppKeyStates

func (c *Client) GetLastAppKeyStates(ctx context.Context, userAddress string, opts *GetLastKeyStatesOptions) ([]app.AppSessionKeyStateV1, error)

GetLastAppKeyStates retrieves the latest app session key states for a user. By default only currently active (non-expired) states are returned; set opts.IncludeInactive to true to include expired or revoked latest states.

Parameters:

  • userAddress: The user's wallet address
  • opts: Optional filters (pass nil for active-only with no session-key filter)

Returns:

  • Slice of AppSessionKeyStateV1 with the latest session key states matching the filter
  • Error if the request fails

Example:

states, err := client.GetLastAppKeyStates(ctx, "0x1234...", nil)
for _, state := range states {
    fmt.Printf("Session key %s expires at %s\n", state.SessionKey, state.ExpiresAt)
}

func (*Client) GetLastChannelKeyStates

func (c *Client) GetLastChannelKeyStates(ctx context.Context, userAddress string, opts *GetLastChannelKeyStatesOptions) ([]core.ChannelSessionKeyStateV1, error)

GetLastChannelKeyStates retrieves the latest channel session key states for a user. By default only currently active (non-expired) states are returned; set opts.IncludeInactive to true to include expired or revoked latest states.

Parameters:

  • userAddress: The user's wallet address
  • opts: Optional filters (pass nil for active-only with no session-key filter)

Returns:

  • Slice of ChannelSessionKeyStateV1 with the latest session key states matching the filter
  • Error if the request fails

Example:

states, err := client.GetLastChannelKeyStates(ctx, "0x1234...", nil)
for _, state := range states {
    fmt.Printf("Session key %s expires at %s\n", state.SessionKey, state.ExpiresAt)
}

func (*Client) GetLatestState

func (c *Client) GetLatestState(ctx context.Context, wallet, asset string, onlySigned bool) (*core.State, error)

GetLatestState retrieves the latest state for a user's asset.

Returns (nil, nil) when the node has no stored state for the wallet/asset pair — absence is a successful response, not an error.

Parameters:

  • wallet: The user's wallet address
  • asset: The asset symbol (e.g., "usdc")
  • onlySigned: If true, returns only the latest signed state

Returns:

  • core.State containing all state information, or nil if absent
  • Error if the request fails

Example:

state, err := client.GetLatestState(ctx, "0x1234...", "usdc", false)
if err != nil { return err }
if state == nil { /* no state yet */ }

func (*Client) GetOnChainBalance

func (c *Client) GetOnChainBalance(ctx context.Context, chainID uint64, asset string, wallet string) (decimal.Decimal, error)

GetOnChainBalance queries the on-chain token balance (ERC-20 or native ETH) for a wallet on a specific blockchain.

Parameters:

  • ctx: Context for the operation
  • chainID: The blockchain ID to query (e.g., 80002 for Polygon Amoy)
  • asset: The asset symbol (e.g., "usdc", "weth")
  • wallet: The Ethereum address to check the balance for

Returns:

  • The token balance as a decimal (already adjusted for token decimals)
  • Error if the query fails

Requirements:

  • Blockchain RPC must be configured for the chain (use WithBlockchainRPC)

func (*Client) GetTransactions

func (c *Client) GetTransactions(ctx context.Context, wallet string, opts *GetTransactionsOptions) ([]core.Transaction, core.PaginationMetadata, error)

GetTransactions retrieves transaction history for a user with optional filtering.

Parameters:

  • wallet: The user's wallet address
  • opts: Optional filters (pass nil for no filters)

Returns:

  • Slice of Transaction
  • core.PaginationMetadata with pagination information
  • Error if the request fails

Example:

txs, meta, err := client.GetTransactions(ctx, "0x1234...", nil)
for _, tx := range txs {
    fmt.Printf("%s: %s → %s (%s %s)\n", tx.TxType, tx.FromAccount, tx.ToAccount, tx.Amount, tx.Asset)
}

func (*Client) GetUserAddress

func (c *Client) GetUserAddress() string

GetUserAddress returns the Ethereum address associated with the signer. This is useful for identifying the current user's wallet address.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

Ping checks connectivity to the nitronode server. This is useful for health checks and verifying the connection is active.

Example:

if err := client.Ping(ctx); err != nil {
    log.Printf("Server is unreachable: %v", err)
}

func (*Client) RevokeAppSessionKey added in v1.4.0

func (c *Client) RevokeAppSessionKey(ctx context.Context, state app.AppSessionKeyStateV1) error

RevokeAppSessionKey revokes an app session key using only the wallet's signature. Use it when the session-key holder cannot or will not co-sign — a lost, unavailable, or compromised delegate. The supplied state must carry the next monotonic Version (latest + 1) and an ExpiresAt at or before now; the method signs it with the wallet (UserSig) and submits with an empty SessionKeySig. The server accepts user-only signatures only on the revocation path (ExpiresAt <= now). For registration, rotation, or extension use SubmitAppSessionKeyState with both signatures.

Parameters:

  • ctx: Context for the operation
  • state: The app session key state to revoke (Version = latest + 1, ExpiresAt <= now)

Returns:

  • Error if ExpiresAt is in the future, signing fails, or the request fails

Example:

state := app.AppSessionKeyStateV1{
    UserAddress:    client.GetUserAddress(),
    SessionKey:     lostSessionKey,
    Version:        latestVersion + 1,
    ApplicationIDs: []string{},
    AppSessionIDs:  []string{},
    ExpiresAt:      time.Now(),
}
err := client.RevokeAppSessionKey(ctx, state)

func (*Client) RevokeChannelSessionKey added in v1.4.0

func (c *Client) RevokeChannelSessionKey(ctx context.Context, state core.ChannelSessionKeyStateV1) error

RevokeChannelSessionKey revokes a channel session key using only the wallet's signature. Use it when the session-key holder cannot or will not co-sign — a lost, unavailable, or compromised delegate. The supplied state must carry the next monotonic Version (latest + 1) and an ExpiresAt at or before now; the method signs it with the wallet (UserSig) and submits with an empty SessionKeySig. The server accepts user-only signatures only on the revocation path (ExpiresAt <= now). For registration, rotation, or extension use SubmitChannelSessionKeyState with both signatures.

Parameters:

  • ctx: Context for the operation
  • state: The channel session key state to revoke (Version = latest + 1, ExpiresAt <= now)

Returns:

  • Error if ExpiresAt is in the future, signing fails, or the request fails

Example:

state := core.ChannelSessionKeyStateV1{
    UserAddress: client.GetUserAddress(),
    SessionKey:  lostSessionKey,
    Version:     latestVersion + 1,
    Assets:      []string{},
    ExpiresAt:   time.Now(),
}
err := client.RevokeChannelSessionKey(ctx, state)

func (*Client) SetHomeBlockchain

func (c *Client) SetHomeBlockchain(asset string, blockchainId uint64) error

SetHomeBlockchain configures the primary blockchain network for a specific asset. This is required for operations like Transfer which may trigger channel creation but do not accept a blockchain ID as a parameter.

Validation:

  • Checks if the asset is actually supported on the specified blockchain.
  • Verifies that a home blockchain hasn't already been set for this asset.

Constraints:

  • This mapping is immutable once set for the client instance.
  • To move an asset to a different blockchain, use the Migrate() method instead.

Parameters:

  • asset: The asset symbol (e.g., "usdc")
  • blockchainId: The chain ID to associate with the asset (e.g., 80002)

Example:

// Set USDC to settle on Polygon Amoy
if err := client.SetHomeBlockchain("usdc", 80002); err != nil {
    log.Fatal(err)
}

func (*Client) SignChannelSessionKeyState

func (c *Client) SignChannelSessionKeyState(state core.ChannelSessionKeyStateV1) (string, error)

SignChannelSessionKeyState produces the wallet UserSig over the channel session key state using the client's state signer. Set the returned hex on state.UserSig before submit. The matching session-key-holder SessionKeySig must also be populated (see SignChannelSessionKeyOwnership) — submits with only one of the two are rejected.

Parameters:

  • state: The channel session key state to sign (UserSig and SessionKeySig fields are excluded from signing)

Returns:

  • The hex-encoded signature string
  • Error if signing fails

Example:

state := core.ChannelSessionKeyStateV1{
    UserAddress: client.GetUserAddress(),
    SessionKey:  "0xabcd...",
    Version:     1,
    Assets:      []string{"usdc"},
    ExpiresAt:   time.Now().Add(24 * time.Hour),
}
state.UserSig, _ = client.SignChannelSessionKeyState(state)
state.SessionKeySig, _ = sdk.SignChannelSessionKeyOwnership(state, sessionKeySigner)
err = client.SubmitChannelSessionKeyState(ctx, state)

func (*Client) SignSessionKeyState

func (c *Client) SignSessionKeyState(state app.AppSessionKeyStateV1) (string, error)

SignSessionKeyState produces the wallet UserSig over the session key state using the client's state signer. Set the returned hex on state.UserSig before submit. The matching session-key-holder SessionKeySig must also be populated (see SignAppSessionKeyOwnership) — submits with only one of the two are rejected.

Parameters:

  • state: The session key state to sign (UserSig and SessionKeySig fields are excluded from signing)

Returns:

  • The hex-encoded signature string
  • Error if signing fails

Example:

state := app.AppSessionKeyStateV1{
    UserAddress:    client.GetUserAddress(),
    SessionKey:     "0xabcd...",
    Version:        1,
    ApplicationIDs: []string{},
    AppSessionIDs:  []string{},
    ExpiresAt:      time.Now().Add(24 * time.Hour),
}
state.UserSig, _ = client.SignSessionKeyState(state)
state.SessionKeySig, _ = sdk.SignAppSessionKeyOwnership(state, sessionKeySigner)
err = client.SubmitAppSessionKeyState(ctx, state)

func (*Client) SubmitAppSessionDeposit

func (c *Client) SubmitAppSessionDeposit(ctx context.Context, appStateUpdate app.AppStateUpdateV1, quorumSigs []string, asset string, depositAmount decimal.Decimal) (string, error)

SubmitAppSessionDeposit submits a deposit to an app session. This updates both the app session state and the user's channel state.

Parameters:

  • appStateUpdate: The app state update with deposit intent
  • quorumSigs: Participant signatures for the app state update
  • userState: The user's updated channel state

Returns:

  • Node's signature for the state
  • Error if the request fails

Example:

appUpdate := app.AppStateUpdateV1{
    AppSessionID: "session123",
    Intent: app.AppStateUpdateIntentDeposit,
    Version: 2,
    Allocations: []app.AppAllocationV1{...},
}
nodeSig, err := client.SubmitAppSessionDeposit(ctx, appUpdate, []string{"sig1"}, userState)

func (*Client) SubmitAppSessionKeyState

func (c *Client) SubmitAppSessionKeyState(ctx context.Context, state app.AppSessionKeyStateV1) error

SubmitAppSessionKeyState submits a session key state for registration, update, revocation, or re-activation. The state must carry both the wallet's UserSig (authorizing the delegation) and the session-key holder's SessionKeySig (proving possession of the key) when state.ExpiresAt is in the future (registration, update, or re-activation). For revocation (state.ExpiresAt at or before now) only UserSig is required — use RevokeAppSessionKey for the wallet-only revocation of a lost, unavailable, or compromised key.

Set state.ExpiresAt to a future time to register or update the key. Set it to a value at or before time.Now() to revoke the key — the auth path filters by expires_at, so the key is deactivated immediately while keeping the same monotonic version sequence. A later submit with the next version and a future ExpiresAt re-activates the same session key address. Negative unix timestamps are rejected by the server.

Parameters:

  • state: The session key state containing delegation information

Returns:

  • Error if the request fails

Example:

state := app.AppSessionKeyStateV1{
    UserAddress:    "0x1234...",
    SessionKey:     "0xabcd...",
    Version:        1,
    ApplicationIDs: []string{"app1"},
    AppSessionIDs:  []string{},
    ExpiresAt:      time.Now().Add(24 * time.Hour),
}
state.UserSig, _ = client.SignSessionKeyState(state)
state.SessionKeySig, _ = sdk.SignAppSessionKeyOwnership(state, sessionKeySigner)
err := client.SubmitAppSessionKeyState(ctx, state)

func (*Client) SubmitAppState

func (c *Client) SubmitAppState(ctx context.Context, appStateUpdate app.AppStateUpdateV1, quorumSigs []string) error

SubmitAppState submits an app session state update. This method handles operate, withdraw, and close intents. For deposits, use SubmitAppSessionDeposit instead.

Parameters:

  • appStateUpdate: The app state update (intent: operate, withdraw, or close)
  • quorumSigs: Participant signatures for the app state update

Returns:

  • Error if the request fails

Example:

appUpdate := app.AppStateUpdateV1{
    AppSessionID: "session123",
    Intent: app.AppStateUpdateIntentOperate,
    Version: 3,
    Allocations: []app.AppAllocationV1{...},
}
err := client.SubmitAppState(ctx, appUpdate, []string{"sig1", "sig2"})

func (*Client) SubmitChannelSessionKeyState

func (c *Client) SubmitChannelSessionKeyState(ctx context.Context, state core.ChannelSessionKeyStateV1) error

SubmitChannelSessionKeyState submits a channel session key state for registration, update, revocation, or re-activation. The state must carry both the wallet's UserSig (authorizing the delegation) and the session-key holder's SessionKeySig (proving possession of the key) when state.ExpiresAt is in the future (registration, update, or re-activation). For revocation (state.ExpiresAt at or before now) only UserSig is required — use RevokeChannelSessionKey for the wallet-only revocation of a lost, unavailable, or compromised key.

Set state.ExpiresAt to a future time to register or update the key. Set it to a value at or before time.Now() to revoke the key — the auth path filters by expires_at, so the key is deactivated immediately while keeping the same monotonic version sequence. A later submit with the next version and a future ExpiresAt re-activates the same session key address. Negative unix timestamps are rejected by the server.

Parameters:

  • state: The channel session key state containing delegation information

Returns:

  • Error if the request fails

Example:

state := core.ChannelSessionKeyStateV1{
    UserAddress: "0x1234...",
    SessionKey:  "0xabcd...",
    Version:     1,
    Assets:      []string{"usdc", "weth"},
    ExpiresAt:   time.Now().Add(24 * time.Hour),
}
state.UserSig, _ = client.SignChannelSessionKeyState(state)
state.SessionKeySig, _ = sdk.SignChannelSessionKeyOwnership(state, sessionKeySigner)
err := client.SubmitChannelSessionKeyState(ctx, state)

func (*Client) Transfer

func (c *Client) Transfer(ctx context.Context, recipientWallet string, asset string, amount decimal.Decimal) (*core.State, error)

Transfer prepares a transfer state to send funds to another wallet address. This method handles two scenarios automatically:

  1. If no channel exists: Creates a new channel with the transfer transition
  2. If channel exists: Advances the state with a transfer send transition

The returned state is signed by both the user and the node. For existing channels, no blockchain interaction is needed. For new channels, use Checkpoint to create the channel on-chain.

Parameters:

  • ctx: Context for the operation
  • recipientWallet: The recipient's wallet address (e.g., "0x1234...")
  • asset: The asset symbol to transfer (e.g., "usdc")
  • amount: The amount to transfer

Returns:

  • The co-signed state with the transfer transition applied
  • Error if the operation fails

Example:

state, err := client.Transfer(ctx, "0xRecipient...", "usdc", decimal.NewFromInt(50))
fmt.Printf("Transfer tx ID: %s\n", state.Transition.TxID)

func (*Client) ValidateAndSignState added in v1.3.0

func (c *Client) ValidateAndSignState(currentState, proposedState *core.State) (string, error)

ValidateAndSignState firstly validates and then signs a channel state by packing it, hashing it, and signing the hash. Returns the signature as a hex-encoded string (with 0x prefix).

This is a low-level method exposed for advanced users who want to manually construct and sign states. Most users should use the high-level methods like Transfer, Deposit, and Withdraw instead.

func (*Client) WaitCh

func (c *Client) WaitCh() <-chan struct{}

WaitCh returns a channel that closes when the connection is lost or closed. This is useful for monitoring connection health in long-running applications.

Example:

go func() {
    <-client.WaitCh()
    log.Println("Connection closed")
}()

func (*Client) WaitForCheckpoint added in v1.4.0

func (c *Client) WaitForCheckpoint(ctx context.Context, asset, txHash string, opts *WaitForCheckpointOptions) (core.BalanceEntry, error)

WaitForCheckpoint waits until the off-chain credit for asset lands after an on-chain checkpoint transaction. Because the node applies a per-chain confirmation gate (confirmation_delay_secs) before crediting an event, the off-chain balance does not update the instant the tx receipt is mined — it updates up to confirmation_delay_secs later.

When opts.ChainID is set, the method first sleeps for that chain's confirmation delay (the lower bound), then polls GetBalances every PollInterval until the target condition is met or Timeout elapses. The provided ctx cancels the whole operation, including the lower-bound sleep.

Target condition:

  • opts.ExpectedBalance set → balance for asset is >= ExpectedBalance.
  • otherwise → balance for asset differs from the value at call time.

txHash is informational and is included in the timeout error.

func (*Client) WatchValidatorRegistered added in v1.3.0

func (c *Client) WatchValidatorRegistered(ctx context.Context, chainID uint64, fromBlock uint64) (<-chan *core.ValidatorRegisteredEvent, error)

WatchValidatorRegistered subscribes to ValidatorRegistered events on the ChannelHub contract for the given chain and delivers them on the returned channel.

Each event carries the newly registered validator ID, its contract address, and the block number at which it was emitted. App builders should alert users and prompt them to revoke ERC20 approvals granted to ChannelHub whenever an unexpected validator appears — see contracts/SECURITY.md.

Gap-free monitoring: pass fromBlock = 0 on the first call. On reconnect, pass lastEvent.BlockNumber + 1 so any events emitted during the outage are replayed before live events resume. This ensures the 1-day VALIDATOR_ACTIVATION_DELAY window is not silently shortened by network interruptions.

The channel is closed when ctx is cancelled or the subscription is lost. On a lost subscription (network drop), call WatchValidatorRegistered again with the last received BlockNumber + 1 to resubscribe without gaps.

The RPC URL configured via WithBlockchainRPC for chainID must be a WebSocket endpoint (wss:// or ws://). HTTP endpoints do not support event subscriptions and will return an error.

func (*Client) Withdraw

func (c *Client) Withdraw(ctx context.Context, blockchainID uint64, asset string, amount decimal.Decimal) (*core.State, error)

Withdraw prepares a withdrawal state to remove funds from the user's channel. This operation handles two scenarios automatically:

  1. If no channel exists: Creates a new channel with the withdrawal transition
  2. If channel exists: Advances the state with a withdrawal transition

The returned state is signed by both the user and the node, but has not yet been submitted to the blockchain. Use Checkpoint to execute the on-chain transaction.

Parameters:

  • ctx: Context for the operation
  • blockchainID: The blockchain network ID (e.g., 80002 for Polygon Amoy)
  • asset: The asset symbol to withdraw (e.g., "usdc")
  • amount: The amount to withdraw

Returns:

  • The co-signed state ready for on-chain checkpoint
  • Error if the operation fails

Example:

state, err := client.Withdraw(ctx, 80002, "usdc", decimal.NewFromInt(25))
txHash, err := client.Checkpoint(ctx, "usdc")
fmt.Printf("Withdrawal transaction: %s\n", txHash)

type Config

type Config struct {
	// URL is the WebSocket URL of the nitronode server
	URL string

	// HandshakeTimeout is the maximum time to wait for initial connection
	HandshakeTimeout time.Duration

	// PingTimeout is how long to wait for a ping from the server before considering the connection dead.
	// The server sends periodic pings to detect dead clients.
	PingTimeout time.Duration

	// ErrorHandler is called when connection errors occur
	ErrorHandler func(error)

	// BlockchainRPCs maps blockchain IDs to their RPC endpoints
	// Used by SDKClient for on-chain operations
	BlockchainRPCs map[uint64]string

	// ApplicationID is an advisory origin tag the client sends to the nitronode as
	// the "app_id" WebSocket query parameter. The nitronode stamps this value on
	// records produced by requests from this client. Empty means no tag.
	ApplicationID string
}

Config holds the configuration options for the Nitronode client.

type CreateAppSessionOptions

type CreateAppSessionOptions struct {
	// OwnerSig is the app owner's signature approving session creation.
	// Required when the app's CreationApprovalNotRequired is false.
	OwnerSig string
}

CreateAppSessionOptions contains optional parameters for CreateAppSession.

type GetAppSessionsOptions

type GetAppSessionsOptions struct {
	// AppSessionID filters by application session ID
	AppSessionID *string

	// Participant filters by participant wallet address
	Participant *string

	// Status filters by status ("open" or "closed")
	Status *string

	// Pagination parameters
	Pagination *core.PaginationParams
}

GetAppSessionsOptions contains optional filters for GetAppSessions.

type GetLastChannelKeyStatesOptions

type GetLastChannelKeyStatesOptions struct {
	// SessionKey filters by a specific session key address
	SessionKey *string
	// IncludeInactive, when set to true, includes latest states whose expires_at is in
	// the past (expired or revoked). Defaults to false (active-only) when nil or false.
	IncludeInactive *bool
}

GetLastChannelKeyStatesOptions contains optional filters for GetLastChannelKeyStates.

type GetLastKeyStatesOptions

type GetLastKeyStatesOptions struct {
	// SessionKey filters by a specific session key address
	SessionKey *string
	// IncludeInactive, when set to true, includes latest states whose expires_at is in
	// the past (expired or revoked). Defaults to false (active-only) when nil or false.
	IncludeInactive *bool
}

GetLastKeyStatesOptions contains optional filters for GetLastKeyStates.

type GetTransactionsOptions

type GetTransactionsOptions struct {
	// Asset filters by asset symbol
	Asset *string

	// Pagination parameters
	Pagination *core.PaginationParams
}

GetTransactionsOptions contains optional filters for GetTransactions.

type Option

type Option func(*Config)

Option is a functional option for configuring the Client.

func WithApplicationID added in v1.3.0

func WithApplicationID(appID string) Option

WithApplicationID sets the application ID sent to the nitronode as the "app_id" WebSocket query parameter. The nitronode treats this as an advisory origin tag on records produced by requests from this connection.

func WithBlockchainRPC

func WithBlockchainRPC(chainID uint64, rpcURL string) Option

WithBlockchainRPC returns an Option that configures a blockchain RPC client for a specific chain. This is required for the Checkpoint method which settles states on-chain.

Parameters:

Example:

client, err := sdk.NewClient(
    wsURL,
    stateSigner,
    txSigner,
    sdk.WithBlockchainRPC(80002, "https://polygon-amoy.alchemy.com/v2/KEY"),
    sdk.WithBlockchainRPC(84532, "https://base-sepolia.alchemy.com/v2/KEY"),
)

func WithErrorHandler

func WithErrorHandler(fn func(error)) Option

WithErrorHandler sets a custom error handler for connection errors. The handler is called when the connection encounters an error or is closed.

func WithHandshakeTimeout

func WithHandshakeTimeout(d time.Duration) Option

WithHandshakeTimeout sets the maximum time to wait for initial connection.

func WithPingTimeout

func WithPingTimeout(d time.Duration) Option

WithPingTimeout sets how long to wait for a ping from the server before considering the connection dead.

type WaitForCheckpointOptions added in v1.4.0

type WaitForCheckpointOptions struct {
	// ChainID, when non-nil, makes WaitForCheckpoint sleep for that chain's
	// confirmation_delay_secs before the first poll (the credit cannot arrive
	// before the gate elapses).
	ChainID *uint64

	// ExpectedBalance, when non-nil, resolves the wait once the polled balance for
	// the asset is >= this value. When nil, the wait resolves on the first balance
	// change relative to the value observed at call time.
	ExpectedBalance *decimal.Decimal

	// PollInterval between balance polls. Zero uses DefaultCheckpointPollInterval.
	PollInterval time.Duration

	// Timeout for polling after the lower-bound wait. Zero uses DefaultCheckpointTimeout.
	Timeout time.Duration
}

WaitForCheckpointOptions configures WaitForCheckpoint.

Directories

Path Synopsis
examples
app_sessions command
challenge command
round_robin command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL