Skip to content

Instantly share code, notes, and snippets.

Last active Dec 13, 2019
What would you like to do?
live-common in a nutshell
"private": true,
"name": "send-example",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@ledgerhq/hw-transport-node-hid-noevents": "canary",
"@ledgerhq/ledger-core": "alpha",
"@ledgerhq/live-common": "alpha",
"axios": "^0.19.0",
"rxjs": "^6.5.3"
"scripts": {
"start": "node index.js"
const { first, map, reduce } = require("rxjs/operators");
const { getCryptoCurrencyById, formatCurrencyUnit, parseCurrencyUnit } = require("@ledgerhq/live-common/lib/currencies");
const { getCurrencyBridge, getAccountBridge } = require("@ledgerhq/live-common/lib/bridge");
// our small example is a script that takes 3 params.
// example: node send.js bitcoin bc1abc..def 0.001
if (!process.argv[4]) {
console.log(`Usage: currencyId recipient amount`);
const currencyId = process.argv[2];
const currency = getCryptoCurrencyById(currencyId);
const recipient = process.argv[3];
const amount = parseCurrencyUnit(currency.units[0], process.argv[4]);
const deviceId = ""; // in HID case
// live-common requires some setup. usually we put that in a live-common-setup.js
const { registerTransportModule } = require("@ledgerhq/live-common/lib/hw");
const TransportNodeHid = require("@ledgerhq/hw-transport-node-hid-noevents").default;
const implementLibcore = require("@ledgerhq/live-common/lib/libcore/platforms/nodejs").default;
const { setNetwork } = require("@ledgerhq/live-common/lib/network");
const { setSupportedCurrencies } = require("@ledgerhq/live-common/lib/data/cryptocurrencies");
// configure which coins to enable
// provide a network function
// provide a libcore implementation
lib: () => require("@ledgerhq/ledger-core"),
dbPath: "./dbdata"
// configure which transport are available
id: "hid",
open: devicePath =>,
disconnect: () => Promise.resolve()
async function main() {
// currency bridge is the interface to scan accounts of the device
const currencyBridge = getCurrencyBridge(currency);
// some currency requires some data to be loaded (today it's not highly used but will be more and more)
await currencyBridge.preload();
// NB scanAccountsOnDevice returns an observable but we'll just get the first account as a promise.
const scannedAccount = await currencyBridge
.scanAccountsOnDevice(currency, deviceId)
// there can be many accounts, for sake of example we take first non empty
first(e => e.type === "discovered" &&,
map(e => e.account)
// account bridge is the interface to sync and do transaction on our account
const accountBridge = getAccountBridge(scannedAccount);
// Minimal way to synchronize an account.
// NB: our scannedAccount is already sync in fact, this is just for the example
const account = await accountBridge
.startSync(scannedAccount, false)
.pipe(reduce((a, f) => f(a), scannedAccount))
console.log(`${} new address: ${account.freshAddress}`);
console.log(`with balance of ${formatCurrencyUnit(account.unit, account.balance)}`);
// We prepare a transaction
let t = accountBridge.createTransaction(account);
t = accountBridge.updateTransaction(t, { amount, recipient });
t = await accountBridge.prepareTransaction(account, t);
// We can always get the status. used for form validation and meta info (like calculated fees)
const status = await accountBridge.getTransactionStatus(account, t);
console.log({ status });
// we can't broadcast the transaction if there are errors
const errors = Object.values(status.errors);
if (errors.length) {
throw errors[0];
// We're good now, we can sign the transaction and broadcast it
const operation = await accountBridge
.signAndBroadcast(account, t, deviceId)
// there are many events. we just take the final broadcasted
first(e => e.type === "broadcasted"),
map(e => e.operation)
// the transaction is broadcasted!
// the resulting operation is an "optimistic" response that can be prepended to our account.operations[]
console.log("broadcasted", operation);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment