| Crates.io | rose-wasm |
| lib.rs | rose-wasm |
| version | 0.1.4-nightly.17.1.a460619 |
| created_at | 2025-12-23 02:13:04.275338+00 |
| updated_at | 2025-12-23 19:26:02.072768+00 |
| description | WASM bindings for Rose wallet |
| homepage | https://nockchain.net |
| repository | https://github.com/nocktoshi/rose-rs |
| max_upload_size | |
| id | 2000624 |
| size | 133,728 |
WebAssembly bindings for the Nockchain Wallet, including cryptographic operations, transaction building, and gRPC-Web client for communicating with the Nockchain server.
cd crates/rose-wasm
wasm-pack build --target web --out-dir pkg --scope nockchain
This generates the WebAssembly module and JavaScript bindings in the pkg/ directory.
Since browsers can't directly communicate with gRPC servers, you need to run an Envoy proxy that translates gRPC-Web requests to native gRPC.
macOS (Homebrew):
brew install envoy
Linux (apt):
sudo apt-get install envoy
Docker:
docker pull envoyproxy/envoy:v1.28-latest
From the repository root:
# Using local installation
envoy -c envoy.yaml
# Using Docker
docker run --rm -it \
--network host \
-v $(pwd)/envoy.yaml:/etc/envoy/envoy.yaml \
envoyproxy/envoy:v1.28-latest
Envoy will:
http://localhost:8080 for gRPC-Web requestslocalhost:6666Make sure your Nockchain gRPC server is running on port 6666:
# From your server directory
./your-grpc-server
Serve the example HTML file with a local HTTP server:
# Using Python
python3 -m http.server 8000
# Using Node.js
npx http-server -p 8000
# Using Rust
cargo install simple-http-server
simple-http-server -p 8000
Then open your browser to:
http://localhost:8000/crates/rose-wasm/examples/grpc-web-demo.html
import init, {
GrpcClient,
deriveMasterKeyFromMnemonic,
TxBuilder,
Note,
Digest,
SpendCondition,
Pkh,
LockPrimitive,
LockTim
} from './pkg/rose_wasm.js';
// Initialize the WASM module
await init();
// Create a client pointing to your Envoy proxy
const client = new GrpcClient('http://localhost:8080');
// Get balance by wallet address
const balance = await client.getBalanceByAddress(
'6psXufjYNRxffRx72w8FF9b5MYg8TEmWq2nEFkqYm51yfqsnkJu8XqX'
);
console.log('Balance:', balance);
// Get balance by first name (note hash)
const balanceByName = await client.getBalanceByFirstName(
'2H7WHTE9dFXiGgx4J432DsCLuMovNkokfcnCGRg7utWGM9h13PgQvsH'
);
console.log('Balance by name:', balanceByName);
// ============================================================================
// Building and signing transactions
// ============================================================================
// Derive keys from mnemonic
const mnemonic = "dice domain inspire horse time...";
const masterKey = deriveMasterKeyFromMnemonic(mnemonic, "");
// Create notes from balance query (protobuf -> wasm types)
const notes = balance.notes.map((entry) => Note.fromProtobuf(entry.note));
// Create spend condition
const pubkeyHash = "your_pubkey_hash_here"; // base58 digest string
const spendCondition = new SpendCondition([
LockPrimitive.newPkh(Pkh.single(pubkeyHash)),
LockPrimitive.newTim(LockTim.coinbase()),
]);
const spendConditions = notes.map(() => spendCondition);
// Build transaction (simple spend)
const feePerWord = 2850816n;
const builder = new TxBuilder(feePerWord);
builder.simpleSpend(
notes,
spendConditions,
new Digest("recipient_address"),
1234567n, // gift
undefined, // fee_override (optional)
new Digest("refund_address"),
false // include_lock_data
);
// Sign and submit
const signingKey = masterKey.privateKey;
if (!signingKey) throw new Error('No private key available');
builder.sign(signingKey);
builder.validate();
const nockchainTx = builder.build();
const rawTx = nockchainTx.toRawTx();
const txProtobuf = rawTx.toProtobuf();
await client.sendTransaction(txProtobuf);
// Check if a transaction was accepted
const accepted = await client.transactionAccepted(rawTx.id.value);
console.log('Transaction accepted:', accepted);
GrpcClientnew GrpcClient(endpoint: string)
Creates a new gRPC-Web client.
endpoint: URL of the Envoy proxy (e.g., http://localhost:8080)getBalanceByAddress(address: string): Promise<Balance>Get the balance for a wallet address.
address: Base58-encoded wallet addressgetBalanceByFirstName(firstName: string): Promise<Balance>Get the balance for a note first name.
firstName: Base58-encoded first name hashsendTransaction(rawTx: RawTransaction): Promise<string>Send a signed transaction to the network.
rawTx: RawTransaction object (must include tx_id)transactionAccepted(txId: string): Promise<boolean>Check if a transaction has been accepted.
txId: Base58-encoded transaction IDtrue if accepted, false otherwiseThe WASM package also exposes key-derivation and signing helpers (backed by rose-crypto).
import init, { deriveMasterKeyFromMnemonic } from './pkg/rose_wasm.js';
await init();
const mnemonic =
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
const masterKey = deriveMasterKeyFromMnemonic(mnemonic, '');
// `ExtendedKey` properties are camelCase in JS
console.log('publicKey bytes:', masterKey.publicKey.length); // 97
console.log('chainCode bytes:', masterKey.chainCode.length); // 32
const child0 = masterKey.deriveChild(0);
// Important: free wasm-bindgen objects when you're done
masterKey.free();
child0.free();
import init, { signMessage, verifySignature } from './pkg/rose_wasm.js';
await init();
// privateKeyBytes: Uint8Array(32)
// publicKeyBytes: Uint8Array(97)
const sig = signMessage(privateKeyBytes, 'hello nockchain');
const ok = verifySignature(publicKeyBytes, sig, 'hello nockchain');
console.log('valid:', ok);
deriveMasterKey(seed: Uint8Array): ExtendedKeyderiveMasterKeyFromMnemonic(mnemonic: string, passphrase?: string): ExtendedKeyhashPublicKey(publicKeyBytes: Uint8Array): stringhashU64(value: number | bigint): stringhashNoun(jamBytes: Uint8Array): stringsignMessage(privateKeyBytes: Uint8Array, message: string): SignatureverifySignature(publicKeyBytes: Uint8Array, signature: Signature, message: string): booleanExtendedKey
privateKey?: Uint8Array, publicKey: Uint8Array, chainCode: Uint8ArrayderiveChild(index: number): ExtendedKey, free(): voidBrowser (WASM) → gRPC-Web (HTTP) → Envoy Proxy → gRPC Server (HTTP/2)
tonic-web-wasm-client translates calls to HTTP requests with gRPC-Web protocolMake sure Envoy is running and properly configured. The envoy.yaml file includes CORS headers.
file://)pkg/ directory contains the built WASM filesIf you encounter build errors:
# Clean and rebuild
cargo clean
wasm-pack build --target web --out-dir pkg --scope nockchain
After making changes to the Rust code:
wasm-pack build --target web --out-dir pkg --scope nockchain
If you modify .proto files, rebuild the project to regenerate the code:
cargo build
See the main repository LICENSE file.