UNPKG

@fluent-wallet/hw-app-conflux

Version:

Ledger Hardware Wallet Conflux Application API

404 lines 18.2 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; import { foreach, isLegacyVersion, splitMessage, splitPath } from "./utils"; import { sign, format } from "js-conflux-sdk"; import BIPPath from "bip32-path"; var remapTransactionRelatedErrors = function (e) { if (e && e.statusCode === 0x6a80) { throw new Error("Missing a parameter. Try enabling blind signature in the app"); } return e; }; var CLA = 0xe0; var P1 = { first: 0x00 }; var P2 = { more: 0x80, last: 0x00 }; var INS = { GET_ADDRESS: 0x02, SIGN_TX: 0x03, SIGN_PERSONAL_MESSAGE: 0x04 }; var CHAINID = { MAINNET: 1029, TESTNET: 1 }; /** * Conflux API * * @param transport a transport for sending commands to a device * @param scrambleKey a scramble key * * @example * import Cfx from "@ledgerhq/hw-app-conflux"; * const cfx = new Cfx(transport) */ var Conflux = /** @class */ (function () { function Conflux(transport, chainId, scrambleKey) { if (scrambleKey === void 0) { scrambleKey = "conflux_default_scramble_key"; } this.transport = transport; this.chainId = chainId || CHAINID.MAINNET; transport.decorateAppAPIMethods(this, [ "getAddress", "signTransaction", "getAppConfiguration", "signPersonalMessage", ], scrambleKey); } /** * get Conflux address for a given BIP 32 path. * @param path a path in BIP 32 format * @option boolDisplay optionally enable or not the display * @option boolChaincode optionally enable or not the chaincode request * @return an object with a publicKey, address and (optionally) chainCode * @example * cfx.getAddress("44'/503'/0'/0/0").then(o => o.publicKey) * cfx.getAddress("44'/503'/0'/0/0",true).then(o => o.publicKey): show mainnet address */ Conflux.prototype.getAddress = function (path, boolDisplay, boolChaincode) { var _this = this; //path buffer var paths = splitPath(path); var buffer = Buffer.alloc(1 + paths.length * 4); buffer[0] = paths.length; paths.forEach(function (element, index) { buffer.writeUInt32BE(element, 1 + 4 * index); }); //chainID buffer if (boolDisplay) { var chainIdBuffer = Buffer.alloc(4); chainIdBuffer.writeUInt32BE(this.chainId); buffer = Buffer.concat([buffer, chainIdBuffer]); } return this.transport .send(0xe0, INS.GET_ADDRESS, boolDisplay ? 0x01 : 0x00, boolChaincode ? 0x01 : 0x00, buffer) .then(function (response) { var publicKeyLength = response[0]; var publicKey = response .slice(2, 1 + publicKeyLength) .toString("hex"); // remove the prefix:04, because 04 means the uncompressed public key var address = format.address("0x".concat(sign["publicKeyToAddress"](Buffer.from(publicKey, "hex")).toString("hex")), _this.chainId); //CIP-37 address var chainCode; if (boolChaincode) { var chainCodeLength = response[1 + publicKeyLength]; chainCode = response .slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + chainCodeLength) .toString("hex"); } return { publicKey: publicKey, address: address, chainCode: chainCode }; }); }; Conflux.prototype._legacy_signTransaction = function (path, rawTxHex) { return __awaiter(this, void 0, void 0, function () { var paths, offset, rawTx, toSend, response, _loop_1; var _this = this; return __generator(this, function (_a) { paths = BIPPath.fromString(path).toPathArray(); offset = 0; rawTx = Buffer.from(rawTxHex, "hex"); toSend = []; _loop_1 = function () { var maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 : 150; var chunkSize = offset + maxChunkSize > rawTx.length ? rawTx.length - offset : maxChunkSize; var buffer = Buffer.alloc(offset === 0 ? 1 + paths.length * 4 + chunkSize : chunkSize); if (offset === 0) { buffer[0] = paths.length; paths.forEach(function (element, index) { buffer.writeUInt32BE(element, 1 + 4 * index); }); rawTx.copy(buffer, 1 + 4 * paths.length, offset, offset + chunkSize); } else { rawTx.copy(buffer, 0, offset, offset + chunkSize); } toSend.push(buffer); offset += chunkSize; }; while (offset !== rawTx.length) { _loop_1(); } return [2 /*return*/, foreach(toSend, function (data, i) { return _this.transport .send(0xe0, INS.SIGN_TX, i === 0 ? 0x00 : 0x80, 0x00, data) .then(function (apduResponse) { response = apduResponse; }); }).then(function () { var response_byte = response.slice(0, 1)[0]; var v = response_byte.toString(16); var r = response.slice(1, 1 + 32).toString("hex"); var s = response.slice(1 + 32, 1 + 32 + 32).toString("hex"); return { v: v, r: r, s: s }; }, function (e) { throw remapTransactionRelatedErrors(e); })]; }); }); }; Conflux.prototype._signTransaction = function (path, rawTxHex) { return __awaiter(this, void 0, void 0, function () { var rawTx, paths, derivationPathBuff, payloadChunks, i, chunk, response, response_byte, v, r, s; return __generator(this, function (_a) { switch (_a.label) { case 0: rawTx = Buffer.from(rawTxHex, "hex"); paths = splitPath(path); derivationPathBuff = Buffer.alloc(1 + paths.length * 4); derivationPathBuff[0] = paths.length; paths.forEach(function (element, index) { derivationPathBuff.writeUInt32BE(element, 1 + 4 * index); }); // send bip32 return [4 /*yield*/, this.transport.send(CLA, INS.SIGN_TX, P1.first, P2.more, derivationPathBuff)]; case 1: // send bip32 _a.sent(); payloadChunks = splitMessage(rawTx, 255); if (!(payloadChunks.length > 1)) return [3 /*break*/, 5]; i = 0; _a.label = 2; case 2: if (!(i < payloadChunks.length - 1)) return [3 /*break*/, 5]; chunk = payloadChunks[i]; return [4 /*yield*/, this.transport.send(CLA, INS.SIGN_TX, i + 1, P2.more, chunk)]; case 3: _a.sent(); _a.label = 4; case 4: i++; return [3 /*break*/, 2]; case 5: return [4 /*yield*/, this.transport.send(CLA, INS.SIGN_TX, Math.max(payloadChunks.length - 1, 1), P2.last, payloadChunks[payloadChunks.length - 1])]; case 6: response = _a.sent(); response_byte = response.subarray(0, 1)[0]; v = response_byte.toString(16); r = response.subarray(1, 1 + 32).toString("hex"); s = response.subarray(1 + 32, 1 + 32 + 32).toString("hex"); return [2 /*return*/, { v: v, r: r, s: s }]; } }); }); }; /** * You can sign a transaction and retrieve v, r, s given the raw transaction and the BIP 32 path of the account to sign * @example cfx.signTransaction("44'/503'/0'/0/0", "e8018504e3b292008252089428ee52a8f3d6e5d15f8b131996950d7f296c7952872bd72a2487400080").then(result => ...) */ Conflux.prototype.signTransaction = function (path, rawTxHex) { return __awaiter(this, void 0, void 0, function () { var version, isLegacy; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this._getAppConfiguration()]; case 1: version = (_a.sent()).version; isLegacy = isLegacyVersion(version); if (isLegacy) return [2 /*return*/, this._legacy_signTransaction(path, rawTxHex)]; return [2 /*return*/, this._signTransaction(path, rawTxHex)]; } }); }); }; Conflux.prototype._getAppConfiguration = function () { return __awaiter(this, void 0, void 0, function () { var r, i, format, nameLength, name, versionLength, version, flagLength, flags; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.transport.send(0xb0, 0x01, 0x00, 0x00)]; case 1: r = _a.sent(); i = 0; format = r[i++]; if (format !== 1) { throw new Error("getAppAndVersion: format not supported"); } nameLength = r[i++]; name = r.slice(i, (i += nameLength)).toString("ascii"); versionLength = r[i++]; version = r.slice(i, (i += versionLength)).toString("ascii"); flagLength = r[i++]; flags = r.slice(i, (i += flagLength)); return [2 /*return*/, { name: name, version: version, flags: flags }]; } }); }); }; Conflux.prototype.getAppConfiguration = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this._getAppConfiguration()]; }); }); }; /** * You can sign a message according to cfx_sign RPC call and retrieve v, r, s given the message and the BIP 32 path of the account to sign. * @example cfx.signPersonalMessage("44'/503'/0'/0/0", Buffer.from("test").toString("hex")) * @param path hdPath * @param messageHex the hex string of the message * @returns */ Conflux.prototype.signPersonalMessage = function (path, messageHex) { var _this = this; var paths = splitPath(path); var offset = 0; var message = Buffer.from(messageHex, "hex"); var toSend = []; var response; var _loop_2 = function () { var maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 - 4 - 4 : 150; var chunkSize = offset + maxChunkSize > message.length ? message.length - offset : maxChunkSize; var buffer = Buffer.alloc(offset === 0 ? 1 + paths.length * 4 + 4 + 4 + chunkSize : chunkSize); if (offset === 0) { buffer[0] = paths.length; paths.forEach(function (element, index) { buffer.writeUInt32BE(element, 1 + 4 * index); }); buffer.writeUInt32BE(this_1.chainId, 1 + paths.length * 4); buffer.writeUInt32BE(message.length, 1 + 4 * paths.length + 4); message.copy(buffer, 1 + 4 * paths.length + 4 + 4, offset, offset + chunkSize); } else { message.copy(buffer, 0, offset, offset + chunkSize); } toSend.push(buffer); offset += chunkSize; }; var this_1 = this; while (offset !== message.length) { _loop_2(); } return foreach(toSend, function (data, i) { return _this.transport .send(0xe0, INS.SIGN_PERSONAL_MESSAGE, i === 0 ? 0x00 : 0x80, 0x00, data) .then(function (apduResponse) { response = apduResponse; }); }).then(function () { var v = response[0]; var r = response.slice(1, 1 + 32).toString("hex"); var s = response.slice(1 + 32, 1 + 32 + 32).toString("hex"); return { v: v, r: r, s: s }; }); }; Conflux.prototype.pathToBuffer = function (originalPath) { var path = originalPath .split("/") .map(function (value) { return value.endsWith("'") || value.endsWith("h") ? value : value + "'"; }) .join("/"); var pathNums = BIPPath.fromString(path).toPathArray(); return this.serializePath(pathNums); }; Conflux.prototype.serializePath = function (path) { var e_1, _a; var buf = Buffer.alloc(1 + path.length * 4); buf.writeUInt8(path.length, 0); try { for (var _b = __values(path.entries()), _c = _b.next(); !_c.done; _c = _b.next()) { var _d = __read(_c.value, 2), i = _d[0], num = _d[1]; buf.writeUInt32BE(num, 1 + i * 4); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b["return"])) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return buf; }; return Conflux; }()); export default Conflux; //# sourceMappingURL=Conflux.js.map