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

@opcat-labs/lambit

v0.1.0

Published

Lambit — a functional TypeScript-to-Bitcoin-Script language and compiler

Readme

Lambit

Functional TypeScript to Bitcoin Script compiler and native runtime.

Lambit lets you define contracts as TypeScript expression trees, compile them into artifact-style JSON (ABI + locking script hex), and exercise deploy/call flows through a provider-driven runtime.

Status

This repository is a 0.1.0 / 0.1.x developer release focused on the functional DSL, compiler pipeline, artifact output, scaffolded project workflow, and provider-driven native runtime.

The public docs focus on contract authoring, local testing, deployment, debugging, and examples. Internal release and parity tracking stays out of the user-facing documentation.

Install

npm install

CLI

Use the Lambit CLI for the common compile, artifact, scaffold, and test workflows:

lambit compile ./contracts/p2pkh.ts --out-dir artifacts
lambit artifact ./contracts/p2pkh.ts --export P2PKH --output artifacts/P2PKH.json
lambit scaffold ./my-lambit-app
lambit test
lambit test --testnet
  • compile loads compatible contract exports from a TS/JS module and writes one artifact JSON file per export.
  • artifact prints or writes a single artifact JSON payload, which is useful for inspection or scripting.
  • scaffold generates a starter project with a contract example and ready-to-run package scripts.
  • test wraps this repo's Mocha setup when run inside this checkout. Outside the package, it lets Mocha discover the current project's config unless you pass --config or --no-config.
  • test forwards extra arguments to Mocha, including --help; use lambit help test for the CLI command options and lambit test --help for Mocha help.
  • For deterministic project-local test runs outside this repo, prefer lambit test --config <path> or lambit test --no-config; both paths load TypeScript tests directly when the selected Mocha config does not already load tsx, and --no-config also disables Mocha config discovery.
  • compile and artifact execute the target module. Only run them on trusted code.
  • Loading .ts contract modules uses the optional packaged tsx dependency, or a compatible tsx version from the current project when one is installed. If optional dependencies were omitted, install tsx before using TypeScript modules with the CLI.

Scaffold a New Project

Generate a starter app from the template shipped in the package:

lambit scaffold ./my-lambit-app

Run lambit scaffold --help for the scaffold command options. In this source checkout, npm run scaffold -- ./my-lambit-app runs the same scaffold implementation after npm install. By default, the generated package.json depends on the current repo version range, for example ^0.1.0. When testing against a local tarball or branch build, override that dependency explicitly:

npm run build
npm pack
npm run scaffold -- ./my-lambit-app --lambit file:/absolute/path/to/opcat-labs-lambit-0.1.0.tgz

Only pass trusted package specifiers to --lambit, because the scaffolded project will install exactly what you provide. Generated projects are private by default to prevent accidental publication. Pass --public if you want the generated package.json to be publishable:

npm run scaffold -- ./my-lambit-lib --public

The scaffolded project includes:

src/
  contracts/
    counter.ts
  index.ts
package.json
tsconfig.json
README.md
.gitignore

Run npm run contracts:compile in the scaffolded project to generate artifacts/Counter.json.

Defaults:

  • ESM TypeScript output in dist/
  • strict TypeScript enabled
  • private: true in package.json unless --public is passed
  • a minimal stateful Counter contract example
  • build, contracts:compile, contracts:artifact, and start scripts ready to run after npm install

Build

npm run build

Test

lambit test

npm test remains available and runs lambit test, including the scaffold integration suite in this source checkout. In this source checkout, npm scripts execute src/cli.ts through tsx; run npm run build before validating the published dist/cli.js binary.

For the guarded OpcatLayer testnet suite, run lambit test --testnet or npm run test:testnet.

Live Testnet E2E

  • Guard: the live suite skips unless LAMBIT_RUN_TESTNET_E2E=1 is set.
  • Wallet: fund a dedicated TESTNET_WIF, then run npm run print:testnet-address to confirm the funding address.
  • Endpoint: set TESTNET_API_BASE_URL only when you need a custom OpcatLayer testnet API.
  • Coverage: the suite deploys and spends funded-provider-compatible native artifacts, including HTLC and RollupStats checks. AtomicSwap's stateless assertOutputs paths are guarded as funded-provider rejections until an unfunded exact-output provider exists.
  • State: the Counter flow leaves the latest contract UTXO live on testnet for later inspection.
  • Focused AtomicSwap guard: run npm run test:testnet:atomicswap. It validates live configuration, locktime shape, and the funded-provider rejection before broadcast; it does not deploy AtomicSwap while the current live provider is funded.
  • GitHub Actions runs the same npm run test:testnet target from .github/workflows/testnet-e2e.yml, but only on trusted contexts: pushes to main and manual workflow_dispatch.
  • The workflow uses the protected GitHub environment opcat-testnet-e2e. Store the funded wallet as the TESTNET_WIF environment secret there, and set TESTNET_API_BASE_URL as an environment variable only if you need a non-default provider endpoint.
  • The guarded workflow sets LAMBIT_RUN_TESTNET_E2E=1 and is serialized with a single concurrency group and cancel-in-progress: false, so two runs do not try to spend the same funded wallet at once and later runs stay queued behind any stuck or long-running job.
  • Re-run intentionally from the Actions tab by choosing Guarded Testnet E2E and Run workflow, or by using GitHub's built-in re-run controls on a completed run.
  • CI logs print the funding address, API base URL, deploy txids, spend txids, and expected rejection context so failures are diagnosable from the run log.
  • lambit test / npm test do not run the live OpcatLayer testnet suite. Run lambit test --testnet or npm run test:testnet explicitly when you want live testnet coverage.
  • When LAMBIT_RUN_TESTNET_E2E=1, TESTNET_WIF must be set to a funded testnet WIF or the command fails fast with an actionable error.
  • getWifAddress(TESTNET_WIF, 'testnet') returns the base58 funding address for that WIF.
  • The same funded WIF pays testnet deploy/call fees and signs the P2PKH spend path.
  • Set TESTNET_API_BASE_URL to target a non-default OpcatLayer API endpoint; the logged endpoint is normalized with /api appended when runtime normalization would do so, and query parameters are omitted from logs so credentials are not printed.
  • The guarded live target covers P2PKH deploy+spend, Counter deploy+increase, chained Counter state rejection, AuctionProof deploy+bid, HTLC claim/refund/cooperative paths, and RollupStats multi-field state transitions via Lambit only.
  • The Counter flow intentionally leaves the latest contract UTXO live on testnet, so repeated runs consume funded balance unless you spend or rotate that wallet.
  • npm run test:testnet:auction is the focused live repro for the single Auction deploy + bid() proof test.
  • The focused Auction command uses a Node runner to set LAMBIT_RUN_TESTNET_E2E=1 for its Mocha run and exits non-zero when TESTNET_WIF is unset or the @auction-proof test tag matches zero tests; that is intentional so a skipped live suite cannot look like an auditable proof run.
  • The focused Auction runner waits synchronously for Mocha and enforces a 7-minute parent-process timeout; a timeout or kill signal is reported as a failed proof run.
  • The focused Auction proof fixture in this repo is intentionally bid-only; settlement/close semantics are not modeled in this proof path.
  • Avoid putting raw WIFs in shell history. Prefer read -s or another local secret-loading flow before running the suite.
npm run build
read -s TESTNET_WIF
TESTNET_WIF="$TESTNET_WIF" npm run print:testnet-address
LAMBIT_RUN_TESTNET_E2E=1 TESTNET_WIF="$TESTNET_WIF" lambit test --testnet
LAMBIT_RUN_TESTNET_E2E=1 TESTNET_WIF="$TESTNET_WIF" npm run test:testnet
LAMBIT_RUN_TESTNET_E2E=1 TESTNET_WIF="$TESTNET_WIF" npm run test:testnet:auction
LAMBIT_RUN_TESTNET_E2E=1 TESTNET_WIF="$TESTNET_WIF" TESTNET_AUDIT_LOG=docs/logs/atomicswap.ndjson npm run test:testnet:atomicswap
unset TESTNET_WIF

Use How to Deploy & Call for native deploy/call, funding, endpoint overrides, and testnet gotchas before repeated live runs.

The focused Auction repro prints machine-readable proof lines like:

[testnet-proof] auction.deploy txid=... vout=0 highestBid=1 highestBidder=...
[testnet-proof] auction.bid txid=... spentTxid=... successorVout=0 highestBid=5 highestBidder=...

Capture those lines in your run log so the live proof remains auditable after the funded testnet spend.

Documentation Map

Repository docs:

README sections:

Concepts

  • Contracts are authored as expression trees (ExprNode) rather than raw script opcodes.
  • contract(name, props, body) and contract(name, props, state, body) return named, callable contract definitions.
  • method(schema, fn) and method.named(schema, (args) => ...) make the method schema the ABI source of truth.
  • Declared constructor props and method params are preserved in artifact.abi with their names and types.
  • Calling a contract def binds props and returns a BoundContract with artifact, methods, and init().
  • buildArtifact(def) and createInstance(...) still exist as lower-level helpers, but the bound-contract flow is the documented default.

Quick Start: Stateless Contract

The standard P2PKH helper provides the byte-minimal address check plus signature check. Use pubKey2Addr(pubkey) when authoring custom P2PKH-style predicates; it documents address intent while lowering to the same OP_HASH160 script surface as hash160(pubkey).

import {
  P2PKH,
} from '@opcat-labs/lambit';

const p2pkh = P2PKH({ addr: '00112233445566778899aabbccddeeff00112233' });
console.log(P2PKH.name); // P2PKH
console.log(p2pkh.artifact.hex); // 76a9<addr>88ac

When you want assertion metadata, wrap the predicate in assert(...). That adds an explicit verify boundary (OP_VERIFY OP_1), so the script is slightly longer.

import {
  contract,
  method,
  TypeTag,
  and,
  assert,
  eq,
  pubKey2Addr,
  checkSig,
} from '@opcat-labs/lambit';

const AssertP2PKH = contract(
  'AssertP2PKH',
  { addr: TypeTag.Ripemd160 },
  ({ props }) => ({
    unlock: method(
      { sig: TypeTag.Sig, pubkey: TypeTag.PubKey },
      (sig, pubkey) => assert(and(eq(pubKey2Addr(pubkey), props.addr), checkSig(sig, pubkey))),
    ),
  }),
);

const assertP2pkh = AssertP2PKH({ addr: '00112233445566778899aabbccddeeff00112233' });
console.log(assertP2pkh.artifact.hex); // 76a9<addr>88ac6951

Authoring Flow: Define, Bind, Transition, Init

Stateful methods return:

  • next: expressions for every declared state field
  • check: spending condition expression

import {
  contract,
  method,
  TypeTag,
  add,
  and,
  checkSig,
  gte,
  sub,
} from '@opcat-labs/lambit';

const Vault = contract(
  'Vault',
  { owner: TypeTag.PubKey },
  { balance: TypeTag.Int },
  ({ props, state }) => ({
    withdraw: method(
      { amount: TypeTag.Int, sig: TypeTag.Sig },
      (amount, sig) => ({
        next: { balance: sub(state.balance, amount) },
        check: and(checkSig(sig, props.owner), gte(state.balance, amount)),
      }),
    ),
    deposit: method(
      { amount: TypeTag.Int },
      (amount) => ({
        // Pedagogical example: anyone can deposit. Add auth for production flows.
        next: { balance: add(state.balance, amount) },
      }),
    ),
  }),
);

const vault = Vault({ owner: '02'.padEnd(66, '1') });
const s0 = { balance: 100n };
const s1 = vault.methods.withdraw.next(s0, { amount: 30n });
const s2 = vault.methods.deposit.next(s1, { amount: 50n });
const instance = vault.init(s0);

console.log(vault.artifact.contract); // Vault
console.log({ s0, s1, s2 });
console.log(instance.state); // { balance: 100n }

Runtime Flow: Deploy and Call

This is a compact offline smoke test for the runtime APIs. The example uses the fluent provider-driven helpers exposed on bound and deployed contracts. Use How to Deploy & Call as the canonical guide for realistic funding, deploy/call, testnet setup, and operational gotchas.

import {
  contract,
  method,
  TypeTag,
  add,
  and,
  checkSig,
  createMemoryProvider,
  createSigner,
  gte,
  sub,
} from '@opcat-labs/lambit';

const Vault = contract(
  'Vault',
  { owner: TypeTag.PubKey },
  { balance: TypeTag.Int },
  ({ props, state }) => ({
    withdraw: method(
      { amount: TypeTag.Int, sig: TypeTag.Sig },
      (amount, sig) => ({
        next: { balance: sub(state.balance, amount) },
        check: and(checkSig(sig, props.owner), gte(state.balance, amount)),
      }),
    ),
    deposit: method(
      { amount: TypeTag.Int },
      (amount) => ({
        // Pedagogical example: anyone can deposit. Add auth for production flows.
        next: { balance: add(state.balance, amount) },
      }),
    ),
  }),
);

const signer = createSigner();
const owner = await signer.getPublicKey();
const provider = createMemoryProvider();
const vault = Vault({ owner });
const deployed = await vault.deploy({ balance: 100n }, {
  provider,
  satoshis: 100_000n,
});

const spend = await deployed.methods.withdraw.call(
  { amount: 30n },
  {
    provider,
    signer,
    invoke: (psbt) => ({
      sig: psbt.getSig(0, { publicKey: owner }),
    }),
    nextState: vault.methods.withdraw.next({ balance: 100n }, { amount: 30n }),
  },
);

console.log(spend.nextInstance?.state); // { balance: 70n }

API Overview

DSL Entry Points

  • contract(name, props, body) and contract(name, props, state, body) return callable contract definitions
  • method(schema, fn) for positional args, or method.named(schema, (args) => ...) / method.named(schema, ({ key }) => ...) for helper-heavy composition and schema-aware named params such as fixed-array helpers
  • calling a contract def binds props: const vault = Vault({ owner })
  • bound methods expose pure transitions with vault.methods.<name>.next(state, args?)
  • vault.init(state?) bridges the authoring API into a runtime Instance
import { FixedArray, TypeTag, contract, eq, method } from '@opcat-labs/lambit';

const Cells = FixedArray(TypeTag.Int, 2);

const Grid = contract(
  'Grid',
  {},
  () => ({
    unlock: method.named(
      { board: Cells, index: TypeTag.Int, value: TypeTag.Int },
      ({ board, index, value }) => eq(board.at(index), value),
    ),
  }),
);

IR and Typing

  • TypeTag defines constructor and state field types
  • alias(name, type) and struct(name, fields) preserve user-defined type metadata in the IR and emitted artifacts; struct(..., genericTypes) is metadata-only today and is not used to specialize runtime encoding
  • ExprNode is the expression-tree IR shape used by the DSL/compiler

DSL Primitives

Examples:

  • Logic: and, or, not, cond
  • Comparison: eq, neq, gt, gte, lt, lte
  • Arithmetic: add, sub, mul, div, mod
  • Crypto: sha256(...), hash160(...), hash256(...), pubKey2Addr(...), checkSig(...), checkSigVerify(...), checkMultiSig(...), checkMultiSigVerify(...), checkDataSig(...), checkDataSigVerify(...)
  • Bytes: cat, slice, len, intToBytes, bytesToInt
  • Build-time arrays: fold, map, every, some

Signature and Address Helpers

pubKey2Addr(pubkey) is the named address helper for P2PKH-style checks. It preserves authored address intent in IR while lowering to the same OP_HASH160 script surface as hash160(pubkey). checkDataSig(sig, message, pubkey) emits OP_CHECKDATASIG.

| Pattern | Use when | |---|---| | and(checkSig(...), otherCheck) or and(checkDataSig(...), otherCheck) | You are composing boolean predicates; the compiler can fuse eligible checks to VERIFY opcodes. | | assert(checkSig(...), message) or assert(checkDataSig(...), message) | You want an explicit verify boundary plus assertion metadata in the artifact. | | checkSigVerify(...), checkMultiSigVerify(...), or checkDataSigVerify(...) | The VERIFY opcode is the whole method body, or you want assertion metadata exactly at that VERIFY site. |

assert(expr) and assert(expr, message) both compile to an explicit verify boundary and record assertion metadata in artifacts.

import {
  contract,
  method,
  TypeTag,
  and,
  assert,
  checkDataSig,
  checkSig,
  eq,
  pubKey2Addr,
} from '@opcat-labs/lambit';

const DataSignedP2PKH = contract(
  'DataSignedP2PKH',
  { addr: TypeTag.Ripemd160 },
  ({ props }) => ({
    unlock: method(
      { sig: TypeTag.Sig, dataSig: TypeTag.Sig, message: TypeTag.ByteString, pubkey: TypeTag.PubKey },
      (sig, dataSig, message, pubkey) => assert(
        and(
          eq(pubKey2Addr(pubkey), props.addr),
          checkSig(sig, pubkey),
          checkDataSig(dataSig, message, pubkey),
        ),
      ),
    ),
  }),
);

const dataSigned = DataSignedP2PKH({ addr: '00112233445566778899aabbccddeeff00112233' });
console.log(dataSigned.artifact.contract); // DataSignedP2PKH
console.log(dataSigned.artifact.hex); // 76a9<addr>88537a5179ac537a537a537aba9a6951

Standard Helpers

  • reusable contract defs: P2PK, P2PKH
  • reusable predicates: p2pkh
  • opcode-byte literals: opcode, opcodes

Compiler Helpers

  • buildArtifact(def, options?) compiles a ContractDef into artifact JSON
  • decodeArtifactHex(artifact.hex, options?) decodes opcode/placeholder token streams for quick diffs
  • snapshotArtifact(artifact, options?) normalizes artifact metadata into a stable snapshot object
  • formatArtifactDebug(artifact, options?) renders a readable multiline debug view
  • stackCompile and stackCompileWithStack compile expression trees into script chunks
  • stateHash and buildDataOutput are the public state-output helpers used by stateful artifact/runtime flows
  • chunksToHex converts script chunks into final hex output

Runtime Helpers

  • vault.init(state) creates an Instance
  • createInstance remains available when you already have an artifact and constructor args
  • vault.deploy(...) and vault.prepareDeploy(...) handle provider-backed deployment from a bound contract
  • vault.prepareDeploy(...) returns a deferred deployment handle with the initialized instance, provider, satoshis, and a later deploy() method
  • deployed.methods.<name>.call(...) and .prepareCall(...) handle provider-backed spends from a deployed instance
  • snapshotInstance(instance) converts runtime values into JSON-safe debug snapshots
  • formatInstanceDebug(instance) renders a readable multiline runtime dump
  • createMemoryProvider implements the Provider interface for fully offline tests, plus an opt-in realistic funding mode that materializes wallet inputs/change; realistic contract outputs must provide output.instance
  • createSigner provides a Bitcoin-format secp256k1 signer for examples and local coverage

Runtime Migration Notes

  • Prefer bound.deploy(...), bound.prepareDeploy(...), deployed.methods.<name>.call(...), and deployed.methods.<name>.prepareCall(...); legacy global deployInstance(...), call(...), and prepareCall(...) remain as deprecated compatibility exports.
  • Signature-bearing fluent method calls accept signer + invoke directly in the options object, so psbt.getSig(...) no longer forces a fallback to a global callback helper.
  • Callback and method-style invocation both reject missing or unexpected named args before transaction building. Partial arg maps that used to fail later at script execution now throw at call preparation time.

Migration Notes

  • Old stateful(props, state, body) or stateful(name, props, state, body) call sites should move to contract('Name', props, state, body).
  • Old post-hoc naming like def.name = 'Vault' should move into the first contract(...) argument so artifact metadata, bound contracts, and docs all agree on one source of truth.
  • Old prop-binding helpers become direct calls on the definition: const vault = Vault({ owner }).
  • If you already have an artifact-only workflow, buildArtifact(def) and createInstance(...) still work. The docs now lead with Vault({ ... }).init(...) because it keeps authoring and instantiation on the same surface.
  • For class/decorator ports, move the class shape into contract('Name', props, state?, body) and use the public authoring guides for the current functional model.

Artifact Output

buildArtifact returns an object with:

  • version (currently 10)
  • compilerVersion (currently 1.0.0-fp)
  • contract name
  • abi entries (functions + constructor), preserving declared constructor prop and method param names/types
  • structs, library, and alias metadata for user-defined schema and reusable library types
  • preserved genericTypes on emitted struct and library metadata entries
  • generated state metadata via stateProps, top-level stateType, and generated <ContractName>State / <LibraryName>State struct entries when state is present
  • buildType and file
  • hex locking script (may include placeholders like <addr>)
  • md5 digest of the final hex

Debug and Snapshot Workflow

Use debug builds plus the snapshot helpers when you want a supported local workflow for inspecting artifacts and runtime instances during contract iteration.

import {
  and,
  buildArtifact,
  checkSig,
  contract,
  decodeArtifactHex,
  eq,
  formatArtifactDebug,
  formatInstanceDebug,
  hash160,
  method,
  snapshotArtifact,
  snapshotInstance,
  TypeTag,
} from '@opcat-labs/lambit';

const P2PKH = contract(
  'P2PKH',
  { addr: TypeTag.Ripemd160 },
  ({ props }) => ({
    unlock: method(
      { sig: TypeTag.Sig, pubkey: TypeTag.PubKey },
      (sig, pubkey) => and(eq(hash160(pubkey), props.addr), checkSig(sig, pubkey)),
    ),
  }),
);

const bound = P2PKH({ addr: '00112233445566778899aabbccddeeff00112233' });
const artifact = buildArtifact(P2PKH, { buildType: 'debug' });
const instance = bound.init();
const artifactSnapshot = snapshotArtifact(artifact);
const instanceSnapshot = snapshotInstance(instance);
const tokens = decodeArtifactHex(artifact.hex);
const artifactDebug = formatArtifactDebug(artifact);
const instanceDebug = formatInstanceDebug(instance);

console.log(tokens);
console.log(JSON.stringify(artifactSnapshot, null, 2));
console.log(JSON.stringify(instanceSnapshot, null, 2));
console.log(artifactDebug);
console.log(instanceDebug);
  • snapshotArtifact(...) omits compilerVersion and md5 by default so snapshots stay focused on semantic diffs; pass { includeCompilerMetadata: true } when you need them.
  • snapshotInstance(...) normalizes bigint values into strings like "2n" and byte-oriented runtime values into hex strings, so JSON.stringify(..., null, 2) works directly in tests.
  • decodeArtifactHex(...) exposes the opcode/placeholder stream used by the golden tests, which is useful when comparing local compiler output against fixtures.
  • Pass { includeDataPushTokens: true } to decodeArtifactHex(...) when you need pushed-byte boundaries as DATA(n) tokens for opcode-profile assertions.
  • decodeArtifactHex(...) validates the full hex stream and throws on malformed or truncated input, so treat it as a strict local inspection tool rather than a lenient parser.
  • formatArtifactDebug(...) and formatInstanceDebug(...) are meant for quick terminal inspection without adding custom logging code.

Project Layout

  • src/dsl: contract builders and primitive constructors
  • src/ir: expression and contract IR definitions
  • src/compiler: compile passes and artifact assembly
  • src/types: opcode/type mappings
  • test: unit tests, golden fixtures, and docs/e2e checks

Current Prototype Limitations and Scope

  • TypeTag.Int is the default bigint-native integer type for new contracts. TypeTag.Int32 remains available only as a deprecated compatibility alias.
  • lit(value) is the built-in literal helper for integer, boolean, and byte-string expression nodes.
  • Struct and alias schemas are encoded and emitted as metadata, but contract expressions still treat them as opaque values; field projection and mutation helpers are outside the current surface.
  • Struct genericTypes are preserved in artifacts for consumers, but runtime encoding/decoding validates declared fields directly and does not specialize generic parameters.
  • Runtime/provider APIs are usable through the documented MemoryProvider and testnet workflows, but 0.1.x provider details may still evolve before a stable release.