# orbis1-sdk-node

Orbis1 SDK for Node.js — A modular TypeScript SDK for RGB asset operations with Bitcoin, featuring gas-free transfers and watch tower monitoring.

## Features

- **🔐 RGB Wallet Operations** — Complete RGB asset lifecycle: issue, send, receive, and manage NIA, UDA, CFA, and IFA assets
- **⚡ Gas-Free Transfers** — Transfer RGB assets without holding BTC for mining fees via collaborative transaction signing
- **🗼 Watch Tower** — Monitor RGB invoices and receive notifications for incoming transfers
- **🏗️ Modular Architecture** — Enable only the features you need; each feature is independently configurable
- **📦 Bundled Package** — Everything you need in one package: core RGB functionality and all features
- **🔒 Type Safety** — Comprehensive TypeScript types with Zod schema validation for runtime safety

## Prerequisites

- **Node.js** >= 18.0.0

## Installation

```bash
npm install orbis1-sdk-node
# or
yarn add orbis1-sdk-node
```

## Usage

### Basic Setup

```typescript
import {
  Orbis1SDK,
  generateKeys,
  Environment,
  LogLevel,
  BitcoinNetwork,
} from 'orbis1-sdk-node';

// 1. Generate or restore keys
const keys = await generateKeys(BitcoinNetwork.TESTNET4);

// 2. Configure and create SDK instance
const sdk = new Orbis1SDK({
  apiKey: 'your-api-key',
  environment: Environment.TESTNET4,
  wallet: {
    enabled: true,
    keys,
    supportedSchemas: ['NIA', 'UDA', 'CFA', 'IFA'],
  },
  features: {
    gasFree: { enabled: true },
    watchTower: { enabled: true },
  },
  logging: { level: LogLevel.INFO },
});

// 3. Initialize SDK (registers and boots all enabled features)
await sdk.initialize();

// 4. Get wallet and perform operations
const wallet = sdk.getWallet();
await wallet.goOnline('ssl://electrum.example.com:50053');
await wallet.sync();

const balance = await wallet.getBtcBalance();
const assets = await wallet.listAssets(['NIA', 'CFA']);
```

### Gas-Free Transfers

Transfer RGB assets without holding BTC for mining fees:

```typescript
// 1. Request fee quote
const transferRequest = {
  userId: 'user-123',
  assetId: 'rgb:...',
  transferAmount: 100250000,  // Amount in base units (e.g., 100.25 TUSDT with precision 6)
  recipientInvoice: 'rgb:invoice...',
}
const quote = await sdk.gasFree().requestFeeQuote(transferRequest);

console.log(`Service fee: ${quote.serviceFeeAmount} (asset units)`);
console.log(`Quote expires: ${quote.expiresAt}`);

// 2. Confirm transfer (if user accepts quote)
const result = await sdk.gasFree().confirmTransfer(transferRequest, quote);

console.log(`Transfer complete! TXID: ${result.txid}`);
console.log(`Service fee: ${result.serviceFee} base units`);
```

### Watch Tower Monitoring

Register RGB invoices with the Watch Tower service. You can attach **FCM** (mobile), **email**, and **Web Push** (browser) in any combination. Omitted fields are not sent to the API.

**FCM — default token (reused on every `addToWatchTower` unless you pass `fcmToken` in params):**

```typescript
const wt = sdk.watchTower();

wt.setFcmToken('your-fcm-device-token');

await wt.addToWatchTower({
  invoice: 'rgb:invoice...',
  // fcmToken merged from setFcmToken()
});
```

**FCM — only on this request (no `setFcmToken`):**

```typescript
await sdk.watchTower().addToWatchTower({
  invoice: 'rgb:invoice...',
  fcmToken: 'device-token-for-this-registration-only',
});
```

**Email:**

```typescript
await sdk.watchTower().addToWatchTower({
  invoice: 'rgb:invoice...',
  email: 'user@example.com',
});
```

**Web Push** — same shape as `PushSubscription.toJSON()` in the browser (`endpoint` + `keys.p256dh` / `keys.auth` as strings). Usually you subscribe in the client, POST that JSON to your server, then call the SDK:

```typescript
// Server: body.webPushNotification came from the browser’s subscription.toJSON()
await sdk.watchTower().addToWatchTower({
  invoice: 'rgb:invoice...',
  webPushNotification: body.webPushNotification,
});
```

Concrete object (when you already have string keys):

```typescript
await sdk.watchTower().addToWatchTower({
  invoice: 'rgb:invoice...',
  webPushNotification: {
    endpoint: 'https://fcm.googleapis.com/fcm/send/...',
    keys: {
      p256dh: 'BNc...',
      auth: 'tBH...',
    },
  },
});
```

**All channels together:**

```typescript
sdk.watchTower().setFcmToken('default-fcm-token');

await sdk.watchTower().addToWatchTower({
  invoice: 'rgb:invoice...',
  email: 'user@example.com',
  webPushNotification: {
    endpoint: 'https://…',
    keys: { p256dh: '…', auth: '…' },
  },
});
```

**Successful response** — `addToWatchTower` resolves to `AddToWatchTowerResponse` (HTTP 2xx). Failures throw `Error` (Axios errors include `status` and `data` on the thrown object when available).

```typescript
const res: AddToWatchTowerResponse = await sdk.watchTower().addToWatchTower({
  invoice: 'rgb:...',
});

// res.status === 'success'
// res.message — human-readable confirmation
// res.data.invoice, res.data.recipient_id, res.data.endpoint_url
// res.data.expiry — Unix timestamp (seconds) for Watch Tower registration
// res.data.fcmToken, res.data.email, res.data.webPushSubscription — echoed when registered
```

Example body:

```json
{
  "status": "success",
  "message": "Invoice added to watchtower successfully",
  "data": {
    "invoice": "rgb:~/~/~/tb4:wvout:…",
    "recipient_id": "tb4:wvout:…",
    "endpoint_url": "https://proxy.iriswallet.com/0.2/json-rpc",
    "fcmToken": "…",
    "webPushSubscription": {
      "endpoint": "https://fcm.googleapis.com/…",
      "keys": { "p256dh": "…", "auth": "…" }
    },
    "email": "user@example.com",
    "expiry": 1773998265
  }
}
```

Note: the request uses `webPushNotification`; the response echoes the stored subscription as `webPushSubscription`.

Types: `AddToWatchTowerParams`, `AddToWatchTowerResponse`, `AddToWatchTowerResponseData`, `WatchTowerWebPushSubscription`, `WatchTowerWebPushKeys`.

### Cleanup

Always cleanup when done:

```typescript
await sdk.cleanup();
```

## Project Structure

```
orbis1-sdk-node/
├── src/                        # TypeScript source
│   ├── index.tsx               # Public API exports
│   ├── Orbis1SDK.ts            # Main SDK class, feature orchestration
│   │
│   ├── core/                   # RGB core functionality
│   │   ├── Interfaces.ts       # Types, enums (BitcoinNetwork, Keys, Assets, etc.)
│   │   ├── KeyManager.ts       # generateKeys, restoreKeys, decodeInvoice
│   │   ├── NativeRgb.ts        # RGB library bindings
│   │   ├── RgbError.ts         # RGB-specific error types
│   │   └── Wallet.ts           # Wallet class (RGB operations, transfers, UTXOs)
│   │
│   ├── types/                  # SDK configuration and types
│   │   ├── SDKConfig.ts        # SDKConfig interface, Environment, LogLevel, Zod schemas
│   │   ├── IFeatureModule.ts   # Feature module interface (lifecycle hooks)
│   │   └── Feature.ts          # Feature enum (GAS_FREE, WATCH_TOWER)
│   │
│   ├── errors/                 # Error handling
│   │   ├── OrbisError.ts       # Base SDK error class
│   │   ├── ConfigurationError.ts
│   │   └── FeatureNotEnabledError.ts
│   │
│   ├── features/               # Feature modules
│   │   ├── gas-free/
│   │   │   ├── GasFreeModule.ts # Main gas-free orchestration
│   │   │   ├── client/
│   │   │   │   ├── IServiceClient.ts
│   │   │   │   └── ServiceClient.ts # HTTP client for gas-free service
│   │   │   ├── consignment/
│   │   │   │   ├── IConsignmentReader.ts
│   │   │   │   └── ConsignmentReader.ts # RGB consignment file reader
│   │   │   ├── errors/
│   │   │   │   ├── GasFreeError.ts
│   │   │   │   ├── ConsignmentVerificationError.ts
│   │   │   │   ├── InvalidPSBTError.ts
│   │   │   │   ├── QuoteExpiredError.ts
│   │   │   │   └── ServiceUnavailableError.ts
│   │   │   ├── types/
│   │   │   │   ├── GasFreeConfig.ts
│   │   │   │   ├── GasFreeRequest.ts
│   │   │   │   ├── GasFreeResult.ts
│   │   │   │   └── FeeQuote.ts
│   │   │   └── utils/
│   │   │       ├── retry.ts
│   │   │       └── validation.ts
│   │   │
│   │   └── watch-tower/
│   │       ├── WatchTowerModule.ts # Watch tower feature implementation
│   │       └── types/
│   │           ├── WatchTowerConfig.ts
│   │           ├── WatchTowerRequest.ts # addToWatchTower params & web push types
│   │           ├── WatchTowerResponse.ts # addToWatchTower success response types
│   │           └── index.ts
│   │
│   └── utils/                  # Shared utilities
│       ├── FeatureRegistry.ts  # Feature registration and lifecycle management
│       └── logger.ts           # Logging utilities
│
├── dist/                       # Build output (compiled JavaScript + types)
├── package.json                # Package manifest and dependencies
├── tsconfig.json               # TypeScript configuration
├── jest.config.js              # Jest test configuration
└── README.md
```

## Architecture Overview

| Component | Purpose |
|-----------|---------|
| **Orbis1SDK** | Main SDK class; orchestrates features, manages lifecycle, provides unified API |
| **Wallet** | RGB wallet operations (issue assets, send/receive, UTXO management, sync) |
| **GasFreeModule** | Coordinates gas-free transfers via external mining inputs and service co-signing |
| **WatchTowerModule** | Monitors RGB invoices and integrates with notifications |
| **FeatureRegistry** | Manages feature lifecycle (register, initialize, cleanup) |
| **RGB Core** | Native RGB library bindings for Node.js |

### Key Design Principles

1. **Modular Features** — Features are optional and independently configurable; enable only what you need
2. **Explicit Initialization** — Call `sdk.initialize()` before using any features; lifecycle is clear and predictable
3. **Wallet Ownership** — Consumers control wallet lifecycle (goOnline, sync, close); SDK never calls these automatically
4. **Type Safety** — Comprehensive TypeScript types with Zod schema validation for runtime safety
5. **Error Handling** — Structured error hierarchy (OrbisError → ConfigurationError, FeatureNotEnabledError, GasFreeError, etc.)

## API Reference

### Core Classes

#### `Orbis1SDK`
Main SDK entry point. Manages features and provides unified API.

**Constructor:**
```typescript
new Orbis1SDK(config: SDKConfig)
```

**Methods:**
- `initialize(): Promise<void>` — Initialize SDK and all enabled features
- `cleanup(): Promise<void>` — Cleanup resources
- `getWallet(): Wallet | undefined` — Get wallet instance
- `gasFree(): GasFreeModule` — Access gas-free feature
- `watchTower(): WatchTowerModule` — Access watch tower feature
- `hasFeature(feature: Feature): boolean` — Check if feature is enabled
- `getConfig(): Readonly<SDKConfig>` — Get SDK configuration
- `getStatus()` — Get SDK and feature status

#### `Wallet`
RGB wallet for asset operations.

**Key Methods:**
- `goOnline(indexerUrl: string): Promise<void>` — Connect to electrum indexer
- `sync(): Promise<void>` — Sync wallet with blockchain
- `getBtcBalance(): Promise<BtcBalance>` — Get BTC balance
- `listAssets(schemas: AssetSchema[]): Promise<Assets>` — List RGB assets
- `blindReceive(...)` — Generate blinded UTXO invoice
- `witnessReceive(...)` — Generate witness transaction invoice
- `send(...)` — Send RGB assets
- `issueAssetNia(...)` — Issue NIA asset
- `issueAssetUda(...)` — Issue UDA asset
- `issueAssetCfa(...)` — Issue CFA asset
- `close(): Promise<void>` — Close wallet

### Feature Modules

#### `GasFreeModule`
Gas-free transfer feature.

**Methods:**
- `requestFeeQuote(request: GasFreeTransferRequest): Promise<FeeQuote>` — Request fee quote
- `confirmTransfer(request: GasFreeTransferRequest, quote: FeeQuote): Promise<GasFreeTransferResult>` — Execute transfer
- `getState()` — Get current transfer state

#### `WatchTowerModule`
Watch tower monitoring feature (HTTP client for the Watch Tower service).

**Methods:**
- `setFcmToken(token: string | null | undefined): void` — Default FCM device token for subsequent `addToWatchTower` calls when `fcmToken` is omitted in params
- `addToWatchTower(params: AddToWatchTowerParams): Promise<AddToWatchTowerResponse>` — Register an invoice; `params` may include `invoice` (required), optional `fcmToken`, `email`, and `webPushNotification`; returns typed success body (`status`, `message`, `data`)

**Related types (exported from the package):** `AddToWatchTowerParams`, `AddToWatchTowerResponse`, `AddToWatchTowerResponseData`, `WatchTowerWebPushSubscription`, `WatchTowerWebPushKeys`, `WatchTowerConfig`

### Utility Functions

#### Key Management
```typescript
generateKeys(network: BitcoinNetwork): Promise<Keys>
restoreKeys(network: BitcoinNetwork, mnemonic: string): Promise<Keys>
decodeInvoice(invoice: string): Promise<InvoiceData>
```

## Examples

Complete working examples are available in the `example/` directory:

```bash
# Generate recipient invoice
yarn example:recipient

# Issue RGB asset and send via gas-free transfer
yarn example:user

# Send to specific recipient
yarn example:user <assetId> <recipientInvoice>

# Register an RGB invoice with Watch Tower (no wallet)
yarn example:watchtower "<rgbInvoice>"
```

See [example/README.md](example/README.md) for detailed workflow documentation.

## Development

### Setup

```bash
# Clone repository
git clone <repo-url>
cd orbis1-sdk-node

# Install dependencies
yarn install

# Build the SDK
yarn build
```

### Scripts

| Command | Description |
|---------|-------------|
| `yarn build` | Compile TypeScript to JavaScript |
| `yarn test` | Run unit tests |
| `yarn test:watch` | Run tests in watch mode |
| `yarn test:coverage` | Run tests with coverage report |
| `yarn lint` | Check code style |
| `yarn lint:fix` | Fix linting issues |
| `yarn format` | Format code with Prettier |
| `yarn typecheck` | TypeScript type checking |
| `yarn example:watchtower` | Example: register invoice with Watch Tower |

### Testing

```bash
# Run all tests
yarn test

# Run with coverage
yarn test:coverage

# Watch mode for development
yarn test:watch
```

## Environment Configuration

The SDK requires proper API key configuration based on environment:

- **Testnet**: Use API keys with prefix `pk_test`
- **Mainnet**: Use API keys with prefix `sk_live`

The SDK validates API key prefixes against the environment to prevent configuration errors.

## Error Handling

The SDK uses a structured error hierarchy:

```typescript
try {
  const result = await sdk.gasFree().confirmTransfer(request, quote);
} catch (error) {
  if (error instanceof GasFreeError) {
    console.error('Gas-free error:', error.code, error.message);
  } else if (error instanceof FeatureNotEnabledError) {
    console.error('Feature not enabled:', error.message);
  } else if (error instanceof ConfigurationError) {
    console.error('Configuration error:', error.message);
  } else {
    console.error('Unknown error:', error);
  }
}
```

## Requirements

- Node.js >= 18.0.0
- TypeScript >= 5.0.0 (if using TypeScript in your project)

## License

MIT

## Contributing

Contributions are welcome! Please read our contributing guidelines before submitting pull requests.

## Support

For issues and questions:
- GitHub Issues: [Report a bug](https://github.com/orbis/orbis1-sdk-node/issues)
- Documentation: [API Reference](#api-reference)

---

**Version:** 0.1.0  
**Last Updated:** March 10, 2026
