@via-labs-tech/usdm-bridge
v1.2.0
Published
Headless USDM bridging between Cardano and Midnight — a Node app, secrets in .env.
Maintainers
Readme
usdm-bridge
Headless USDM bridging between Cardano and Midnight for Node.js — use it as a library or run the bundled CLI. One env var selects the network pair: NETWORK=mainnet for Cardano Mainnet ↔ Midnight mainnet, default is Preprod ↔ Preview. Both pairs are live-tested; contract addresses, chain ids, and ZK artifacts for both ship inside the package (see MAINNET.md for what differs per network).
Wallet mnemonics and API keys live in .env and never leave the server. Chain reads need no API key by default (Koios' free public tier; no CORS server-side), or Blockfrost if you set a project id.
Install
npm install @via-labs-tech/usdm-bridgeConfiguration comes from the environment (a .env file in your working directory works — see below).
Usage
import { bridgeUSDM } from '@via-labs-tech/usdm-bridge'
const { txHash } = await bridgeUSDM({
direction: 'cardano-to-midnight', // or 'midnight-to-cardano'
amount: '5',
recipient: 'mn_addr1...', // or addr1... for midnight-to-cardano
})Also exported: getLucid, getSpendableUtxos, getEnterpriseAddress, koiosProvider, deriveMidnightAddress, and the config constants (NETWORK, IS_MAINNET, CONTRACTS, USDM_TOKEN_COLOR, USDM_DECIMALS, ...).
The library no longer loads .env itself — the bundled CLI scripts do, and from your own code you provide the environment (e.g. node --env-file=.env app.js).
Browser
Same import, same call, same env-var configuration — the only difference is the optional wallet param: name the installed wallet and the bridge signs through it instead of the environment. The package does the whole bridge; wallet discovery, balances, and UI stay the frontend's business:
import { bridgeUSDM } from '@via-labs-tech/usdm-bridge'
// Cardano -> Midnight (any CIP-30 wallet)
const { txHash } = await bridgeUSDM({
direction: 'cardano-to-midnight',
amount: '5',
recipient: 'mn_addr…',
wallet: 'eternl', // just the name — the package finds it on window.cardano
})
// Midnight -> Cardano (connector-v4 wallet; proving happens inside the wallet — no proof server)
const { txHash, txId } = await bridgeUSDM({
direction: 'midnight-to-cardano',
amount: '5',
recipient: 'addr…',
wallet: '1am', // just the name — the package finds it on window.midnight
})The bundler setup is the non-obvious part (wasm plugins, Node polyfills, CORS realities, ZK asset serving) — see FRONTEND.md for the full guide, and example/plain-frontend for a complete working React app (the bridge logic is two ~50-line hooks).
CLI
From a checkout (npm install && npm run build && cp .env.example .env), or against the installed package (node node_modules/@via-labs-tech/usdm-bridge/bridge.mjs ...):
node bridge.mjs c2m 5 # Cardano -> Midnight, to your own Midnight wallet
node bridge.mjs c2m 5 mn_addr1... # ...or to a specific Midnight address
node bridge.mjs m2c 5 # Midnight -> Cardano, to your own Cardano wallet
node bridge.mjs m2c 5 addr1... # ...or to a specific Cardano address
node balance-cardano.mjs # Cardano wallet balances (ADA + USDM)
node balance.mjs # Midnight wallet balances (USDM, DUST)
node make-dust.mjs # register NIGHT UTxOs for DUST generationAll commands follow NETWORK from .env; override per run with e.g. NETWORK=testnet node balance.mjs.
Environment (.env)
| variable | needed for | notes |
| --- | --- | --- |
| NETWORK | network selection | mainnet for the mainnet pair; testnet (default) = Preprod/Preview |
| CARDANO_MNEMONIC | cardano → midnight | 12/15/24 words; must hold (t)USDM + ADA on the selected network |
| MIDNIGHT_MNEMONIC (or MIDNIGHT_SEED) | midnight → cardano | must hold USDM + DUST on the selected network |
| PROOF_SERVER_URL | midnight → cardano | default http://localhost:6300; proving is local |
| BLOCKFROST_PROJECT_ID | optional | if set, Cardano reads use Blockfrost; if unset, keyless Koios |
| KOIOS_URL, BLOCKFROST_URL, MIDNIGHT_NODE_URL, MIDNIGHT_INDEXER_URL, MIDNIGHT_INDEXER_WS_URL, WALLET_STATE_FILE | optional | endpoint / cache overrides |
Every wallet secret also takes a network suffix that wins on its network — CARDANO_MNEMONIC_MAINNET / CARDANO_MNEMONIC_PREPROD, MIDNIGHT_MNEMONIC_MAINNET / MIDNIGHT_MNEMONIC_PREVIEW — so both networks' wallets can live in one .env and NETWORK picks between them. The plain (unsuffixed) name is the shared fallback.
Notes:
- Cardano → Midnight builds, signs, submits, and awaits the lock tx. Arrival on Midnight follows after the source-chain confirmations validators wait for (150 blocks on mainnet, 1 on Preprod).
- Midnight → Cardano calls the gateway's
bridgecircuit; needs a running proof server, and the wallet needs DUST (generated by a Cardano-side NIGHT registration pointing at this wallet's dust key — a clearOUT OF DUSTerror names the key if it has none). First run syncs the Midnight wallet from genesis (minutes); state is cached per network (wallet-state.json/wallet-state.mainnet.json) afterward. - The Midnight ZK assets ship in
artifacts/midnight/<network>and are read from disk.
