Guide
WASM module
tenrai-wasm brings the Tenrai rules for transactions, addresses, amounts and wallet keys to browsers and other WebAssembly hosts.
On this page
tenrai-wasm is the protocol code of the node, compiled to WebAssembly. It encodes and checks transactions, derives keys and addresses, and signs. It makes no network requests, stores no wallet and chooses no coins; your application does that and submits the finished transaction.
Use the SDK when you can: its wallet wraps this module and adds coin selection, fees and resending. Use the module directly when you need full control over a transaction, or a host the SDK does not cover.
Get it
The SDK package contains the module (wasm/tenrai_wasm.js, wasm/tenrai_wasm_bg.wasm) and a release.json with their SHA-256 hashes. To build it yourself from the node repository, install the wasm32-unknown-unknown target and wasm-pack, then run:
pwsh scripts/package-wasm.ps1 -Revision <git revision>The package lands in crates/wasm/pkg/.
Rules of the interface
- Every protocol integer is a decimal string. Amounts are never JavaScript numbers.
- Every transaction call takes the manifest from
GET /v1/transactions/manifestas JSON, and refuses a body bound to another chain, protocol version or rule set. networkismainnet,testnet,testnet100ordevelopment.
Build a payment
-
Describe the body and encode it with
encodeTransactionBody(requestJson, manifestJson):JSON { "valid_from_height": "40", "valid_until_height": "1840", "inputs": [{ "transaction_id": "<96 hex>", "output_index": "0" }], "outputs": [{ "address": "trai1…", "amount": "100000000" }], "payload_hex": "" }The fee is implicit: inputs minus outputs.
quoteTransactionreturns the exact mass and the minimum fee for the body. -
Get the message for each input with
spendMessage(body, index, previousOutputJson, manifest), where the previous output is{ "address": "…", "amount": "…" }. -
Sign with
WalletRoot.sign(account, branch, index, message, "spend")and wrap each signature withencodeWitness(publicKey, signature). -
Assemble the body and the witnesses, inputs first, with
assembleTransaction. -
Verify with
verifyTransaction(wire, previousOutputsJson, ownerAddress, manifest)before the transaction leaves your code. -
Submit the hex to
POST /v1/transactions.
Smaller inputs
Once an address has spent, its public key is in the chain's key registry and later inputs can refer to it instead of carrying it. Ask POST /v1/keys/registered, then quote with quoteTransactionWithInputProfiles and encode those inputs with encodeWitnessWithProfile("referenced", null, signature). Only use a key the registry reports from a finalized cut.
Bindings
| Group | Functions |
|---|---|
| Manifest | validateManifest |
| Bodies | encodeTransactionBody, inspectTransactionBody, transactionId, quoteTransaction, quoteTransactionWithInputProfiles, quoteTransactionWithWitnesses |
| Authorization | spendMessage, operationMessage, possessionMessage, encodeWitness, encodeWitnessWithProfile, encodeMultisigInlineWitness, assembleTransaction, inspectTransaction, verifyTransaction, verifyTransactionWithReferencedKeys |
| Multisig | createMultisigDescriptor, validateMultisigDescriptor, multisigAddress, createMultisigPackage, decodeMultisigPackage, reviewMultisigPackage, multisigPackageSigningMessages, addMultisigPackageSignatures, combineMultisigPackages, completeMultisigPackage |
| Text | addressFromPublicKey, parseAddress, burnAddress, parsePaymentUri, parseAmount, formatAmount |
| Recipient payloads | encodeRecipientPayloads, decodeRecipientPayloads |
| Messenger | messengerLimits, isMessengerPayload, WalletRoot (messengerId, messengerRequest, messengerAccept, messengerMessage, messengerChats) |
| Keys | mnemonicFromEntropy, mnemonicEntropy, WalletRoot (fingerprint, derivePublicKey, sign, signPossession, proveAddress) |
| Address ownership | addressVerificationWords, addressProofRequest, verifyAddressProof |
| Wallet file | sealWalletFile, openWalletFile |
| Service answers | serviceIdentityForPublicKey, verifyServiceResponse |
Keys
The mnemonic functions turn 32 bytes of entropy into the 24-word phrase and back. WalletRoot derives hardened ML-DSA-65 keys from that entropy, an optional passphrase, an account, a receive or change branch and an index. Signing draws fresh randomness inside the module. Free the WalletRoot when you are done; the module keeps no other key material.
Multisig
The module supports transparent 2-of-3 and 3-of-5 addresses. createMultisigDescriptor(threshold, membersJson) sorts the public keys and returns the descriptor, and multisigAddress(descriptorJson, network) returns its address.
For an offline signing flow, first encode the normal payment body, then call createMultisigPackage(descriptor, body, inputAmountsJson, manifest). Before any key signs, call reviewMultisigPackage(package, manifest) and show its recipients, amounts, fee, inputs, validity window, descriptor, template digest and witness_profile. Give that exact digest to multisigPackageSigningMessages; it returns the member's per-input messages. Add the resulting signatures with addMultisigPackageSignatures.
combineMultisigPackages accepts only partial packages for the same immutable template, descriptor and manifest. Once the review says it is complete, completeMultisigPackage verifies the collected threshold signatures and returns the ordinary wire transaction. createMultisigPackage makes inline packages. A referenced TRMSIGP2 package can be decoded, reviewed, signed, combined and completed without a network lookup; completion checks it locally against its descriptor keys, not that those keys are finalized in the chain registry. This package path is preferred over assembling inline threshold witnesses by hand because it binds every signature to the reviewed transaction template.
Staking operations
A body may carry one staking operation, for example a bond:
{ "type": "bond", "data": { "validator_id": "<96 hex>", "owner_address": "trai1…", "reward_address": "trai1…", "amount": "100000000000" } }The owner signs operationMessage(body, manifest) with the purpose operation. Its witness follows the input witnesses.
