npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yallet/rwa-sdk

v0.2.0

Published

Yallet RWA SDK - Mint encrypted invoices and assets as Solana cNFTs

Downloads

134

Readme

Yallet RWA SDK

SDK for minting encrypted RWA (Real World Assets) as Solana compressed NFTs (cNFTs).

Features

  • 🔐 End-to-end encryption using xidentity (X25519) public keys
  • 📦 Permanent storage on Arweave via ArDrive Turbo
  • 🌳 Compressed NFTs via Metaplex Bubblegum (low cost)
  • 👥 User registry for xidentity management
  • 📄 Multiple asset types: Invoices, Notes (messages), Files, Photos, Contacts
  • 🔗 Chrome extension compatible: Payloads match Yallet extension decrypt format
  • 🧩 Optional WASM: Use acegf WASM for encryption (see wasm/README.md)

Installation

npm install @yallet/rwa-sdk

Quick Start

import { createRWASDK, AssetType } from '@yallet/rwa-sdk';

// 1. Create SDK instance
const sdk = createRWASDK({
  rpcEndpoint: 'https://api.mainnet-beta.solana.com',
  network: 'mainnet',
  merkleTreeAddress: 'YOUR_MERKLE_TREE_ADDRESS',
  arweaveApiKey: 'YOUR_ARWEAVE_API_KEY', // Optional for free tier
});

// 2. Set up signer (for minting transactions)
import { keypairIdentity } from '@metaplex-foundation/umi';
sdk.setSigner(yourKeypair);

// 3. Register users with their xidentity
await sdk.registerUser({
  solanaAddress: 'CUSTOMER_WALLET_ADDRESS',
  xidentity: 'BASE64_XIDENTITY_PUBLIC_KEY', // From Yallet wallet
  name: 'Customer Name',
  registeredAt: Date.now(),
});

// 4. Mint encrypted invoice
const result = await sdk.mintInvoice(
  {
    invoiceId: 'INV-2024-001',
    amount: 1500.00,
    currency: 'USD',
    date: new Date().toISOString(),
    dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
    items: [
      { description: 'Consulting Services', quantity: 10, unitPrice: 150, amount: 1500 }
    ],
  },
  'CUSTOMER_WALLET_ADDRESS',
  { name: 'Invoice #INV-2024-001' }
);

console.log('Minted:', result.assetId);
console.log('Signature:', result.signature);

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Your Application                        │
├─────────────────────────────────────────────────────────────┤
│                     Yallet RWA SDK                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  Registry   │  │ Encryption  │  │   Minting   │         │
│  │ (xidentity) │  │   (ECIES)   │  │ (Bubblegum) │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                   ┌─────────────┐                           │
│                   │   Storage   │                           │
│                   │  (Arweave)  │                           │
│                   └─────────────┘                           │
├─────────────────────────────────────────────────────────────┤
│  Solana Blockchain        │         Arweave Network         │
│  (cNFT Ownership)         │      (Encrypted Data Storage)   │
└─────────────────────────────────────────────────────────────┘

User Registration Flow

Before minting assets to a user, they must register their xidentity:

// User provides their xidentity (from Yallet wallet)
// The xidentity is a base64-encoded X25519 public key

await sdk.registerUser({
  solanaAddress: 'USER_WALLET_ADDRESS',
  xidentity: 'ZzFXyDpLw7GMu3PiPopkM7hHBRWTupjcP7qxSJO2SHU=',
  name: 'John Doe',
  email: '[email protected]', // Optional
  registeredAt: Date.now(),
});

Getting xidentity from Yallet Wallet

Users can export their xidentity from Yallet wallet:

  1. Open Yallet → Settings → Export xidentity
  2. The xidentity is safe to share (it's a public key)
  3. Only the wallet owner can decrypt assets encrypted with their xidentity

API Reference

createRWASDK(config, options?)

Create a new SDK instance.

const sdk = createRWASDK({
  // Required
  rpcEndpoint: 'https://api.mainnet-beta.solana.com',
  network: 'mainnet', // or 'devnet'

  // Merkle Tree (required for minting)
  merkleTreeAddress: 'YOUR_TREE_ADDRESS',
  merkleTreeAuthority: 'TREE_AUTHORITY', // Optional

  // Arweave (optional)
  arweaveEndpoint: 'https://turbo.irys.xyz',
  arweaveApiKey: 'YOUR_API_KEY',

  // Backend API (optional, for tree management)
  backendApiUrl: 'https://api.yourdomain.com',
});

sdk.registerUser(user)

Register a user with their xidentity.

await sdk.registerUser({
  solanaAddress: string,  // User's Solana wallet address
  xidentity: string,      // Base64-encoded X25519 public key
  name?: string,          // Display name
  email?: string,         // Email address
  metadata?: object,      // Custom metadata
});

sdk.mintInvoice(invoice, recipientAddress, options?)

Mint an encrypted invoice to a registered user (payload matches Yallet extension decrypt format). Optionally pass pdfBase64, senderProfile, recipientProfile for extension display.

sdk.mintFile(file, recipientAddress, options?)

Mint an encrypted file (FileData: filename, mimeType, content base64, size).

sdk.mintMessage(note, recipientAddress, options?)

Mint an encrypted message/note (NoteData: title, content), with optional replyTo. Payload type is always sent (institution sends to users).

sdk.mintPhoto(photo, recipientAddress, options?)

Mint an encrypted photo (PhotoData: filename, mimeType, content base64).

Example: mintInvoice

const result = await sdk.mintInvoice(
  {
    invoiceId: 'INV-001',
    amount: 100.00,
    currency: 'USD',
    date: '2024-01-15T00:00:00Z',
    dueDate: '2024-02-15T00:00:00Z',
    description: 'Monthly subscription',
    items: [
      { description: 'Service', quantity: 1, unitPrice: 100, amount: 100 }
    ],
  },
  'RECIPIENT_ADDRESS',
  { name: 'Invoice #INV-001', pdfBase64: '...' }  // optional PDF
);

sdk.mint(request)

Mint any asset type.

const result = await sdk.mint({
  assetType: AssetType.NOTE,
  assetData: {
    title: 'Meeting Notes',
    content: 'Discussion points...',
    tags: ['meeting', 'project-x'],
  },
  recipientAddress: 'RECIPIENT_ADDRESS',
  name: 'Meeting Notes - Jan 15',
});

Asset Types

| Type | Description | Data Structure | |------|-------------|----------------| | INVOICE | Invoice | InvoiceData | | NOTE | Message / note | NoteData | | FILE | File | FileData | | PHOTO | Photo | PhotoData | | CONTACT | Contact | ContactData |

Payload builders (extension JSON–compatible)

To customize the payload before encryption, use the same builder functions the Yallet extension expects:

import {
  buildInvoicePayload,
  buildFilePayload,
  buildMessagePayload,
  buildPhotoPayload,
} from '@yallet/rwa-sdk';

const bundle = buildInvoicePayload(invoice, { pdfBase64, senderProfile, recipientProfile });
const filePayload = buildFilePayload(fileData);
const notePayload = buildMessagePayload(noteData, { replyTo: previousNftId });
const photoPayload = buildPhotoPayload(photoData);

Custom User Registry

For production, implement a database-backed registry:

import { createRWASDK } from '@yallet/rwa-sdk';

const sdk = createRWASDK(config, {
  registry: {
    type: 'http',
    httpConfig: {
      baseUrl: 'https://api.yourdomain.com/registry',
      apiKey: 'YOUR_API_KEY',
    },
  },
});

Or implement the UserRegistry interface:

import type { UserRegistry, RegisteredUser } from '@yallet/rwa-sdk';

class MyDatabaseRegistry implements UserRegistry {
  async getUser(solanaAddress: string): Promise<RegisteredUser | null> {
    // Query your database
  }
  async registerUser(user: RegisteredUser): Promise<void> {
    // Insert into database
  }
  // ... other methods
}

const sdk = createRWASDK(config, {
  registry: new MyDatabaseRegistry(),
});

Server-side Mint (Yault credential NFT)

For Yault or other apps that prefer server-side mint (no registry, no signer):

import { mintCredentialNftViaServer, RWA_UPLOAD_AND_MINT_ENDPOINTS } from '@yallet/rwa-sdk';

const result = await mintCredentialNftViaServer(
  recipientSolanaAddress,
  { user_cred, mnemonic, index, label },
  {
    xidentity: recipientXidentity,  // from recipient-addresses API
    dev: true,                      // use built-in dev endpoint
    uploadAndMintApiUrl: undefined, // override if needed
    network: 'devnet',
  }
);

Dev mode

When dev: true and uploadAndMintApiUrl is not set, the SDK uses RWA_UPLOAD_AND_MINT_ENDPOINTS.dev (api.yallet.xyz). For production, use dev: false or pass uploadAndMintApiUrl explicitly.

WASM encryption (Yallet extension–compatible)

For full compatibility with Yallet extension decryption, use acegf WASM encryption:

  1. In acegf-wallet, run wasm-pack build --target web, then copy the generated pkg/acegf_bg.wasm and pkg/acegf.js into this SDK's wasm/ directory (or your deploy path).
  2. In your app, initialize WASM and pass the encrypt function into the SDK:
import init from './wasm/acegf.js';  // or your path
import { createRWASDK, createWasmEncryptor } from '@yallet/rwa-sdk';

const acegf = await init();  // optional wasm path: init('/path/to/acegf_bg.wasm')
const sdk = createRWASDK(config, {
  wasmEncryptFn: createWasmEncryptor(acegf.acegf_encrypt_for_xidentity),
});

Without wasmEncryptFn, the SDK uses built-in pure JS ECIES; production should use WASM. See wasm/README.md.

Encryption ↔ extension decrypt (inverse)

The built-in ECIES in this SDK is aligned with dev.yallet.chrome-extension (acegf WASM) so that:

  • Encrypt (SDK / Yault): shared_secret = ECDH(ephemeral, xidentity)dh_key = SHA256(shared_secret)encrypted_aes_key = AES-GCM(dh_key).encrypt(iv, random_aes_key)encrypted_data = AES-GCM(aes_key).encrypt(iv, plaintext). Payload on Arweave: { encrypted: { ephemeral_pub, encrypted_aes_key, iv, encrypted_data }, metadata }.
  • Decrypt (extension): dh_key = SHA256(ECDH(ephemeral_pub, recipient_private))aes_key = AES-GCM(dh_key).decrypt(iv, encrypted_aes_key)plaintext = AES-GCM(aes_key).decrypt(iv, encrypted_data) → decode base64, optional gzip, then envelope.data (e.g. note { title, content, metadata }).

Envelope shape: { version, type, timestamp, uuid, schema?, previousToken?, data: asset }. The extension expects envelope.data to have e.g. title and content for notes. Server-side credential mint uses the same envelope and note shape so the extension can decrypt and show the note.

Merkle Tree Setup

Before minting, you need a Merkle tree. Create one using Metaplex:

import { createTree } from '@metaplex-foundation/mpl-bubblegum';

const { tree } = await createTree(umi, {
  maxDepth: 14,        // 16,384 NFTs capacity
  maxBufferSize: 64,
});

sdk.setTreeConfig({
  treeAddress: tree.publicKey,
  treeAuthority: umi.identity.publicKey,
  maxDepth: 14,
  capacity: 16384,
  used: 0,
  remaining: 16384,
  usagePercent: 0,
});

Error Handling

const result = await sdk.mintInvoice(invoice, recipientAddress);

if (!result.success) {
  console.error('Minting failed:', result.error);
  // Handle error
} else {
  console.log('Success! Asset ID:', result.assetId);
}

License

MIT

Links