From 959c73b547dd09f1c4e41ece276f6fce49fbe45c Mon Sep 17 00:00:00 2001
From: coderofstuff <114628839+coderofstuff@users.noreply.github.com>
Date: Tue, 26 Dec 2023 00:00:14 -0700
Subject: [PATCH] Full conversion to typescript (#5)
* Full conversion to typescript
* Fix export logic
* README fixes
* Version bump to 1.1.0
While this looks like a significant change, the code is semantically the same as before
getAddress is a typo and has been renamed to the correct getPublicKey. I'm the only user of this repo at this time and I'll be changing all references to the correct one when updating to this version so I consider this all a minor change.
Breaking change:
- getAddress is now getPublicKey (which is what it should've been in the first place)
---
.gitignore | 3 +-
README.md | 18 +-
index.js | 9 -
jest.config.ts | 10 +
lib-es/index.d.ts | 4 +
lib-es/index.d.ts.map | 1 +
lib-es/index.js | 4 +
lib-es/index.js.map | 1 +
lib-es/kaspa.d.ts | 60 ++++
lib-es/kaspa.d.ts.map | 1 +
lib-es/kaspa.js | 171 ++++++++++++
lib-es/kaspa.js.map | 1 +
lib-es/transaction.d.ts | 86 ++++++
lib-es/transaction.d.ts.map | 1 +
{src => lib-es}/transaction.js | 70 ++---
lib-es/transaction.js.map | 1 +
lib/index.d.ts | 4 +
lib/index.d.ts.map | 1 +
lib/index.js | 13 +
lib/index.js.map | 1 +
lib/kaspa.d.ts | 60 ++++
lib/kaspa.d.ts.map | 1 +
lib/kaspa.js | 173 ++++++++++++
lib/kaspa.js.map | 1 +
lib/transaction.d.ts | 86 ++++++
lib/transaction.d.ts.map | 1 +
lib/transaction.js | 148 ++++++++++
lib/transaction.js.map | 1 +
package-lock.json | 261 +++++++++++++++++-
package.json | 14 +-
src/index.ts | 4 +
src/{kaspa.js => kaspa.ts} | 56 ++--
src/transaction.ts | 206 ++++++++++++++
tests/kaspa.test.js | 220 ---------------
tests/kaspa.test.ts | 485 +++++++++++++++++++++++++++++++++
tsconfig.json | 25 ++
36 files changed, 1864 insertions(+), 338 deletions(-)
delete mode 100644 index.js
create mode 100644 jest.config.ts
create mode 100644 lib-es/index.d.ts
create mode 100644 lib-es/index.d.ts.map
create mode 100644 lib-es/index.js
create mode 100644 lib-es/index.js.map
create mode 100644 lib-es/kaspa.d.ts
create mode 100644 lib-es/kaspa.d.ts.map
create mode 100644 lib-es/kaspa.js
create mode 100644 lib-es/kaspa.js.map
create mode 100644 lib-es/transaction.d.ts
create mode 100644 lib-es/transaction.d.ts.map
rename {src => lib-es}/transaction.js (83%)
create mode 100644 lib-es/transaction.js.map
create mode 100644 lib/index.d.ts
create mode 100644 lib/index.d.ts.map
create mode 100644 lib/index.js
create mode 100644 lib/index.js.map
create mode 100644 lib/kaspa.d.ts
create mode 100644 lib/kaspa.d.ts.map
create mode 100644 lib/kaspa.js
create mode 100644 lib/kaspa.js.map
create mode 100644 lib/transaction.d.ts
create mode 100644 lib/transaction.d.ts.map
create mode 100644 lib/transaction.js
create mode 100644 lib/transaction.js.map
create mode 100644 src/index.ts
rename src/{kaspa.js => kaspa.ts} (76%)
create mode 100644 src/transaction.ts
delete mode 100644 tests/kaspa.test.js
create mode 100644 tests/kaspa.test.ts
create mode 100644 tsconfig.json
diff --git a/.gitignore b/.gitignore
index 40b878d..11d85a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-node_modules/
\ No newline at end of file
+node_modules/
+coverage
\ No newline at end of file
diff --git a/README.md b/README.md
index bf10c36..a8bb1a7 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Ledger Hardware Wallet Kaspa JavaScript bindings.
* [Kaspa](#kaspa)
* [Parameters](#parameters)
* [Examples](#examples)
- * [getAddress](#getaddress)
+ * [getPublicKey](#getaddress)
* [Parameters](#parameters-1)
* [Examples](#examples-1)
* [signTransaction](#signtransaction)
@@ -32,13 +32,13 @@ Kaspa API
#### Examples
```javascript
-import Kaspa from "@ledgerhq/hw-app-kaspa";
+import Kaspa from "hw-app-kaspa";
const kaspa = new Kaspa(transport);
```
-#### getAddress
+#### getPublicKey
-Get Kaspa address (public key) for a BIP32 path.
+Get Kaspa Public Key for a BIP32 path.
##### Parameters
@@ -48,10 +48,10 @@ Get Kaspa address (public key) for a BIP32 path.
##### Examples
```javascript
-kaspa.getAddress("44'/111111'/0'").then(r => r.address)
+kaspa.getPublicKey("44'/111111'/0'")
```
-Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<{address: [Buffer](https://nodejs.org/api/buffer.html)}>** an object with the address field
+Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** the public key buffer with chain code
#### signTransaction
@@ -63,9 +63,9 @@ Sign a Kaspa transaction.
##### Examples
-```javascript
-const Kaspa = require("../src/kaspa");
-const { TransactionInput, TransactionOutput, Transaction } = require("../src/transaction");
+```typescript
+import Kaspa from 'hw-app-kaspa';
+import { TransactionInput, TransactionOutput, Transaction } from 'hw-app-kaspa';
...
diff --git a/index.js b/index.js
deleted file mode 100644
index 8d25359..0000000
--- a/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const Kaspa = require('./src/kaspa');
-const {TransactionInput, TransactionOutput, Transaction} = require('./src/transaction');
-
-module.exports = {
- Kaspa,
- TransactionInput,
- TransactionOutput,
- Transaction,
-};
diff --git a/jest.config.ts b/jest.config.ts
new file mode 100644
index 0000000..bb23d25
--- /dev/null
+++ b/jest.config.ts
@@ -0,0 +1,10 @@
+export default {
+ preset: "ts-jest",
+ testEnvironment: "node",
+ testRegex: ".test.ts$",
+ collectCoverage: true,
+ testPathIgnorePatterns: ["packages/*/lib-es", "packages/*/lib"],
+ coveragePathIgnorePatterns: ["packages/create-dapp"],
+ passWithNoTests: true,
+ rootDir: __dirname,
+};
\ No newline at end of file
diff --git a/lib-es/index.d.ts b/lib-es/index.d.ts
new file mode 100644
index 0000000..c6c9ab6
--- /dev/null
+++ b/lib-es/index.d.ts
@@ -0,0 +1,4 @@
+import Kaspa from './kaspa';
+export { TransactionInput, TransactionOutput, Transaction } from './transaction';
+export default Kaspa;
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/lib-es/index.d.ts.map b/lib-es/index.d.ts.map
new file mode 100644
index 0000000..3040d4a
--- /dev/null
+++ b/lib-es/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAC,MAAM,eAAe,CAAC;AAE/E,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/lib-es/index.js b/lib-es/index.js
new file mode 100644
index 0000000..f2b3419
--- /dev/null
+++ b/lib-es/index.js
@@ -0,0 +1,4 @@
+import Kaspa from './kaspa';
+export { TransactionInput, TransactionOutput, Transaction } from './transaction';
+export default Kaspa;
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/lib-es/index.js.map b/lib-es/index.js.map
new file mode 100644
index 0000000..d37acd5
--- /dev/null
+++ b/lib-es/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAC,MAAM,eAAe,CAAC;AAE/E,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/lib-es/kaspa.d.ts b/lib-es/kaspa.d.ts
new file mode 100644
index 0000000..16b2eb5
--- /dev/null
+++ b/lib-es/kaspa.d.ts
@@ -0,0 +1,60 @@
+///
+import Transport from "@ledgerhq/hw-transport";
+import { Transaction } from "./transaction";
+declare class Kaspa {
+ /**
+ * @type {Transport}
+ */
+ transport: Transport;
+ constructor(transport: Transport);
+ /**
+ * Get Kaspa address (public key) for a BIP32 path.
+ *
+ * @param {string} path a BIP32 path
+ * @param {boolean} display flag to show display
+ * @returns {Buffer} an object with the address field
+ *
+ * @example
+ * kaspa.getPublicKey("44'/111111'/0'").then(r => r.address)
+ */
+ getPublicKey(path: any, display?: boolean): Promise;
+ /**
+ * Sign a Kaspa transaction. Applies the signatures into the input objects
+ *
+ * @param {Transaction} transaction - the Transaction object
+ *
+ *
+ * @example
+ * kaspa.signTransaction(transaction)
+ */
+ signTransaction(transaction: Transaction): Promise;
+ /**
+ * Sign personal message on the device
+ * @param {String} message - the personal message string to sign. Max 120 len for Nano S, 200 len for others
+ * @param {0|1} addressType
+ * @param {number} addressIndex
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.signMessage(message).then(r => r.version)
+ */
+ signMessage(message: string, addressType: 0 | 1, addressIndex: number): Promise<{
+ signature: string;
+ messageHash: string;
+ }>;
+ /**
+ * Get application configuration.
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.getVersion().then(r => r.version)
+ */
+ getVersion(): Promise<{
+ version: string;
+ }>;
+ sendToDevice(instruction: any, p1: any, payload?: Buffer, p2?: number): Promise;
+}
+export default Kaspa;
+//# sourceMappingURL=kaspa.d.ts.map
\ No newline at end of file
diff --git a/lib-es/kaspa.d.ts.map b/lib-es/kaspa.d.ts.map
new file mode 100644
index 0000000..d6f2ffc
--- /dev/null
+++ b/lib-es/kaspa.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"kaspa.d.ts","sourceRoot":"","sources":["../src/kaspa.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAG/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAwC5C,cAAM,KAAK;IACP;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC;gBAET,SAAS,EAAE,SAAS;IAShC;;;;;;;;;OASG;IACG,YAAY,CAAC,IAAI,KAAA,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAUnE;;;;;;;;OAQG;IACG,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C9D;;;;;;;;;;OAUG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,GAAC,CAAC,EAAE,YAAY,EAAE,MAAM;;;;IA+BzE;;;;;;;OAOG;IACG,UAAU;;;IAMV,YAAY,CAAC,WAAW,KAAA,EAAE,EAAE,KAAA,EAAE,OAAO,SAAkB,EAAE,EAAE,SAAU;CAc9E;AAED,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/lib-es/kaspa.js b/lib-es/kaspa.js
new file mode 100644
index 0000000..34a3b9b
--- /dev/null
+++ b/lib-es/kaspa.js
@@ -0,0 +1,171 @@
+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());
+ });
+};
+import { StatusCodes } from "@ledgerhq/errors";
+const BIP32Path = require("bip32-path");
+// Get Address
+const P1_NON_CONFIRM = 0x00;
+const P1_CONFIRM = 0x01;
+// Sign Transaction
+const P1_HEADER = 0x00;
+const P1_OUTPUTS = 0x01;
+const P1_INPUTS = 0x02;
+const P1_NEXT_SIGNATURE = 0x03;
+const P2_LAST = 0x00;
+const P2_MORE = 0x80;
+const LEDGER_CLA = 0xe0;
+const INS = {
+ GET_VERSION: 0x04,
+ GET_ADDRESS: 0x05,
+ SIGN_TX: 0x06,
+ SIGN_MESSAGE: 0x07,
+};
+function pathToBuffer(originalPath) {
+ const pathNums = BIP32Path.fromString(originalPath).toPathArray();
+ return serializePath(pathNums);
+}
+function serializePath(path) {
+ const buf = Buffer.alloc(1 + path.length * 4);
+ buf.writeUInt8(path.length, 0);
+ for (const [i, num] of path.entries()) {
+ buf.writeUInt32BE(num, 1 + i * 4);
+ }
+ return buf;
+}
+class Kaspa {
+ constructor(transport) {
+ this.transport = transport;
+ this.transport.decorateAppAPIMethods(this, [
+ "getVersion",
+ "getAddress",
+ "signTransaction",
+ ], "");
+ }
+ /**
+ * Get Kaspa address (public key) for a BIP32 path.
+ *
+ * @param {string} path a BIP32 path
+ * @param {boolean} display flag to show display
+ * @returns {Buffer} an object with the address field
+ *
+ * @example
+ * kaspa.getPublicKey("44'/111111'/0'").then(r => r.address)
+ */
+ getPublicKey(path, display = false) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const pathBuffer = pathToBuffer(path);
+ const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
+ const publicKeyBuffer = yield this.sendToDevice(INS.GET_ADDRESS, p1, pathBuffer);
+ return publicKeyBuffer;
+ });
+ }
+ /**
+ * Sign a Kaspa transaction. Applies the signatures into the input objects
+ *
+ * @param {Transaction} transaction - the Transaction object
+ *
+ *
+ * @example
+ * kaspa.signTransaction(transaction)
+ */
+ signTransaction(transaction) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const header = transaction.serialize();
+ yield this.sendToDevice(INS.SIGN_TX, P1_HEADER, header, P2_MORE);
+ for (const output of transaction.outputs) {
+ yield this.sendToDevice(INS.SIGN_TX, P1_OUTPUTS, output.serialize(), P2_MORE);
+ }
+ let signatureBuffer = null;
+ for (let i = 0; i < transaction.inputs.length; i++) {
+ let p2 = i >= transaction.inputs.length - 1 ? P2_LAST : P2_MORE;
+ const input = transaction.inputs[i];
+ signatureBuffer = yield this.sendToDevice(INS.SIGN_TX, P1_INPUTS, input.serialize(), p2);
+ }
+ while (signatureBuffer) {
+ const [hasMore, inputIndex, sigLen, ...signatureAndSighash] = signatureBuffer;
+ const sigBuf = signatureAndSighash.slice(0, sigLen);
+ const sighashLen = signatureAndSighash[64];
+ const sighashBuf = signatureAndSighash.slice(65, 65 + sighashLen);
+ if (sigLen != 64) {
+ throw new Error(`Expected signature length is 64. Received ${sigLen} for input ${inputIndex}`);
+ }
+ if (sighashLen != 32) {
+ throw new Error(`Expected sighash length is 32. Received ${sighashLen} for input ${inputIndex}`);
+ }
+ transaction.inputs[inputIndex].setSignature(Buffer.from(sigBuf).toString("hex"));
+ transaction.inputs[inputIndex].setSighash(Buffer.from(sighashBuf).toString("hex"));
+ // Keep going as long as hasMore is true-ish
+ if (!hasMore) {
+ break;
+ }
+ signatureBuffer = yield this.sendToDevice(INS.SIGN_TX, P1_NEXT_SIGNATURE);
+ }
+ });
+ }
+ /**
+ * Sign personal message on the device
+ * @param {String} message - the personal message string to sign. Max 120 len for Nano S, 200 len for others
+ * @param {0|1} addressType
+ * @param {number} addressIndex
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.signMessage(message).then(r => r.version)
+ */
+ signMessage(message, addressType, addressIndex) {
+ return __awaiter(this, void 0, void 0, function* () {
+ if (addressIndex < 0 || addressIndex > 0xFFFFFFFF) {
+ throw new Error('Address index must be an integer in range [0, 0xFFFFFFFF]');
+ }
+ const addressTypeBuf = Buffer.alloc(1);
+ addressTypeBuf.writeUInt8(addressType || 0);
+ const addressIndexBuf = Buffer.alloc(4);
+ addressIndexBuf.writeUInt32BE(addressIndex || 0);
+ const messageBuffer = Buffer.from(message);
+ const messageLenBuf = Buffer.alloc(1);
+ messageLenBuf.writeUInt8(messageBuffer.length);
+ const payload = Buffer.concat([
+ addressTypeBuf,
+ addressIndexBuf,
+ messageLenBuf,
+ messageBuffer,
+ ]);
+ const signatureBuffer = yield this.sendToDevice(INS.SIGN_MESSAGE, P1_NON_CONFIRM, payload);
+ const [sigLen, ...signatureAndMessageHash] = signatureBuffer;
+ const signature = Buffer.from(signatureAndMessageHash.slice(0, sigLen)).toString('hex');
+ const messageHashLen = signatureAndMessageHash[64];
+ const messageHash = Buffer.from(signatureAndMessageHash.slice(65, 65 + messageHashLen)).toString('hex');
+ return { signature, messageHash };
+ });
+ }
+ /**
+ * Get application configuration.
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.getVersion().then(r => r.version)
+ */
+ getVersion() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const [major, minor, patch] = yield this.sendToDevice(INS.GET_VERSION, P1_NON_CONFIRM);
+ return { version: `${major}.${minor}.${patch}` };
+ });
+ }
+ sendToDevice(instruction, p1, payload = Buffer.alloc(0), p2 = P2_LAST) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const acceptStatusList = [StatusCodes.OK];
+ const reply = yield this.transport.send(LEDGER_CLA, instruction, p1, p2, payload, acceptStatusList);
+ return reply.subarray(0, reply.length - 2);
+ });
+ }
+}
+export default Kaspa;
+//# sourceMappingURL=kaspa.js.map
\ No newline at end of file
diff --git a/lib-es/kaspa.js.map b/lib-es/kaspa.js.map
new file mode 100644
index 0000000..fb9473d
--- /dev/null
+++ b/lib-es/kaspa.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"kaspa.js","sourceRoot":"","sources":["../src/kaspa.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lib-es/transaction.d.ts b/lib-es/transaction.d.ts
new file mode 100644
index 0000000..b40c655
--- /dev/null
+++ b/lib-es/transaction.d.ts
@@ -0,0 +1,86 @@
+///
+type TransactionApiJSON = {
+ transaction: {
+ version: number;
+ inputs: TransactionInputApiJSON[];
+ outputs: TransactionOutputApiJSON[];
+ lockTime: number;
+ subnetworkId: string;
+ };
+};
+export declare class Transaction {
+ inputs: TransactionInput[];
+ outputs: TransactionOutput[];
+ version: number;
+ changeAddressType: number;
+ changeAddressIndex: number;
+ constructor(txData: {
+ inputs: TransactionInput[];
+ outputs: TransactionOutput[];
+ version: number;
+ changeAddressType?: number;
+ changeAddressIndex?: number;
+ });
+ serialize(): Buffer;
+ /**
+ * Convert this transaction to a JSON object that api.kaspa.org will accept
+ */
+ toApiJSON(): TransactionApiJSON;
+}
+type TransactionInputApiJSON = {
+ previousOutpoint: {
+ transactionId: string;
+ index: number;
+ };
+ signatureScript: string | null;
+ sequence: number;
+ sigOpCount: number;
+};
+export declare class TransactionInput {
+ signature?: string | null;
+ sighash?: string | null;
+ value: number;
+ prevTxId: string;
+ outpointIndex: number;
+ addressType: number;
+ addressIndex: number;
+ constructor(inputData: {
+ value: number;
+ prevTxId: string;
+ outpointIndex: number;
+ addressType: number;
+ addressIndex: number;
+ });
+ serialize(): Buffer;
+ /**
+ *
+ * @param {string} signature
+ */
+ setSignature(signature: string): void;
+ setSighash(sighash: string): void;
+ toApiJSON(): TransactionInputApiJSON;
+}
+type TransactionOutputApiJSON = {
+ amount: number;
+ scriptPublicKey: {
+ version: number;
+ scriptPublicKey: string;
+ };
+};
+export declare class TransactionOutput {
+ value: number;
+ scriptPublicKey: string;
+ constructor(outputData: {
+ value: number;
+ scriptPublicKey: string;
+ });
+ serialize(): Buffer;
+ toApiJSON(): TransactionOutputApiJSON;
+}
+declare const _default: {
+ Transaction: typeof Transaction;
+ TransactionInput: typeof TransactionInput;
+ TransactionOutput: typeof TransactionOutput;
+};
+export default _default;
+//# sourceMappingURL=transaction.d.ts.map
\ No newline at end of file
diff --git a/lib-es/transaction.d.ts.map b/lib-es/transaction.d.ts.map
new file mode 100644
index 0000000..ebdb2b8
--- /dev/null
+++ b/lib-es/transaction.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":";AAEA,KAAK,kBAAkB,GAAG;IACtB,WAAW,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,uBAAuB,EAAE,CAAC;QAClC,OAAO,EAAE,wBAAwB,EAAE,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAA;CACJ,CAAC;AAEF,qBAAa,WAAW;IACpB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;gBAEf,MAAM,EAAE;QAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAC;IA0BxJ,SAAS,IAAI,MAAM;IAyBnB;;OAEG;IACH,SAAS,IAAI,kBAAkB;CAWlC;AAED,KAAK,uBAAuB,GAAG;IAC3B,gBAAgB,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,MAAM,CAAA;KAChB,CAAC;IACF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;CACxC,CAAC;AAEF,qBAAa,gBAAgB;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;gBAET,SAAS,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC;IAU1H,SAAS,IAAI,MAAM;IAqBnB;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC,SAAS,IAAI,uBAAuB;CAWvC;AAED,KAAK,wBAAwB,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;KAC3B,CAAA;CACJ,CAAC;AAEF,qBAAa,iBAAiB;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;gBAEZ,UAAU,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAC;IAUhE,SAAS,IAAI,MAAM;IAQnB,SAAS,IAAI,wBAAwB;CASxC;;;;;;AAED,wBAIE"}
\ No newline at end of file
diff --git a/src/transaction.js b/lib-es/transaction.js
similarity index 83%
rename from src/transaction.js
rename to lib-es/transaction.js
index b136127..08a69eb 100644
--- a/src/transaction.js
+++ b/lib-es/transaction.js
@@ -1,7 +1,7 @@
-const BN = require("bn.js");
-
-class Transaction {
- constructor(txData = {}) {
+import BN from "bn.js";
+export class Transaction {
+ constructor(txData) {
+ var _a, _b;
/**
* @type {TransactionInput[]}
*/
@@ -14,35 +14,26 @@ class Transaction {
* @type {int}
*/
this.version = txData.version;
-
- this.changeAddressType = Number.isInteger(txData.changeAddressType) ? txData.changeAddressType : 0;
- this.changeAddressIndex = Number.isInteger(txData.changeAddressIndex) ? txData.changeAddressIndex : 0;
-
+ this.changeAddressType = (_a = txData.changeAddressType) !== null && _a !== void 0 ? _a : 0;
+ this.changeAddressIndex = (_b = txData.changeAddressIndex) !== null && _b !== void 0 ? _b : 0;
if (!(this.changeAddressType === 0 || this.changeAddressType === 1)) {
throw new Error(`changeAddressType must be 0 or 1 if set`);
}
-
if (this.changeAddressIndex < 0x00000000 || this.changeAddressIndex > 0xFFFFFFFF) {
throw new Error(`changeAddressIndex must be between 0x00000000 and 0xFFFFFFFF`);
}
}
-
serialize() {
const versionBuf = Buffer.alloc(2);
versionBuf.writeUInt16BE(this.version);
-
const outputLenBuf = Buffer.alloc(1);
outputLenBuf.writeUInt8(this.outputs.length);
-
const inputLenBuf = Buffer.alloc(1);
inputLenBuf.writeUInt8(this.inputs.length);
-
const changeAddressTypeBuf = Buffer.alloc(1);
- changeAddressTypeBuf.writeUInt8(this.changeAddressType || 0);
-
+ changeAddressTypeBuf.writeUInt8(this.changeAddressType);
const changeAddressIndexBuf = Buffer.alloc(4);
- changeAddressIndexBuf.writeUInt32BE(this.changeAddressIndex || 0);
-
+ changeAddressIndexBuf.writeUInt32BE(this.changeAddressIndex);
return Buffer.concat([
versionBuf,
outputLenBuf,
@@ -51,7 +42,6 @@ class Transaction {
changeAddressIndexBuf,
]);
}
-
/**
* Convert this transaction to a JSON object that api.kaspa.org will accept
*/
@@ -67,38 +57,24 @@ class Transaction {
};
}
}
-
-class TransactionInput {
- /**
- * @type {str}
- */
- signature;
-
- /**
- * @type {str}
- */
- sighash;
-
- constructor(inputData = {}) {
+export class TransactionInput {
+ constructor(inputData) {
this.value = inputData.value;
this.prevTxId = inputData.prevTxId;
this.outpointIndex = inputData.outpointIndex;
this.addressType = inputData.addressType;
this.addressIndex = inputData.addressIndex;
+ this.signature = null;
+ this.sighash = null;
}
-
serialize() {
const valueBuf = Buffer.from(new BN(this.value).toArray('BE', 8));
-
const addressTypeBuf = Buffer.alloc(1);
addressTypeBuf.writeUInt8(this.addressType);
-
const addressIndexBuf = Buffer.alloc(4);
addressIndexBuf.writeUInt32BE(this.addressIndex);
-
const outpointIndexBuf = Buffer.alloc(1);
outpointIndexBuf.writeUInt8(this.outpointIndex);
-
return Buffer.concat([
valueBuf,
Buffer.from(this.prevTxId, 'hex'),
@@ -107,19 +83,16 @@ class TransactionInput {
outpointIndexBuf,
]);
}
-
/**
- *
- * @param {string} signature
+ *
+ * @param {string} signature
*/
setSignature(signature) {
this.signature = signature;
}
-
setSighash(sighash) {
this.sighash = sighash;
}
-
toApiJSON() {
return {
previousOutpoint: {
@@ -132,21 +105,15 @@ class TransactionInput {
};
}
}
-
-class TransactionOutput {
- constructor(outputData = {}) {
+export class TransactionOutput {
+ constructor(outputData) {
if (!outputData.value || outputData.value < 0 || outputData.value > 0xFFFFFFFFFFFFFFFF) {
throw new Error('value must be set to a value greater than 0 and less than 0xFFFFFFFFFFFFFFFF');
}
this.value = outputData.value;
- if (!outputData.scriptPublicKey) {
- throw new Error('scriptPublicKey must be set');
- }
-
// Only then do we care about the script public key
this.scriptPublicKey = outputData.scriptPublicKey;
}
-
serialize() {
const valueBuf = Buffer.from(new BN(this.value).toArray('BE', 8));
return Buffer.concat([
@@ -154,7 +121,6 @@ class TransactionOutput {
Buffer.from(this.scriptPublicKey, 'hex'),
]);
}
-
toApiJSON() {
return {
amount: this.value,
@@ -165,9 +131,9 @@ class TransactionOutput {
};
}
}
-
-module.exports = {
+export default {
Transaction,
TransactionInput,
TransactionOutput,
};
+//# sourceMappingURL=transaction.js.map
\ No newline at end of file
diff --git a/lib-es/transaction.js.map b/lib-es/transaction.js.map
new file mode 100644
index 0000000..f9470f2
--- /dev/null
+++ b/lib-es/transaction.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/index.d.ts b/lib/index.d.ts
new file mode 100644
index 0000000..c6c9ab6
--- /dev/null
+++ b/lib/index.d.ts
@@ -0,0 +1,4 @@
+import Kaspa from './kaspa';
+export { TransactionInput, TransactionOutput, Transaction } from './transaction';
+export default Kaspa;
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/lib/index.d.ts.map b/lib/index.d.ts.map
new file mode 100644
index 0000000..3040d4a
--- /dev/null
+++ b/lib/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAC,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAC,MAAM,eAAe,CAAC;AAE/E,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..59bd041
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,13 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Transaction = exports.TransactionOutput = exports.TransactionInput = void 0;
+const kaspa_1 = __importDefault(require("./kaspa"));
+var transaction_1 = require("./transaction");
+Object.defineProperty(exports, "TransactionInput", { enumerable: true, get: function () { return transaction_1.TransactionInput; } });
+Object.defineProperty(exports, "TransactionOutput", { enumerable: true, get: function () { return transaction_1.TransactionOutput; } });
+Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
+exports.default = kaspa_1.default;
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/lib/index.js.map b/lib/index.js.map
new file mode 100644
index 0000000..edae1ef
--- /dev/null
+++ b/lib/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAC5B,6CAA+E;AAAvE,+GAAA,gBAAgB,OAAA;AAAE,gHAAA,iBAAiB,OAAA;AAAE,0GAAA,WAAW,OAAA;AAExD,kBAAe,eAAK,CAAC"}
\ No newline at end of file
diff --git a/lib/kaspa.d.ts b/lib/kaspa.d.ts
new file mode 100644
index 0000000..16b2eb5
--- /dev/null
+++ b/lib/kaspa.d.ts
@@ -0,0 +1,60 @@
+///
+import Transport from "@ledgerhq/hw-transport";
+import { Transaction } from "./transaction";
+declare class Kaspa {
+ /**
+ * @type {Transport}
+ */
+ transport: Transport;
+ constructor(transport: Transport);
+ /**
+ * Get Kaspa address (public key) for a BIP32 path.
+ *
+ * @param {string} path a BIP32 path
+ * @param {boolean} display flag to show display
+ * @returns {Buffer} an object with the address field
+ *
+ * @example
+ * kaspa.getPublicKey("44'/111111'/0'").then(r => r.address)
+ */
+ getPublicKey(path: any, display?: boolean): Promise;
+ /**
+ * Sign a Kaspa transaction. Applies the signatures into the input objects
+ *
+ * @param {Transaction} transaction - the Transaction object
+ *
+ *
+ * @example
+ * kaspa.signTransaction(transaction)
+ */
+ signTransaction(transaction: Transaction): Promise;
+ /**
+ * Sign personal message on the device
+ * @param {String} message - the personal message string to sign. Max 120 len for Nano S, 200 len for others
+ * @param {0|1} addressType
+ * @param {number} addressIndex
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.signMessage(message).then(r => r.version)
+ */
+ signMessage(message: string, addressType: 0 | 1, addressIndex: number): Promise<{
+ signature: string;
+ messageHash: string;
+ }>;
+ /**
+ * Get application configuration.
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.getVersion().then(r => r.version)
+ */
+ getVersion(): Promise<{
+ version: string;
+ }>;
+ sendToDevice(instruction: any, p1: any, payload?: Buffer, p2?: number): Promise;
+}
+export default Kaspa;
+//# sourceMappingURL=kaspa.d.ts.map
\ No newline at end of file
diff --git a/lib/kaspa.d.ts.map b/lib/kaspa.d.ts.map
new file mode 100644
index 0000000..d6f2ffc
--- /dev/null
+++ b/lib/kaspa.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"kaspa.d.ts","sourceRoot":"","sources":["../src/kaspa.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAG/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAwC5C,cAAM,KAAK;IACP;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC;gBAET,SAAS,EAAE,SAAS;IAShC;;;;;;;;;OASG;IACG,YAAY,CAAC,IAAI,KAAA,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAUnE;;;;;;;;OAQG;IACG,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C9D;;;;;;;;;;OAUG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,GAAC,CAAC,EAAE,YAAY,EAAE,MAAM;;;;IA+BzE;;;;;;;OAOG;IACG,UAAU;;;IAMV,YAAY,CAAC,WAAW,KAAA,EAAE,EAAE,KAAA,EAAE,OAAO,SAAkB,EAAE,EAAE,SAAU;CAc9E;AAED,eAAe,KAAK,CAAC"}
\ No newline at end of file
diff --git a/lib/kaspa.js b/lib/kaspa.js
new file mode 100644
index 0000000..b31933c
--- /dev/null
+++ b/lib/kaspa.js
@@ -0,0 +1,173 @@
+"use strict";
+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());
+ });
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const errors_1 = require("@ledgerhq/errors");
+const BIP32Path = require("bip32-path");
+// Get Address
+const P1_NON_CONFIRM = 0x00;
+const P1_CONFIRM = 0x01;
+// Sign Transaction
+const P1_HEADER = 0x00;
+const P1_OUTPUTS = 0x01;
+const P1_INPUTS = 0x02;
+const P1_NEXT_SIGNATURE = 0x03;
+const P2_LAST = 0x00;
+const P2_MORE = 0x80;
+const LEDGER_CLA = 0xe0;
+const INS = {
+ GET_VERSION: 0x04,
+ GET_ADDRESS: 0x05,
+ SIGN_TX: 0x06,
+ SIGN_MESSAGE: 0x07,
+};
+function pathToBuffer(originalPath) {
+ const pathNums = BIP32Path.fromString(originalPath).toPathArray();
+ return serializePath(pathNums);
+}
+function serializePath(path) {
+ const buf = Buffer.alloc(1 + path.length * 4);
+ buf.writeUInt8(path.length, 0);
+ for (const [i, num] of path.entries()) {
+ buf.writeUInt32BE(num, 1 + i * 4);
+ }
+ return buf;
+}
+class Kaspa {
+ constructor(transport) {
+ this.transport = transport;
+ this.transport.decorateAppAPIMethods(this, [
+ "getVersion",
+ "getAddress",
+ "signTransaction",
+ ], "");
+ }
+ /**
+ * Get Kaspa address (public key) for a BIP32 path.
+ *
+ * @param {string} path a BIP32 path
+ * @param {boolean} display flag to show display
+ * @returns {Buffer} an object with the address field
+ *
+ * @example
+ * kaspa.getPublicKey("44'/111111'/0'").then(r => r.address)
+ */
+ getPublicKey(path, display = false) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const pathBuffer = pathToBuffer(path);
+ const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
+ const publicKeyBuffer = yield this.sendToDevice(INS.GET_ADDRESS, p1, pathBuffer);
+ return publicKeyBuffer;
+ });
+ }
+ /**
+ * Sign a Kaspa transaction. Applies the signatures into the input objects
+ *
+ * @param {Transaction} transaction - the Transaction object
+ *
+ *
+ * @example
+ * kaspa.signTransaction(transaction)
+ */
+ signTransaction(transaction) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const header = transaction.serialize();
+ yield this.sendToDevice(INS.SIGN_TX, P1_HEADER, header, P2_MORE);
+ for (const output of transaction.outputs) {
+ yield this.sendToDevice(INS.SIGN_TX, P1_OUTPUTS, output.serialize(), P2_MORE);
+ }
+ let signatureBuffer = null;
+ for (let i = 0; i < transaction.inputs.length; i++) {
+ let p2 = i >= transaction.inputs.length - 1 ? P2_LAST : P2_MORE;
+ const input = transaction.inputs[i];
+ signatureBuffer = yield this.sendToDevice(INS.SIGN_TX, P1_INPUTS, input.serialize(), p2);
+ }
+ while (signatureBuffer) {
+ const [hasMore, inputIndex, sigLen, ...signatureAndSighash] = signatureBuffer;
+ const sigBuf = signatureAndSighash.slice(0, sigLen);
+ const sighashLen = signatureAndSighash[64];
+ const sighashBuf = signatureAndSighash.slice(65, 65 + sighashLen);
+ if (sigLen != 64) {
+ throw new Error(`Expected signature length is 64. Received ${sigLen} for input ${inputIndex}`);
+ }
+ if (sighashLen != 32) {
+ throw new Error(`Expected sighash length is 32. Received ${sighashLen} for input ${inputIndex}`);
+ }
+ transaction.inputs[inputIndex].setSignature(Buffer.from(sigBuf).toString("hex"));
+ transaction.inputs[inputIndex].setSighash(Buffer.from(sighashBuf).toString("hex"));
+ // Keep going as long as hasMore is true-ish
+ if (!hasMore) {
+ break;
+ }
+ signatureBuffer = yield this.sendToDevice(INS.SIGN_TX, P1_NEXT_SIGNATURE);
+ }
+ });
+ }
+ /**
+ * Sign personal message on the device
+ * @param {String} message - the personal message string to sign. Max 120 len for Nano S, 200 len for others
+ * @param {0|1} addressType
+ * @param {number} addressIndex
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.signMessage(message).then(r => r.version)
+ */
+ signMessage(message, addressType, addressIndex) {
+ return __awaiter(this, void 0, void 0, function* () {
+ if (addressIndex < 0 || addressIndex > 0xFFFFFFFF) {
+ throw new Error('Address index must be an integer in range [0, 0xFFFFFFFF]');
+ }
+ const addressTypeBuf = Buffer.alloc(1);
+ addressTypeBuf.writeUInt8(addressType || 0);
+ const addressIndexBuf = Buffer.alloc(4);
+ addressIndexBuf.writeUInt32BE(addressIndex || 0);
+ const messageBuffer = Buffer.from(message);
+ const messageLenBuf = Buffer.alloc(1);
+ messageLenBuf.writeUInt8(messageBuffer.length);
+ const payload = Buffer.concat([
+ addressTypeBuf,
+ addressIndexBuf,
+ messageLenBuf,
+ messageBuffer,
+ ]);
+ const signatureBuffer = yield this.sendToDevice(INS.SIGN_MESSAGE, P1_NON_CONFIRM, payload);
+ const [sigLen, ...signatureAndMessageHash] = signatureBuffer;
+ const signature = Buffer.from(signatureAndMessageHash.slice(0, sigLen)).toString('hex');
+ const messageHashLen = signatureAndMessageHash[64];
+ const messageHash = Buffer.from(signatureAndMessageHash.slice(65, 65 + messageHashLen)).toString('hex');
+ return { signature, messageHash };
+ });
+ }
+ /**
+ * Get application configuration.
+ *
+ * @returns {Buffer} application config object
+ *
+ * @example
+ * kaspa.getVersion().then(r => r.version)
+ */
+ getVersion() {
+ return __awaiter(this, void 0, void 0, function* () {
+ const [major, minor, patch] = yield this.sendToDevice(INS.GET_VERSION, P1_NON_CONFIRM);
+ return { version: `${major}.${minor}.${patch}` };
+ });
+ }
+ sendToDevice(instruction, p1, payload = Buffer.alloc(0), p2 = P2_LAST) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const acceptStatusList = [errors_1.StatusCodes.OK];
+ const reply = yield this.transport.send(LEDGER_CLA, instruction, p1, p2, payload, acceptStatusList);
+ return reply.subarray(0, reply.length - 2);
+ });
+ }
+}
+exports.default = Kaspa;
+//# sourceMappingURL=kaspa.js.map
\ No newline at end of file
diff --git a/lib/kaspa.js.map b/lib/kaspa.js.map
new file mode 100644
index 0000000..e91c41e
--- /dev/null
+++ b/lib/kaspa.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"kaspa.js","sourceRoot":"","sources":["../src/kaspa.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/transaction.d.ts b/lib/transaction.d.ts
new file mode 100644
index 0000000..b40c655
--- /dev/null
+++ b/lib/transaction.d.ts
@@ -0,0 +1,86 @@
+///
+type TransactionApiJSON = {
+ transaction: {
+ version: number;
+ inputs: TransactionInputApiJSON[];
+ outputs: TransactionOutputApiJSON[];
+ lockTime: number;
+ subnetworkId: string;
+ };
+};
+export declare class Transaction {
+ inputs: TransactionInput[];
+ outputs: TransactionOutput[];
+ version: number;
+ changeAddressType: number;
+ changeAddressIndex: number;
+ constructor(txData: {
+ inputs: TransactionInput[];
+ outputs: TransactionOutput[];
+ version: number;
+ changeAddressType?: number;
+ changeAddressIndex?: number;
+ });
+ serialize(): Buffer;
+ /**
+ * Convert this transaction to a JSON object that api.kaspa.org will accept
+ */
+ toApiJSON(): TransactionApiJSON;
+}
+type TransactionInputApiJSON = {
+ previousOutpoint: {
+ transactionId: string;
+ index: number;
+ };
+ signatureScript: string | null;
+ sequence: number;
+ sigOpCount: number;
+};
+export declare class TransactionInput {
+ signature?: string | null;
+ sighash?: string | null;
+ value: number;
+ prevTxId: string;
+ outpointIndex: number;
+ addressType: number;
+ addressIndex: number;
+ constructor(inputData: {
+ value: number;
+ prevTxId: string;
+ outpointIndex: number;
+ addressType: number;
+ addressIndex: number;
+ });
+ serialize(): Buffer;
+ /**
+ *
+ * @param {string} signature
+ */
+ setSignature(signature: string): void;
+ setSighash(sighash: string): void;
+ toApiJSON(): TransactionInputApiJSON;
+}
+type TransactionOutputApiJSON = {
+ amount: number;
+ scriptPublicKey: {
+ version: number;
+ scriptPublicKey: string;
+ };
+};
+export declare class TransactionOutput {
+ value: number;
+ scriptPublicKey: string;
+ constructor(outputData: {
+ value: number;
+ scriptPublicKey: string;
+ });
+ serialize(): Buffer;
+ toApiJSON(): TransactionOutputApiJSON;
+}
+declare const _default: {
+ Transaction: typeof Transaction;
+ TransactionInput: typeof TransactionInput;
+ TransactionOutput: typeof TransactionOutput;
+};
+export default _default;
+//# sourceMappingURL=transaction.d.ts.map
\ No newline at end of file
diff --git a/lib/transaction.d.ts.map b/lib/transaction.d.ts.map
new file mode 100644
index 0000000..ebdb2b8
--- /dev/null
+++ b/lib/transaction.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":";AAEA,KAAK,kBAAkB,GAAG;IACtB,WAAW,EAAE;QACT,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,uBAAuB,EAAE,CAAC;QAClC,OAAO,EAAE,wBAAwB,EAAE,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAA;CACJ,CAAC;AAEF,qBAAa,WAAW;IACpB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;gBAEf,MAAM,EAAE;QAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAC;IA0BxJ,SAAS,IAAI,MAAM;IAyBnB;;OAEG;IACH,SAAS,IAAI,kBAAkB;CAWlC;AAED,KAAK,uBAAuB,GAAG;IAC3B,gBAAgB,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,MAAM,CAAA;KAChB,CAAC;IACF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;CACxC,CAAC;AAEF,qBAAa,gBAAgB;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;gBAET,SAAS,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC;IAU1H,SAAS,IAAI,MAAM;IAqBnB;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC,SAAS,IAAI,uBAAuB;CAWvC;AAED,KAAK,wBAAwB,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;KAC3B,CAAA;CACJ,CAAC;AAEF,qBAAa,iBAAiB;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;gBAEZ,UAAU,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAC;IAUhE,SAAS,IAAI,MAAM;IAQnB,SAAS,IAAI,wBAAwB;CASxC;;;;;;AAED,wBAIE"}
\ No newline at end of file
diff --git a/lib/transaction.js b/lib/transaction.js
new file mode 100644
index 0000000..1565eee
--- /dev/null
+++ b/lib/transaction.js
@@ -0,0 +1,148 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.TransactionOutput = exports.TransactionInput = exports.Transaction = void 0;
+const bn_js_1 = __importDefault(require("bn.js"));
+class Transaction {
+ constructor(txData) {
+ var _a, _b;
+ /**
+ * @type {TransactionInput[]}
+ */
+ this.inputs = txData.inputs;
+ /**
+ * @type {TransactionOutput[]}
+ */
+ this.outputs = txData.outputs;
+ /**
+ * @type {int}
+ */
+ this.version = txData.version;
+ this.changeAddressType = (_a = txData.changeAddressType) !== null && _a !== void 0 ? _a : 0;
+ this.changeAddressIndex = (_b = txData.changeAddressIndex) !== null && _b !== void 0 ? _b : 0;
+ if (!(this.changeAddressType === 0 || this.changeAddressType === 1)) {
+ throw new Error(`changeAddressType must be 0 or 1 if set`);
+ }
+ if (this.changeAddressIndex < 0x00000000 || this.changeAddressIndex > 0xFFFFFFFF) {
+ throw new Error(`changeAddressIndex must be between 0x00000000 and 0xFFFFFFFF`);
+ }
+ }
+ serialize() {
+ const versionBuf = Buffer.alloc(2);
+ versionBuf.writeUInt16BE(this.version);
+ const outputLenBuf = Buffer.alloc(1);
+ outputLenBuf.writeUInt8(this.outputs.length);
+ const inputLenBuf = Buffer.alloc(1);
+ inputLenBuf.writeUInt8(this.inputs.length);
+ const changeAddressTypeBuf = Buffer.alloc(1);
+ changeAddressTypeBuf.writeUInt8(this.changeAddressType);
+ const changeAddressIndexBuf = Buffer.alloc(4);
+ changeAddressIndexBuf.writeUInt32BE(this.changeAddressIndex);
+ return Buffer.concat([
+ versionBuf,
+ outputLenBuf,
+ inputLenBuf,
+ changeAddressTypeBuf,
+ changeAddressIndexBuf,
+ ]);
+ }
+ /**
+ * Convert this transaction to a JSON object that api.kaspa.org will accept
+ */
+ toApiJSON() {
+ return {
+ transaction: {
+ version: this.version,
+ inputs: this.inputs.map((i) => i.toApiJSON()),
+ outputs: this.outputs.map((o) => o.toApiJSON()),
+ lockTime: 0,
+ subnetworkId: '0000000000000000000000000000000000000000',
+ },
+ };
+ }
+}
+exports.Transaction = Transaction;
+class TransactionInput {
+ constructor(inputData) {
+ this.value = inputData.value;
+ this.prevTxId = inputData.prevTxId;
+ this.outpointIndex = inputData.outpointIndex;
+ this.addressType = inputData.addressType;
+ this.addressIndex = inputData.addressIndex;
+ this.signature = null;
+ this.sighash = null;
+ }
+ serialize() {
+ const valueBuf = Buffer.from(new bn_js_1.default(this.value).toArray('BE', 8));
+ const addressTypeBuf = Buffer.alloc(1);
+ addressTypeBuf.writeUInt8(this.addressType);
+ const addressIndexBuf = Buffer.alloc(4);
+ addressIndexBuf.writeUInt32BE(this.addressIndex);
+ const outpointIndexBuf = Buffer.alloc(1);
+ outpointIndexBuf.writeUInt8(this.outpointIndex);
+ return Buffer.concat([
+ valueBuf,
+ Buffer.from(this.prevTxId, 'hex'),
+ addressTypeBuf,
+ addressIndexBuf,
+ outpointIndexBuf,
+ ]);
+ }
+ /**
+ *
+ * @param {string} signature
+ */
+ setSignature(signature) {
+ this.signature = signature;
+ }
+ setSighash(sighash) {
+ this.sighash = sighash;
+ }
+ toApiJSON() {
+ return {
+ previousOutpoint: {
+ transactionId: this.prevTxId,
+ index: this.outpointIndex,
+ },
+ signatureScript: this.signature ? `41${this.signature}01` : null,
+ sequence: 0,
+ sigOpCount: 1
+ };
+ }
+}
+exports.TransactionInput = TransactionInput;
+class TransactionOutput {
+ constructor(outputData) {
+ if (!outputData.value || outputData.value < 0 || outputData.value > 0xFFFFFFFFFFFFFFFF) {
+ throw new Error('value must be set to a value greater than 0 and less than 0xFFFFFFFFFFFFFFFF');
+ }
+ this.value = outputData.value;
+ // Only then do we care about the script public key
+ this.scriptPublicKey = outputData.scriptPublicKey;
+ }
+ serialize() {
+ const valueBuf = Buffer.from(new bn_js_1.default(this.value).toArray('BE', 8));
+ return Buffer.concat([
+ valueBuf,
+ Buffer.from(this.scriptPublicKey, 'hex'),
+ ]);
+ }
+ toApiJSON() {
+ return {
+ amount: this.value,
+ scriptPublicKey: {
+ version: 0,
+ scriptPublicKey: this.scriptPublicKey,
+ },
+ };
+ }
+}
+exports.TransactionOutput = TransactionOutput;
+exports.default = {
+ Transaction,
+ TransactionInput,
+ TransactionOutput,
+};
+//# sourceMappingURL=transaction.js.map
\ No newline at end of file
diff --git a/lib/transaction.js.map b/lib/transaction.js.map
new file mode 100644
index 0000000..f6b4cab
--- /dev/null
+++ b/lib/transaction.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index ffcd1c8..63a3364 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "hw-app-kaspa",
- "version": "1.0.0",
+ "version": "1.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "hw-app-kaspa",
- "version": "1.0.0",
+ "version": "1.1.0",
"license": "ISC",
"dependencies": {
"@ledgerhq/errors": "^6.14.0",
@@ -17,7 +17,11 @@
"devDependencies": {
"@ledgerhq/hw-transport-mocker": "^6.27.19",
"@ledgerhq/hw-transport-node-speculos": "^6.27.19",
- "jest": "^29.7.0"
+ "@types/jest": "^29.5.11",
+ "@types/node": "^20.10.5",
+ "jest": "^29.7.0",
+ "ts-jest": "^29.1.1",
+ "ts-node": "^10.9.2"
}
},
"node_modules/@ampproject/remapping": {
@@ -673,6 +677,28 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1101,6 +1127,30 @@
"@sinonjs/commons": "^3.0.0"
}
},
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true
+ },
"node_modules/@types/babel__core": {
"version": "7.20.3",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz",
@@ -1175,13 +1225,23 @@
"@types/istanbul-lib-report": "*"
}
},
+ "node_modules/@types/jest": {
+ "version": "29.5.11",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz",
+ "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==",
+ "dev": true,
+ "dependencies": {
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
+ }
+ },
"node_modules/@types/node": {
- "version": "20.8.7",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz",
- "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==",
+ "version": "20.10.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz",
+ "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==",
"dev": true,
"dependencies": {
- "undici-types": "~5.25.1"
+ "undici-types": "~5.26.4"
}
},
"node_modules/@types/stack-utils": {
@@ -1205,6 +1265,27 @@
"integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==",
"dev": true
},
+ "node_modules/acorn": {
+ "version": "8.11.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
+ "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz",
+ "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -1257,6 +1338,12 @@
"node": ">= 8"
}
},
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -1452,6 +1539,18 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/bs-logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
+ "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+ "dev": true,
+ "dependencies": {
+ "fast-json-stable-stringify": "2.x"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@@ -1632,6 +1731,12 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1695,6 +1800,15 @@
"node": ">=8"
}
},
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@@ -2804,6 +2918,12 @@
"node": ">=8"
}
},
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -2828,6 +2948,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
"node_modules/makeerror": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@@ -3458,6 +3584,92 @@
"node": ">=8.0"
}
},
+ "node_modules/ts-jest": {
+ "version": "29.1.1",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
+ "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==",
+ "dev": true,
+ "dependencies": {
+ "bs-logger": "0.x",
+ "fast-json-stable-stringify": "2.x",
+ "jest-util": "^29.0.0",
+ "json5": "^2.2.3",
+ "lodash.memoize": "4.x",
+ "make-error": "1.x",
+ "semver": "^7.5.3",
+ "yargs-parser": "^21.0.1"
+ },
+ "bin": {
+ "ts-jest": "cli.js"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": ">=7.0.0-beta.0 <8",
+ "@jest/types": "^29.0.0",
+ "babel-jest": "^29.0.0",
+ "jest": "^29.0.0",
+ "typescript": ">=4.3 <6"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "@jest/types": {
+ "optional": true
+ },
+ "babel-jest": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@@ -3484,10 +3696,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/typescript": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+ "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
"node_modules/undici-types": {
- "version": "5.25.3",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz",
- "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/update-browserslist-db": {
@@ -3520,6 +3746,12 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
"node_modules/v8-to-istanbul": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz",
@@ -3636,6 +3868,15 @@
"node": ">=12"
}
},
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index d462034..8763b88 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,13 @@
{
"name": "hw-app-kaspa",
- "version": "1.0.0",
+ "version": "1.1.0",
"description": "",
- "main": "index.js",
+ "main": "lib/index.js",
+ "module": "lib-es/index.js",
+ "types": "lib/index.d.ts",
"scripts": {
+ "build": "tsc && tsc -m ES6 --outDir lib-es",
+ "watch": "tsc --watch",
"test": "jest"
},
"repository": {
@@ -28,6 +32,10 @@
"devDependencies": {
"@ledgerhq/hw-transport-mocker": "^6.27.19",
"@ledgerhq/hw-transport-node-speculos": "^6.27.19",
- "jest": "^29.7.0"
+ "@types/jest": "^29.5.11",
+ "@types/node": "^20.10.5",
+ "jest": "^29.7.0",
+ "ts-jest": "^29.1.1",
+ "ts-node": "^10.9.2"
}
}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..3788903
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,4 @@
+import Kaspa from './kaspa';
+export {TransactionInput, TransactionOutput, Transaction} from './transaction';
+
+export default Kaspa;
diff --git a/src/kaspa.js b/src/kaspa.ts
similarity index 76%
rename from src/kaspa.js
rename to src/kaspa.ts
index eedbd5a..464e682 100644
--- a/src/kaspa.js
+++ b/src/kaspa.ts
@@ -1,11 +1,10 @@
-const Transport = require("@ledgerhq/hw-transport").default;
+import Transport from "@ledgerhq/hw-transport";
+import { StatusCodes } from "@ledgerhq/errors";
-const { StatusCodes } = require("@ledgerhq/errors");
+import { Transaction } from "./transaction";
const BIP32Path = require("bip32-path");
-const {Transaction} = require('./transaction');
-
// Get Address
const P1_NON_CONFIRM = 0x00;
const P1_CONFIRM = 0x01;
@@ -46,18 +45,15 @@ class Kaspa {
/**
* @type {Transport}
*/
- transport = null;
+ transport: Transport;
- constructor(transport) {
- if (!(transport instanceof Transport)) {
- throw new Error("transport must be an instance of @ledgerhq/hw-transport");
- }
+ constructor(transport: Transport) {
this.transport = transport;
this.transport.decorateAppAPIMethods(this, [
"getVersion",
"getAddress",
"signTransaction",
- ]);
+ ], "");
}
/**
@@ -68,18 +64,16 @@ class Kaspa {
* @returns {Buffer} an object with the address field
*
* @example
- * kaspa.getAddress("44'/111111'/0'").then(r => r.address)
+ * kaspa.getPublicKey("44'/111111'/0'").then(r => r.address)
*/
- async getAddress(path, display) {
+ async getPublicKey(path, display: boolean = false): Promise {
const pathBuffer = pathToBuffer(path);
const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
- const addressBuffer = await this.sendToDevice(INS.GET_ADDRESS, p1, pathBuffer);
+ const publicKeyBuffer = await this.sendToDevice(INS.GET_ADDRESS, p1, pathBuffer);
- return {
- address: addressBuffer,
- };
+ return publicKeyBuffer;
}
/**
@@ -89,13 +83,9 @@ class Kaspa {
*
*
* @example
- * kaspa.signTransaction(transaction).then(r => r.signature)
+ * kaspa.signTransaction(transaction)
*/
- async signTransaction(transaction) {
- if (!(transaction instanceof Transaction)) {
- throw new Error("transaction must be an instance of Transaction");
- }
-
+ async signTransaction(transaction: Transaction): Promise {
const header = transaction.serialize();
await this.sendToDevice(INS.SIGN_TX, P1_HEADER, header, P2_MORE);
@@ -104,7 +94,7 @@ class Kaspa {
await this.sendToDevice(INS.SIGN_TX, P1_OUTPUTS, output.serialize(), P2_MORE);
}
- let signatureBuffer = null;
+ let signatureBuffer: Buffer | null = null;
for (let i = 0; i < transaction.inputs.length; i++) {
let p2 = i >= transaction.inputs.length - 1 ? P2_LAST : P2_MORE;
@@ -126,8 +116,8 @@ class Kaspa {
throw new Error(`Expected sighash length is 32. Received ${sighashLen} for input ${inputIndex}`)
}
- transaction.inputs[inputIndex].setSignature(Buffer(sigBuf).toString("hex"));
- transaction.inputs[inputIndex].setSighash(Buffer(sighashBuf).toString("hex"));
+ transaction.inputs[inputIndex].setSignature(Buffer.from(sigBuf).toString("hex"));
+ transaction.inputs[inputIndex].setSighash(Buffer.from(sighashBuf).toString("hex"));
// Keep going as long as hasMore is true-ish
if (!hasMore) {
@@ -141,18 +131,16 @@ class Kaspa {
/**
* Sign personal message on the device
* @param {String} message - the personal message string to sign. Max 120 len for Nano S, 200 len for others
+ * @param {0|1} addressType
+ * @param {number} addressIndex
*
* @returns {Buffer} application config object
*
* @example
* kaspa.signMessage(message).then(r => r.version)
*/
- async signMessage(message, addressType, addressIndex) {
- if (addressType !== 0 && addressType !== 1) {
- throw new Error('Address type must be 0 or 1');
- }
-
- if (isNaN(addressIndex) || addressIndex < 0 || addressIndex > 0xFFFFFFFF) {
+ async signMessage(message: string, addressType: 0|1, addressIndex: number) {
+ if (addressIndex < 0 || addressIndex > 0xFFFFFFFF) {
throw new Error('Address index must be an integer in range [0, 0xFFFFFFFF]');
}
@@ -175,9 +163,9 @@ class Kaspa {
const signatureBuffer = await this.sendToDevice(INS.SIGN_MESSAGE, P1_NON_CONFIRM, payload);
const [sigLen, ...signatureAndMessageHash] = signatureBuffer;
- const signature = Buffer(signatureAndMessageHash.slice(0, sigLen)).toString('hex');
+ const signature = Buffer.from(signatureAndMessageHash.slice(0, sigLen)).toString('hex');
const messageHashLen = signatureAndMessageHash[64];
- const messageHash = Buffer(signatureAndMessageHash.slice(65, 65 + messageHashLen)).toString('hex');
+ const messageHash = Buffer.from(signatureAndMessageHash.slice(65, 65 + messageHashLen)).toString('hex');
return { signature, messageHash };
}
@@ -212,4 +200,4 @@ class Kaspa {
}
}
-module.exports = Kaspa;
+export default Kaspa;
diff --git a/src/transaction.ts b/src/transaction.ts
new file mode 100644
index 0000000..9c9c058
--- /dev/null
+++ b/src/transaction.ts
@@ -0,0 +1,206 @@
+import BN from "bn.js";
+
+type TransactionApiJSON = {
+ transaction: {
+ version: number,
+ inputs: TransactionInputApiJSON[],
+ outputs: TransactionOutputApiJSON[],
+ lockTime: number,
+ subnetworkId: string,
+ }
+};
+
+export class Transaction {
+ inputs: TransactionInput[];
+ outputs: TransactionOutput[];
+ version: number;
+ changeAddressType: number;
+ changeAddressIndex: number;
+
+ constructor(txData: {inputs: TransactionInput[], outputs: TransactionOutput[], version: number, changeAddressType?: number, changeAddressIndex?: number}) {
+ /**
+ * @type {TransactionInput[]}
+ */
+ this.inputs = txData.inputs;
+ /**
+ * @type {TransactionOutput[]}
+ */
+ this.outputs = txData.outputs;
+ /**
+ * @type {int}
+ */
+ this.version = txData.version;
+
+ this.changeAddressType = txData.changeAddressType ?? 0;
+ this.changeAddressIndex = txData.changeAddressIndex ?? 0;
+
+ if (!(this.changeAddressType === 0 || this.changeAddressType === 1)) {
+ throw new Error(`changeAddressType must be 0 or 1 if set`);
+ }
+
+ if (this.changeAddressIndex < 0x00000000 || this.changeAddressIndex > 0xFFFFFFFF) {
+ throw new Error(`changeAddressIndex must be between 0x00000000 and 0xFFFFFFFF`);
+ }
+ }
+
+ serialize(): Buffer {
+ const versionBuf = Buffer.alloc(2);
+ versionBuf.writeUInt16BE(this.version);
+
+ const outputLenBuf = Buffer.alloc(1);
+ outputLenBuf.writeUInt8(this.outputs.length);
+
+ const inputLenBuf = Buffer.alloc(1);
+ inputLenBuf.writeUInt8(this.inputs.length);
+
+ const changeAddressTypeBuf = Buffer.alloc(1);
+ changeAddressTypeBuf.writeUInt8(this.changeAddressType);
+
+ const changeAddressIndexBuf = Buffer.alloc(4);
+ changeAddressIndexBuf.writeUInt32BE(this.changeAddressIndex);
+
+ return Buffer.concat([
+ versionBuf,
+ outputLenBuf,
+ inputLenBuf,
+ changeAddressTypeBuf,
+ changeAddressIndexBuf,
+ ]);
+ }
+
+ /**
+ * Convert this transaction to a JSON object that api.kaspa.org will accept
+ */
+ toApiJSON(): TransactionApiJSON {
+ return {
+ transaction: {
+ version: this.version,
+ inputs: this.inputs.map((i) => i.toApiJSON()),
+ outputs: this.outputs.map((o) => o.toApiJSON()),
+ lockTime: 0,
+ subnetworkId: '0000000000000000000000000000000000000000',
+ },
+ };
+ }
+}
+
+type TransactionInputApiJSON = {
+ previousOutpoint: {
+ transactionId: string,
+ index: number
+ },
+ signatureScript: string | null,
+ sequence: number, sigOpCount: number,
+};
+
+export class TransactionInput {
+ signature?: string | null;
+ sighash?: string | null;
+ value: number;
+ prevTxId: string;
+ outpointIndex: number;
+ addressType: number;
+ addressIndex: number;
+
+ constructor(inputData: {value: number, prevTxId: string, outpointIndex: number, addressType: number, addressIndex: number}) {
+ this.value = inputData.value;
+ this.prevTxId = inputData.prevTxId;
+ this.outpointIndex = inputData.outpointIndex;
+ this.addressType = inputData.addressType;
+ this.addressIndex = inputData.addressIndex;
+ this.signature = null;
+ this.sighash = null;
+ }
+
+ serialize(): Buffer {
+ const valueBuf = Buffer.from(new BN(this.value).toArray('BE', 8));
+
+ const addressTypeBuf = Buffer.alloc(1);
+ addressTypeBuf.writeUInt8(this.addressType);
+
+ const addressIndexBuf = Buffer.alloc(4);
+ addressIndexBuf.writeUInt32BE(this.addressIndex);
+
+ const outpointIndexBuf = Buffer.alloc(1);
+ outpointIndexBuf.writeUInt8(this.outpointIndex);
+
+ return Buffer.concat([
+ valueBuf,
+ Buffer.from(this.prevTxId, 'hex'),
+ addressTypeBuf,
+ addressIndexBuf,
+ outpointIndexBuf,
+ ]);
+ }
+
+ /**
+ *
+ * @param {string} signature
+ */
+ setSignature(signature: string): void {
+ this.signature = signature;
+ }
+
+ setSighash(sighash: string): void {
+ this.sighash = sighash;
+ }
+
+ toApiJSON(): TransactionInputApiJSON {
+ return {
+ previousOutpoint: {
+ transactionId: this.prevTxId,
+ index: this.outpointIndex,
+ },
+ signatureScript: this.signature ? `41${this.signature}01` : null,
+ sequence: 0,
+ sigOpCount: 1
+ };
+ }
+}
+
+type TransactionOutputApiJSON = {
+ amount: number,
+ scriptPublicKey: {
+ version: number,
+ scriptPublicKey: string,
+ }
+};
+
+export class TransactionOutput {
+ value: number;
+ scriptPublicKey: string;
+
+ constructor(outputData: {value: number, scriptPublicKey: string}) {
+ if (!outputData.value || outputData.value < 0 || outputData.value > 0xFFFFFFFFFFFFFFFF) {
+ throw new Error('value must be set to a value greater than 0 and less than 0xFFFFFFFFFFFFFFFF');
+ }
+ this.value = outputData.value;
+
+ // Only then do we care about the script public key
+ this.scriptPublicKey = outputData.scriptPublicKey;
+ }
+
+ serialize(): Buffer {
+ const valueBuf: Buffer = Buffer.from(new BN(this.value).toArray('BE', 8));
+ return Buffer.concat([
+ valueBuf,
+ Buffer.from(this.scriptPublicKey, 'hex'),
+ ]);
+ }
+
+ toApiJSON(): TransactionOutputApiJSON {
+ return {
+ amount: this.value,
+ scriptPublicKey: {
+ version: 0,
+ scriptPublicKey: this.scriptPublicKey,
+ },
+ };
+ }
+}
+
+export default {
+ Transaction,
+ TransactionInput,
+ TransactionOutput,
+};
diff --git a/tests/kaspa.test.js b/tests/kaspa.test.js
deleted file mode 100644
index d834703..0000000
--- a/tests/kaspa.test.js
+++ /dev/null
@@ -1,220 +0,0 @@
-const {
- openTransportReplayer,
- RecordStore,
-} = require("@ledgerhq/hw-transport-mocker");
-const Kaspa = require("../src/kaspa");
-const { TransactionInput, TransactionOutput, Transaction } = require("../src/transaction");
-
-describe("kaspa", () => {
- it("getVersion", async () => {
- const transport = await openTransportReplayer(
- RecordStore.fromString(`
- => e004000000
- <= 0105069000
- `)
- );
- const kaspa = new Kaspa(transport);
- const result = await kaspa.getVersion();
- expect(result).toEqual({
- version: "1.5.6"
- });
- });
-
- it("getAddress without display", async () => {
- const transport = await openTransportReplayer(
- RecordStore.fromString(`
- => e005000015058000002c8001b207800000000000000000000000
- <= deadbeef9000
- `)
- );
- const kaspa = new Kaspa(transport);
- const { address } = await kaspa.getAddress("44'/111111'/0'/0/0", false);
- expect(address.toString("hex")).toEqual("deadbeef");
- });
-
- it("getAddress with display", async () => {
- const transport = await openTransportReplayer(
- RecordStore.fromString(`
- => e005010015058000002c8001b207800000000000000000000000
- <= deadbeef9000
- `)
- );
- const kaspa = new Kaspa(transport);
- const { address } = await kaspa.getAddress("44'/111111'/0'/0/0", true);
- expect(address.toString("hex")).toEqual("deadbeef");
- });
-
- it("signTransaction with simple data", async () => {
- const transport = await openTransportReplayer(
- RecordStore.fromString(`
- => e006008009000001010102030405
- <= 9000
- => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
- <= 9000
- => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
- <= 000040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
- `)
- );
- const kaspa = new Kaspa(transport);
-
- const txin = new TransactionInput({
- prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
- value: 1100000,
- addressType: 0,
- addressIndex: 0,
- outpointIndex: 0,
- });
-
- const txout = new TransactionOutput({
- value: 1090000,
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- });
-
- const tx = new Transaction({
- version: 0,
- inputs: [txin],
- outputs: [txout],
- changeAddressType: 1,
- changeAddressIndex: 0x02030405,
- });
-
- await kaspa.signTransaction(tx);
- expect(txin.signature).toEqual("ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3");
- expect(txin.sighash).toEqual("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
- });
-
- it("signMessage with simple data", async () => {
- const expectedSignature = 'ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3';
- const expectedMessageHash = '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff';
- const transport = await openTransportReplayer(
- RecordStore.fromString(`
- => e00700001200000000000c48656c6c6f204b6173706121
- <= 40${expectedSignature}20${expectedMessageHash}9000
- `)
- );
- const kaspa = new Kaspa(transport);
-
- try {
- const { signature, messageHash } = await kaspa.signMessage('Hello Kaspa!', 0, 0);
- expect(signature).toEqual(expectedSignature);
- expect(messageHash).toEqual(messageHash);
- } catch (e) {
- console.error(e);
- expect(e).toBe(null);
- }
- });
-});
-
-describe("Transaction", () => {
- it("should serialize", () => {
- const txin = new TransactionInput({
- prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
- value: 1100000,
- addressType: 0,
- addressIndex: 0,
- outpointIndex: 0,
- });
-
- const txout = new TransactionOutput({
- value: 1090000,
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- });
-
- const tx = new Transaction({
- version: 0,
- inputs: [txin],
- outputs: [txout],
- changeAddressType: 1,
- changeAddressIndex: 0x02030405
- });
-
- const expectation = Buffer.from([0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05]);
-
- expect(tx.serialize().equals(expectation)).toBeTruthy();
- });
-});
-
-describe("TransactionOutput", () => {
- it("should throw no error if only scriptPublicKey and value is set", () => {
- let err = null;
- try {
- new TransactionOutput({
- value: 1090000,
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- });
- } catch (e) {
- err = e;
- }
-
- expect(err).toBe(null);
- });
-
- it("should serialize value and scriptPublicKey", () => {
- const serial = new TransactionOutput({
- value: 1090000,
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- }).serialize();
-
- const expectation = Buffer.from([
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa1, 0xd0,
- 0x20, 0x11, 0xa7, 0x21, 0x5f, 0x66, 0x8e, 0x92,
- 0x10, 0x13, 0xeb, 0x7a, 0xac, 0x9b, 0x7e, 0x64,
- 0xb9, 0xec, 0x6e, 0x75, 0x7c, 0x1b, 0x64, 0x8e,
- 0x89, 0x38, 0x8c, 0x91, 0x9f, 0x67, 0x6a, 0xa8,
- 0x8c, 0xac,
- ]);
-
- expect(serial.equals(expectation)).toBeTruthy();
- });
-
- it("should throw errors if none of scriptPublicKey or (addressType, addressIndex) are set", () => {
- let err = null;
- try {
- new TransactionOutput({
- value: 1090000
- });
- } catch (e) {
- err = e;
- }
-
- expect(err).not.toBe(null);
- });
-
- it("should throw errors if value is not set", () => {
- let err = null;
- try {
- new TransactionOutput({
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- });
- } catch (e) {
- err = e;
- }
-
- expect(err).not.toBe(null);
- });
-
- it("should throw errors if value is < 0 or > 0xFFFFFFFFFFFFFFFF", () => {
- let err = null;
- try {
- new TransactionOutput({
- value: 0,
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- });
- } catch (e) {
- err = e;
- }
-
- expect(err).not.toBe(null);
-
- try {
- new TransactionOutput({
- value: 0xFFFFFFFFFFFFFFFF + 1,
- scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
- });
- } catch (e) {
- err = e;
- }
-
- expect(err).not.toBe(null);
- });
-});
diff --git a/tests/kaspa.test.ts b/tests/kaspa.test.ts
new file mode 100644
index 0000000..407b666
--- /dev/null
+++ b/tests/kaspa.test.ts
@@ -0,0 +1,485 @@
+import {
+ openTransportReplayer,
+ RecordStore,
+ } from "@ledgerhq/hw-transport-mocker";
+import Kaspa from "../src/kaspa";
+import { TransactionInput, TransactionOutput, Transaction } from "../src/transaction";
+
+describe("kaspa", () => {
+ it("getVersion", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e004000000
+ <= 0105069000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+ const result = await kaspa.getVersion();
+ expect(result).toEqual({
+ version: "1.5.6"
+ });
+ });
+
+ it("getPublicKey without display (implicit)", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e005000015058000002c8001b207800000000000000000000000
+ <= deadbeef9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+ const publicKey = await kaspa.getPublicKey("44'/111111'/0'/0/0");
+ expect(publicKey.toString("hex")).toEqual("deadbeef");
+ });
+
+ it("getPublicKey without display", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e005000015058000002c8001b207800000000000000000000000
+ <= deadbeef9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+ const publicKey = await kaspa.getPublicKey("44'/111111'/0'/0/0", false);
+ expect(publicKey.toString("hex")).toEqual("deadbeef");
+ });
+
+ it("getPublicKey with display", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e005010015058000002c8001b207800000000000000000000000
+ <= deadbeef9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+ const publicKey = await kaspa.getPublicKey("44'/111111'/0'/0/0", true);
+ expect(publicKey.toString("hex")).toEqual("deadbeef");
+ });
+
+ it("signTransaction with simple data", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e006008009000001010102030405
+ <= 9000
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
+ <= 9000
+ => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
+ <= 000040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ const tx = new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 1,
+ changeAddressIndex: 0x02030405,
+ });
+
+ await kaspa.signTransaction(tx);
+ expect(txin.signature).toEqual("ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3");
+ expect(txin.sighash).toEqual("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
+ });
+
+ it("signTransaction with multi inputs", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e006008009000001020102030405
+ <= 9000
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
+ <= 9000
+ => e00602802e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
+ <= 9000
+ => e00602002e000000000010c8e040aa22362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
+ <= 010040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
+ => e006030000
+ <= 000140b33f7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+
+ const txin1 = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txin2 = new TransactionInput({
+ prevTxId: "40aa22362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ const tx = new Transaction({
+ version: 0,
+ inputs: [txin1, txin2],
+ outputs: [txout],
+ changeAddressType: 1,
+ changeAddressIndex: 0x02030405,
+ });
+
+ await kaspa.signTransaction(tx);
+ expect(txin1.signature).toEqual("ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3");
+ expect(txin1.sighash).toEqual("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
+
+ expect(txin2.signature).toEqual("b33f7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3");
+ expect(txin2.sighash).toEqual("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
+ });
+
+ it("signTransaction should always have sigLen = 64", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e006008009000001010102030405
+ <= 9000
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
+ <= 9000
+ => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
+ <= 00004100ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ const tx = new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 1,
+ changeAddressIndex: 0x02030405,
+ });
+
+ let err: any = null;
+ try {
+ await kaspa.signTransaction(tx);
+ } catch (e) {
+ err = e;
+ }
+ });
+
+ it("signTransaction should always have sighashLen = 32", async () => {
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e006008009000001010102030405
+ <= 9000
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
+ <= 9000
+ => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
+ <= 000040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3210000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ const tx = new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 1,
+ changeAddressIndex: 0x02030405,
+ });
+
+ let err: any = null;
+ try {
+ await kaspa.signTransaction(tx);
+ } catch (e) {
+ err = e;
+ }
+ });
+
+ it("Transaction should set default change address index and type", () => {
+ const tx = new Transaction({
+ version: 0,
+ inputs: [],
+ outputs: [],
+ });
+
+ expect(tx.changeAddressIndex).toEqual(0);
+ expect(tx.changeAddressType).toEqual(0);
+ });
+
+ it("TransactionInput should show null for signatureScript when unsigned", () => {
+ const txin = new TransactionInput({
+ prevTxId: "",
+ value: 0,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ expect(txin.toApiJSON().signatureScript).toEqual(null);
+
+ const expectedSignature = "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3";
+ txin.setSignature(expectedSignature);
+
+ expect(txin.toApiJSON().signatureScript).toEqual(`41${expectedSignature}01`);
+ });
+
+ it("signMessage with simple data", async () => {
+ const expectedSignature = 'ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3';
+ const expectedMessageHash = '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff';
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e00700001200000000000c48656c6c6f204b6173706121
+ <= 40${expectedSignature}20${expectedMessageHash}9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+
+ try {
+ const { signature, messageHash } = await kaspa.signMessage('Hello Kaspa!', 0, 0);
+ expect(signature).toEqual(expectedSignature);
+ expect(messageHash).toEqual(messageHash);
+ } catch (e) {
+ console.error(e);
+ expect(e).toBe(null);
+ }
+ });
+
+ it("signMessage with simple data", async () => {
+ const expectedSignature = 'ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3';
+ const expectedMessageHash = '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff';
+ const transport = await openTransportReplayer(
+ RecordStore.fromString(`
+ => e00700001200000000000c48656c6c6f204b6173706121
+ <= 40${expectedSignature}20${expectedMessageHash}9000
+ `)
+ );
+ const kaspa = new Kaspa(transport);
+
+ let err: any = null;
+ try {
+ const { signature, messageHash } = await kaspa.signMessage('Hello Kaspa!', 0, -1);
+ expect(signature).toEqual(expectedSignature);
+ expect(messageHash).toEqual(messageHash);
+ } catch (e) {
+ err = e;
+ }
+
+ expect(err).not.toBe(null);
+ });
+});
+
+describe("Transaction", () => {
+ it("should serialize", () => {
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ const tx = new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 1,
+ changeAddressIndex: 0x02030405
+ });
+
+ const expectation = Buffer.from([0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05]);
+
+ expect(tx.serialize().equals(expectation)).toBeTruthy();
+ });
+
+ it("should check that change address type is 0 or 1", () => {
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ let err: any = null;
+ try {
+ new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 2,
+ changeAddressIndex: 0x02030405
+ });
+ } catch (e) {
+ err = e;
+ }
+
+ expect(err).not.toBe(null);
+ });
+
+ it("should check that change address index to be in range", () => {
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ let err: any = null;
+ try {
+ new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 0,
+ changeAddressIndex: -1
+ });
+ } catch (e) {
+ err = e;
+ }
+
+ expect(err).not.toBe(null);
+ });
+
+ it("should check that API JSON is producible", () => {
+ const txin = new TransactionInput({
+ prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
+ value: 1100000,
+ addressType: 0,
+ addressIndex: 0,
+ outpointIndex: 0,
+ });
+
+ const txout = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+
+ const tx = new Transaction({
+ version: 0,
+ inputs: [txin],
+ outputs: [txout],
+ changeAddressType: 1,
+ changeAddressIndex: 0x02030405
+ });
+
+ const json = tx.toApiJSON().transaction;
+ expect(json.version).toEqual(0);
+ expect(json.inputs.length).toEqual(1);
+ expect(json.outputs.length).toEqual(1);
+ expect(json.lockTime).toEqual(0);
+ expect(json.subnetworkId).toEqual("0000000000000000000000000000000000000000");
+ });
+});
+
+describe("TransactionOutput", () => {
+ it("should throw no error if only scriptPublicKey and value is set", () => {
+ let err: any = null;
+ try {
+ new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+ } catch (e) {
+ err = e;
+ }
+
+ expect(err).toBe(null);
+ });
+
+ it("should serialize value and scriptPublicKey", () => {
+ const serial = new TransactionOutput({
+ value: 1090000,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ }).serialize();
+
+ const expectation = Buffer.from([
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa1, 0xd0,
+ 0x20, 0x11, 0xa7, 0x21, 0x5f, 0x66, 0x8e, 0x92,
+ 0x10, 0x13, 0xeb, 0x7a, 0xac, 0x9b, 0x7e, 0x64,
+ 0xb9, 0xec, 0x6e, 0x75, 0x7c, 0x1b, 0x64, 0x8e,
+ 0x89, 0x38, 0x8c, 0x91, 0x9f, 0x67, 0x6a, 0xa8,
+ 0x8c, 0xac,
+ ]);
+
+ expect(serial.equals(expectation)).toBeTruthy();
+ });
+
+ it("should throw errors if value is < 0 or > 0xFFFFFFFFFFFFFFFF", () => {
+ let err: any = null;
+ try {
+ new TransactionOutput({
+ value: 0,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+ } catch (e) {
+ err = e;
+ }
+
+ expect(err).not.toBe(null);
+
+ try {
+ new TransactionOutput({
+ value: 0xFFFFFFFFFFFFFFFF + 1,
+ scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
+ });
+ } catch (e) {
+ err = e;
+ }
+
+ expect(err).not.toBe(null);
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..8a36946
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "allowJs": false,
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "moduleResolution": "node",
+ "pretty": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "declaration": true,
+ "declarationMap": true,
+ "downlevelIteration": true,
+ "noImplicitAny": false,
+ "noImplicitThis": false,
+ "module": "commonjs",
+ "lib": ["DOM", "ES2017"],
+ "isolatedModules": false,
+ "target": "ES6",
+ "outDir": "lib"
+ },
+ "include": ["src/**/*"]
+}
\ No newline at end of file