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

@mwaddip/cmttk

v1.0.0

Published

Cardano Minimal Transaction Toolkit — pure TypeScript, no WASM

Downloads

56

Readme

cmttk — Cardano Minimal Transaction Toolkit

Pure TypeScript Cardano transaction building. No WASM, no CML, no Lucid. ~1500 lines, 236KB compiled, bundles with esbuild to under 500KB.

What it is

cmttk handles the core operations that Cardano dApp backends need: derive wallets, query the chain, build transactions (with native tokens, Plutus scripts, inline datums, minting), sign with Ed25519, submit. It replaces Lucid Evolution, MeshJS, @cardano-sdk/*, and cardano-multiplatform-lib — a dependency tree that typically weighs 200MB and includes multiple WASM binaries that resist bundling.

What it replaces

| cmttk module | Replaces | Size comparison | |---|---|---| | cbor.ts | @emurgo/cardano-serialization-lib (CML) | 12KB vs 8MB WASM | | data.ts | Lucid's Constr, Data.to/from | 5KB vs pulled in all of CML | | tx.ts | Lucid's transaction builder | 20KB vs ~3MB bundled | | provider.ts | @lucid-evolution/provider, @blockfrost/blockfrost-js | 8KB vs 40MB SDK | | wallet.ts | @stricahq/bip32ed25519 + libsodium-wrappers-sumo | Uses noble-bip32ed25519 (4.5KB) | | time.ts | Lucid internal slot conversion | 1KB |

How it works

Cardano transactions are CBOR-encoded binary structures. Most libraries use cardano-multiplatform-lib (a Rust-compiled WASM module) to serialize them. cmttk encodes the CBOR directly in TypeScript using a minimal encoder/decoder, following the Conway-era CDDL spec.

Signing uses noble-bip32ed25519 — a pure JS implementation of BIP32-Ed25519 key derivation backed by @noble/curves. The same library provides a drop-in libsodium-wrappers-sumo shim if your dependency tree still references it transitively.

Chain queries go through Koios (free, no API key) or Blockfrost (optional, with project ID) via native fetch(). No SDK.

Installation

npm install github:mwaddip/cmttk#v1.0.0

Or add to package.json:

"cmttk": "github:mwaddip/cmttk#v1.0.0"

Quick start

Send ADA

import { deriveWallet, getProvider, buildAndSubmitTransfer } from "cmttk";

const wallet = await deriveWallet("your twenty four word mnemonic ...", "preprod");
const provider = getProvider("preprod");

const txHash = await buildAndSubmitTransfer({
  provider,
  fromAddress: wallet.address,
  toAddress: "addr_test1qz...",
  assets: { lovelace: 5_000_000n }, // 5 ADA (auto-bumped to min-UTxO if below)
  signingKey: wallet.paymentKey,
});

console.log(txHash);

Mint a token with Plutus script

import { deriveWallet, getProvider, buildAndSubmitScriptTx, Constr, Data } from "cmttk";

const wallet = await deriveWallet("your mnemonic ...", "preprod");
const provider = getProvider("preprod");

const mintRedeemer = Data.to(new Constr(0, [])); // MintNft
const datum = Data.to(new Constr(0, ["deadbeef"]));

const txHash = await buildAndSubmitScriptTx({
  provider,
  walletAddress: wallet.address,
  scriptInputs: [],
  outputs: [
    { address: wallet.address, assets: { lovelace: 2_000_000n, [policyId + assetName]: 1n } },
    { address: wallet.address, assets: { lovelace: 2_000_000n, [policyId + refName]: 1n }, datumCbor: datum },
  ],
  mints: [{
    policyId,
    assets: { [assetName]: 1n, [refName]: 1n },
    redeemerCbor: mintRedeemer,
    scriptCbor: compiledCode, // from plutus.json
  }],
  validFrom: Date.now() - 120_000,
  validTo: Date.now() + 600_000,
  network: "preprod",
  requiredSigners: [serverKeyHash],
  signingKey: wallet.paymentKey,
});

Spend a script UTXO

import { buildAndSubmitScriptTx, Constr, Data, getProvider, deriveWallet, parseKoiosUtxos } from "cmttk";

const provider = getProvider("preprod");
const wallet = await deriveWallet("your mnemonic ...", "preprod");

// Find the UTXO to spend
const rawUtxos = await provider.fetchUtxos(validatorAddress);
const utxos = parseKoiosUtxos(rawUtxos);
const target = utxos[0]; // pick your UTXO

// Decode inline datum, compute updated datum
const oldDatum = Data.from(inlineDatumHex);
const newDatum = Data.to(new Constr(0, [/* updated fields */]));

const txHash = await buildAndSubmitScriptTx({
  provider,
  walletAddress: wallet.address,
  scriptInputs: [{
    utxo: target,
    address: validatorAddress,
    redeemerCbor: Data.to(new Constr(0, [])),
  }],
  outputs: [{
    address: validatorAddress, // continuing output
    assets: { lovelace: 2_000_000n },
    datumCbor: newDatum,
  }],
  spendingScriptCbor: compiledCode,
  validFrom: Date.now() - 120_000,
  validTo: Date.now() + 600_000,
  network: "preprod",
  requiredSigners: [keyHash],
  signingKey: wallet.paymentKey,
});

Apply parameters to Aiken validators

import { applyParamsToScript } from "cmttk";
import { blake2b } from "@noble/hashes/blake2b";
import { hexToBytes, bytesToHex } from "cmttk/cbor";
import plutus from "./plutus.json" with { type: "json" };

const serverKeyHash = "2dbdd41304e95e4a1846c045328d746bf2267a0a619ec55976e7beb1";

// Apply parameters to the subscription validator (2 params)
const subValidator = plutus.validators.find(v => v.title === "subscription.subscription.spend")!;
const subCode = applyParamsToScript(subValidator.compiledCode, [serverKeyHash, serverKeyHash]);

// Compute the script hash (for deriving the validator address)
function scriptHash(compiledCode: string): string {
  const bytes = hexToBytes(compiledCode);
  const preimage = new Uint8Array(1 + bytes.length);
  preimage[0] = 0x03; // PlutusV3 prefix
  preimage.set(bytes, 1);
  return bytesToHex(blake2b(preimage, { dkLen: 28 }));
}
const subHash = scriptHash(subCode); // use as policy ID or to derive validator address

// Chain: apply subscription hash to beacon validator
const beaconValidator = plutus.validators.find(v => v.title === "beacon.beacon.mint")!;
const beaconCode = applyParamsToScript(beaconValidator.compiledCode, [subHash]);

Encode Plutus Data

import { Constr, Data, fromText } from "cmttk";

// Encode
const datum = new Constr(0, [
  42n,                           // Int
  fromText("hello"),             // ByteArray (UTF-8 → hex)
  new Constr(1, []),             // Bool True (Aiken convention)
  [new Constr(0, ["ab", "cd"])], // List of Constr
]);
const cborHex = Data.to(datum);

// Decode
const decoded = Data.from(cborHex);
// decoded is Constr { index: 0, fields: [42n, "68656c6c6f", Constr { index: 1, fields: [] }, ...] }

Migrating from Lucid / MeshJS

Replace Lucid() / MeshTxBuilder initialization with getProvider() + deriveWallet(). Replace lucid.newTx().pay.ToAddress()...complete()...sign()...submit() chains with a single buildAndSubmitTransfer() or buildAndSubmitScriptTx() call. Replace import { Constr, Data } from "@lucid-evolution/lucid" with import { Constr, Data } from "cmttk" — the API is identical. Replace getAddressDetails(addr).paymentCredential.hash with getPaymentKeyHash(addr). applyParamsToScript is a drop-in replacement for Lucid's — same signature, same behavior. Blockfrost and Koios are both supported through getProvider("preprod", blockfrostId?, koiosUrl?) with the same query interface.

What it does not do

  • Plutus script evaluation / ex-unit calculation. Execution unit budgets in redeemers are set to generous defaults. The node validates them and rejects if exceeded, but you won't get exact costs. For production, use the Koios or Blockfrost /ogmios evaluate endpoint to get precise ex-units before submission.

  • Multi-signature transactions. Only single Ed25519 signer from a BIP39 mnemonic. Multi-sig, native scripts, and hardware wallet signing are not implemented.

  • Governance. Conway-era governance actions (DRep registration, voting, proposals) are not supported.

  • Stake pool operations. Pool registration, delegation, and reward withdrawal transactions are not built.

  • Datum witness sets. Only inline datums (post-Babbage) are supported. Legacy datum-hash-based outputs are not.

  • Browser CIP-30 wallet integration. This is a backend toolkit. For browser-side CIP-30 wallet interaction, use the wallet's signTx / submitTx APIs directly with the CBOR output from the encoder.

  • Automatic UTXO management. Coin selection uses CIP-2 Random-Improve (with Largest-First fallback), which promotes healthy UTxO distribution. It does not do multi-output balancing or UTxO consolidation.

These are deliberate scope boundaries, not missing features. The toolkit handles the 90% case — query, build, sign, submit — for dApp backends interacting with Plutus validators. If you need governance transactions or hardware wallet support, use a full-featured library.

API reference

See API.md for the complete function and type reference.

Architecture

cmttk/src/
  index.ts      barrel export
  cbor.ts       CBOR encoder/decoder (all major types)
  data.ts       Plutus Data: Constr, Data.to/from, fromText, applyParamsToScript
  tx.ts         transaction builder, coin selection, fee calc
  provider.ts   Koios + Blockfrost (native fetch)
  wallet.ts     CIP-1852 key derivation from BIP39 mnemonic
  address.ts    bech32 validation, payment key hash extraction
  time.ts       slot <-> POSIX millisecond conversion
  types.ts      CardanoNetwork, AssetId

Dependencies:

Tested on-chain

All operations have been tested on Cardano preprod testnet:

  • ADA transfers (simple and with calculated fees)
  • Native token transfers
  • Plan creation with inline datums
  • Reference script deployment (PlutusV3)
  • Beacon token minting (with Plutus redeemer)
  • Subscription UTXO creation at validator address
  • ServiceCollect spending (continuing output with updated datum)
  • CIP-68 NFT minting (user + reference tokens)

Contributing

This is a small, focused library. If it solves your problem, use it. If it's missing something you need, PRs are welcome. If you disagree with a design decision, fork it — the codebase is small enough to own entirely.

Issues and pull requests: github.com/mwaddip/cmttk

License

MIT