@varnir/chain-client
v0.6.0
Published
The Varnir SDK - VarnirClient (onboard an identity, get a wallet, read balances, send, raise invoices) plus the low-level signing/transport primitives it is built from.
Readme
@varnir/chain-client
The Varnir SDK: onboard an identity, get a wallet, read balances, send funds, and raise invoices against a Varnir ledger network, plus the low-level signing and transport primitives it's built from.
This is what docs.varnir.site's SDK reference documents, and the same client this monorepo's own wallet and scanner applications are built on — not a thin wrapper kept separate from the "real" implementation.
Custody model
Every transaction this SDK sends is signed with a private key that stays
in the calling process. VarnirClient takes your privateKeyHex (or an
already-onboarded identity's key) as a constructor argument and signs
locally, using either @activeledger/sdk-node (real Node.js) or pure-JS
@noble/curves (browsers, React Native) depending on which transport you
import — see Four entry points below.
The key is never sent to a Varnir-hosted server, and no method on this
client transmits it anywhere; the ledger only ever receives an already-signed
transaction. This is custody, not a hosted signing service: whoever holds
the private key controls the funds, and this SDK is how that holder signs
without handing the key to anyone else.
An already-onboarded caller supplies both halves — the private key and the
identity it's paired with. The SDK does not re-derive the identity from the
key for you (it could — computeIdentity does exactly that), because
treating a locally computed id as authoritative is how a caller ends up
holding an identity that exists nowhere on the ledger.
Install
npm install @varnir/chain-clientQuick start
import {VarnirClient} from '@varnir/chain-client';
import {generateRecoveryPhrase, derivePrivateKeyFromPhrase} from '@varnir/chain-client/keys';
const {privateKeyHex} = derivePrivateKeyFromPhrase(generateRecoveryPhrase());
const client = await VarnirClient.onboard({network: 'testnet', privateKeyHex});
const wallet = await client.getWallet('niles');
const invoice = await client.createInvoice({wallets: [wallet], name: 'Invoice', amount: '10'});'testnet' is the entire network configuration — node URLs, gateway URLs,
deployed contract stream ids, each chain's ledger-side network spelling,
and native decimals are all resolved internally. A caller never needs to
know a hostname or a hex contract id.
The full method reference — onboard / new VarnirClient({privateKeyHex, identity}),
identityExists, getIdentityStream, listWallets, getWallet,
createWallet, getBalances, send, getTransfer, waitForTransfer,
createInvoice, and the statics generateKey / computeIdentity — lives
at docs.varnir.site.
Four entry points, one package
// The SDK, plus every low-level primitive it is built from.
import {VarnirClient, signPayloadBase64, sendToAnyNode, gatewayGet} from '@varnir/chain-client';
// Recovery phrases and BIP32/BIP44 derivation, re-exported from
// @varnir/signing. A subpath because it pulls in bip39 and its wordlist,
// which callers of the main entry point have no use for.
import {generateRecoveryPhrase, derivePrivateKeyFromPhrase} from '@varnir/chain-client/keys';
// Direct-to-node, real Node.js only — @activeledger/sdk-node, signs via
// node:crypto.
import {NodeTransport, importEllipticCurveKey} from '@varnir/chain-client/node';
// Direct-to-node, browsers and React Native — @activeledger/sdk-web,
// signs via @noble/curves (pure JS, no native/WebCrypto dependency).
import {WebTransport, importEllipticCurveKey} from '@varnir/chain-client/web';Pick the transport by where your code runs, not by habit:
@varnir/chain-client(default import) —VarnirClient, the network/chain registry, a typed gateway HTTP client, and the manual-sign helpers. No dependency on either ledger SDK, so it's safe to import from anywhere, including a React Native bundler.@varnir/chain-client/node—NodeTransport: generic direct-to-node ledger operations (key generation, onboarding, signing, sending) via@activeledger/sdk-node. Real Node.js only.@varnir/chain-client/web—WebTransport: the same method shape asNodeTransport, via@activeledger/sdk-web. Safe in browsers and React Native — no Node core-module dependency, so it needs no polyfills.
Each transport lives behind its own subpath (via package.json's
exports map) so a bundler never needs to tree-shake the other ledger SDK
out — it's simply never imported unless you ask for it.
Importing an externally-derived key
If you already derive a secp256k1 key pair yourself (your own BIP32/BIP44
path, a hardware wallet, anything that isn't this package's own key
generation), importEllipticCurveKey (from /node or /web) wraps an
already-derived compressed secp256k1 key pair into the shape
NodeTransport/WebTransport's signTransaction/onboardKey expect,
without going through this package's own key generation:
import {importEllipticCurveKey, WebTransport} from '@varnir/chain-client/web';
const key = importEllipticCurveKey({privateKeyHex, publicKeyHex}); // your own key pair
const transport = new WebTransport();
await transport.onboardKey(key);publicKeyHex must be the compressed form (33 bytes) — the uncompressed
form is not supported.
Examples
examples/wallet-and-invoice-flow.ts is a complete external-automation
walkthrough — onboard an identity, get a testnet wallet, and raise an
invoice against it, entirely client-signed. Run it with
npx tsx examples/wallet-and-invoice-flow.ts from this package's directory.
Known limitations
listTransactions's return shape has only been confirmed against a freshly-reset network with no indexed activity yet (it returned{"unknown": 1}instead of an array in that case). Treat the result asTransactionSummary[] | {unknown: number}and narrow withArray.isArray(...)before treating it as a list.- The React Native / native-mobile path (
WebTransporton Hermes) has been verified on the web target; direct on-device iOS/Android signing behavior has not been separately confirmed.
License
MIT
