@bloxbean/mesmo
v0.1.0-pre8
Published
JavaScript bindings for Cardano Client Lib (CCL) via GraalVM native library
Downloads
478
Maintainers
Readme
Mesmo
JavaScript bindings for Cardano Client Lib via the Mesmo native library, using Bun's built-in FFI.
Part of the Mesmo project. See the top-level README for the full API reference and
docs/quicktx.mdfor transaction building.
Requirements
- Bun 1.0+.
The native library is bundled inside the platform package — no separate download or
MESMO_LIB_PATH needed for an installed package.
Node.js is not supported. Node's FFI libraries (ffi-napi, koffi) crash against the GraalVM native library due to stack-boundary detection. Use Bun, whose built-in FFI works correctly. See the project
TODO.mdNon-Goals.
Installing
Recommended — a package that bundles the native library:
bun add @bloxbean/mesmoThe package ships the matching libmesmo.* under libs/, so new Mesmo() just works — nothing
else to set. At load time the bindings look for the library in this order: an explicit
new Mesmo(libPath), the MESMO_LIB_PATH env var, then the bundled libs/ copy.
Building the tarball yourself, or developing against a locally built libmesmo:
see BUILD_FROM_SOURCE.md.
Examples
The examples/ directory contains:
| File | What it shows |
|------|---------------|
| account.js | Create an account, restore from mnemonic, derive keys and a DRep ID |
| primitives.js | Mnemonics, Blake2b hashing, Ed25519 signing, address parsing/validation |
| transaction.js | Build an unsigned payment offline (QuickTx) and sign it — no node/DevKit needed |
Quick start
import { Mesmo, TESTNET } from './src/index.js';
const lib = new Mesmo(); // loads libmesmo, starts a GraalVM isolate
try {
using account = lib.accounts.create(TESTNET); // managed handle (ADR-0016)
console.log(account.info.base_address); // addr_test1...
console.log(account.exportRecoveryPhrase()); // 24-word phrase — one-shot, deliberate
} finally {
lib.close(); // tears down the isolate
}API namespaces
A Mesmo instance exposes these namespaces (all offline operations):
lib.accounts, lib.address, lib.crypto, lib.tx, lib.plutus,
lib.script, lib.quicktx.
Errors throw MesmoError; using a Mesmo after close() throws MesmoClosedError.
Networks — read this before passing a number
Every network parameter takes one of the exported constants:
| Constant | Value (CCL enum ordinal) |
|---|---|
| MAINNET | 0 |
| TESTNET | 1 |
⚠️ These are CCL's
Networkenum ordinals, NOT Cardano's on-chain network id — and they are inverted with respect to it. On-chain,0 = testnetand1 = mainnet; hereMAINNET = 0andTESTNET = 1. Solib.accounts.create(0)derives a mainnet key, not a testnet one. Never pass a raw number — always pass a constant.
network is required (there is no mainnet default), an out-of-range value throws, and the
TypeScript type is closed (type Network = 0 | 1), so create(99) will not compile:
import { Mesmo, TESTNET, MAINNET } from '@bloxbean/mesmo';
lib.accounts.create(TESTNET); // addr_test1… — on-chain network_id 0
lib.accounts.create(); // TypeError: network is required
lib.accounts.create(99); // RangeError: invalid networkThe genuine on-chain network id is the network_id field returned by address.info() — it is
not a Network ordinal and must not be fed back into create():
using acct = lib.accounts.create(MAINNET); // MAINNET is the ordinal 0 …
lib.address.info(acct.info.base_address).network_id; // … but the on-chain id is 1TypeScript
The package ships src/index.d.ts, typed against the namespaced runtime API. bun run typecheck
compiles test/types.test-d.ts against it (part of the Gradle test task), so the declarations
cannot drift from the runtime.
Transactions are defined as a TxPlan YAML document and built fully offline — you supply the UTXOs and protocol parameters:
const result = lib.quicktx.build(yaml, utxos, protocolParams); // { tx_cbor, tx_hash, fee }Chain-data providers (optional)
build() is offline — you supply the UTXOs and protocol parameters. The optional providers fetch
those for you over HTTP (Bun's built-in fetch), so the native library stays offline and
provider-free:
import { Mesmo, YaciProvider, BlockfrostProvider } from "@bloxbean/mesmo";
const lib = new Mesmo();
const provider = new BlockfrostProvider(projectId, { network: "preprod" }); // or new YaciProvider()
const result = await lib.quicktx.buildWith(yaml, provider, [senderAddress]);Plug in any backend (Koios, Ogmios, …) by supplying an object with utxos(address) and
protocolParams(). UTXO selection is handled inside Mesmo — a provider only returns all
UTXOs at the address.
Transaction evaluators (optional)
A Plutus build needs each redeemer's execution units. Mesmo computes them offline with Scalus when you supply none — so a script build just works, no evaluation step:
const result = await lib.quicktx.buildWith(yaml, provider, [senderAddress]); // Scalus computes the unitsTo use a remote evaluator instead (e.g. an authoritative fallback), pass a
TransactionEvaluator; buildWith runs a two-pass (draft → evaluate → rebuild). libmesmo never makes
HTTP calls (ADR-0013), so remote evaluation lives
here in the wrapper:
import { BlockfrostEvaluator } from "@bloxbean/mesmo";
const evaluator = new BlockfrostEvaluator(projectId, { network: "preprod" });
const result = await lib.quicktx.buildWith(yaml, provider, [senderAddress], evaluator);Plug in any evaluator (Ogmios, …) by supplying an object with evaluate(txCbor, utxos). To supply
units you computed yourself, call build(…, execUnits) directly. See examples/evaluator.js.
