UNPKG

@ledgerhq/hw-app-xrp

Version:
170 lines 6.98 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bip32_path_1 = __importDefault(require("bip32-path")); /** * XRP API * * @example * import Transport from "@ledgerhq/hw-transport-node-hid"; * // import Transport from "@ledgerhq/hw-transport-u2f"; // for browser * import Xrp from "@ledgerhq/hw-app-xrp"; * import { encode } from 'ripple-binary-codec'; * * function establishConnection() { * return Transport.create() * .then(transport => new Xrp(transport)); * } * * function fetchAddress(xrp) { * return xrp.getAddress("44'/144'/0'/0/0"); * } * * function signTransaction(xrp, deviceData, seqNo) { * let transactionJSON = { * TransactionType: "Payment", * Account: deviceData.address, * Destination: "rTooLkitCksh5mQa67eaa2JaWHDBnHkpy", * Amount: "1000000", * Fee: "15", * Flags: 2147483648, * Sequence: seqNo, * SigningPubKey: deviceData.publicKey.toUpperCase() * }; * * const transactionBlob = encode(transactionJSON); * * console.log('Sending transaction to device for approval...'); * return xrp.signTransaction("44'/144'/0'/0/0", transactionBlob); * } * * function prepareAndSign(xrp, seqNo) { * return fetchAddress(xrp) * .then(deviceData => signTransaction(xrp, deviceData, seqNo)); * } * * establishConnection() * .then(xrp => prepareAndSign(xrp, 123)) * .then(signature => console.log(`Signature: ${signature}`)) * .catch(e => console.log(`An error occurred (${e.message})`)); */ class Xrp { transport; constructor(transport, scrambleKey = "XRP") { this.transport = transport; transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction", "getAppConfiguration"], scrambleKey); } /** * get XRP address for a given BIP 32 path. * * @param path a path in BIP 32 format * @param display optionally enable or not the display * @param chainCode optionally enable or not the chainCode request * @param ed25519 optionally enable or not the ed25519 curve (secp256k1 is default) * @return an object with a publicKey, address and (optionally) chainCode * @example * const result = await xrp.getAddress("44'/144'/0'/0/0"); * const { publicKey, address } = result; */ async getAddress(path, display, chainCode, ed25519) { const bipPath = bip32_path_1.default.fromString(path).toPathArray(); const curveMask = ed25519 ? 0x80 : 0x40; const cla = 0xe0; const ins = 0x02; const p1 = display ? 0x01 : 0x00; const p2 = curveMask | (chainCode ? 0x01 : 0x00); const data = Buffer.alloc(1 + bipPath.length * 4); data.writeInt8(bipPath.length, 0); bipPath.forEach((segment, index) => { data.writeUInt32BE(segment, 1 + index * 4); }); const response = await this.transport.send(cla, ins, p1, p2, data); const publicKeyLength = response[0]; const addressLength = response[1 + publicKeyLength]; return { publicKey: response.slice(1, 1 + publicKeyLength).toString("hex"), address: response .slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + addressLength) .toString("ascii"), chainCode: chainCode ? response .slice(1 + publicKeyLength + 1 + addressLength, 1 + publicKeyLength + 1 + addressLength + 32) .toString("hex") : undefined, }; } /** * sign a XRP transaction with a given BIP 32 path * * The rawTxHex parameter is the serialized transaction blob represented as * hex. * * @param path a path in BIP 32 format * @param rawTxHex a raw hex string representing a serialized transaction blob. * This parameter can be encoded using [ripple-binary-codec](https://www.npmjs.com/package/ripple-binary-codec). * See https://xrpl.org/serialization.html for more documentation on the serialization format. * @param ed25519 optionally enable or not the ed25519 curve (secp256k1 is default) * @return a signature as hex string * @example * const signature = await xrp.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F"); */ async signTransaction(path, rawTxHex, ed25519) { const bipPath = bip32_path_1.default.fromString(path).toPathArray(); const rawTx = Buffer.from(rawTxHex, "hex"); const curveMask = ed25519 ? 0x80 : 0x40; const apdus = []; let offset = 0; while (offset !== rawTx.length) { const isFirst = offset === 0; const maxChunkSize = isFirst ? 150 - 1 - bipPath.length * 4 : 150; const hasMore = offset + maxChunkSize < rawTx.length; const chunkSize = hasMore ? maxChunkSize : rawTx.length - offset; const apdu = { cla: 0xe0, ins: 0x04, p1: (isFirst ? 0x00 : 0x01) | (hasMore ? 0x80 : 0x00), p2: curveMask, data: isFirst ? Buffer.alloc(1 + bipPath.length * 4 + chunkSize) : Buffer.alloc(chunkSize), }; if (isFirst) { apdu.data.writeInt8(bipPath.length, 0); bipPath.forEach((segment, index) => { apdu.data.writeUInt32BE(segment, 1 + index * 4); }); rawTx.copy(apdu.data, 1 + bipPath.length * 4, offset, offset + chunkSize); } else { rawTx.copy(apdu.data, 0, offset, offset + chunkSize); } apdus.push(apdu); offset += chunkSize; } let response = Buffer.alloc(0); for (const apdu of apdus) { response = await this.transport.send(apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.data); } // the last 2 bytes are status code from the hardware return response.slice(0, response.length - 2).toString("hex"); } /** * get the version of the XRP app installed on the hardware device * * @return an object with a version * @example * const result = await xrp.getAppConfiguration(); * * { * "version": "1.0.3" * } */ async getAppConfiguration() { const response = await this.transport.send(0xe0, 0x06, 0x00, 0x00); return { version: "" + response[1] + "." + response[2] + "." + response[3], }; } } exports.default = Xrp; //# sourceMappingURL=Xrp.js.map