@alephium/ledger-app 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { Account, KeyType } from '@alephium/web3';
3
3
  import Transport from '@ledgerhq/hw-transport';
4
- import { TokenMetadata } from './types';
5
4
  export declare const CLA = 128;
6
5
  export declare enum INS {
7
6
  GET_VERSION = 0,
@@ -11,12 +10,12 @@ export declare enum INS {
11
10
  }
12
11
  export declare const GROUP_NUM = 4;
13
12
  export declare const HASH_LEN = 32;
14
- export default class AlephiumApp {
13
+ export declare class AlephiumApp {
15
14
  readonly transport: Transport;
16
15
  constructor(transport: Transport);
17
16
  close(): Promise<void>;
18
17
  getVersion(): Promise<string>;
19
18
  getAccount(startPath: string, targetGroup?: number, keyType?: KeyType, display?: boolean): Promise<readonly [Account, number]>;
20
19
  signHash(path: string, hash: Buffer): Promise<string>;
21
- signUnsignedTx(path: string, unsignedTx: Buffer, tokenMetadata?: TokenMetadata[]): Promise<string>;
20
+ signUnsignedTx(path: string, unsignedTx: Buffer): Promise<string>;
22
21
  }
@@ -23,11 +23,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.HASH_LEN = exports.GROUP_NUM = exports.INS = exports.CLA = void 0;
26
+ exports.AlephiumApp = exports.HASH_LEN = exports.GROUP_NUM = exports.INS = exports.CLA = void 0;
27
27
  const web3_1 = require("@alephium/web3");
28
28
  const hw_transport_1 = require("@ledgerhq/hw-transport");
29
29
  const serde = __importStar(require("./serde"));
30
30
  const elliptic_1 = require("elliptic");
31
+ const types_1 = require("./types");
32
+ const tx_encoder_1 = require("./tx-encoder");
33
+ const merkle_1 = require("./merkle");
31
34
  const ec = new elliptic_1.ec('secp256k1');
32
35
  exports.CLA = 0x80;
33
36
  var INS;
@@ -39,8 +42,6 @@ var INS;
39
42
  })(INS = exports.INS || (exports.INS = {}));
40
43
  exports.GROUP_NUM = 4;
41
44
  exports.HASH_LEN = 32;
42
- // The maximum payload size is 255: https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/hw-transport/src/Transport.ts#L261
43
- const MAX_PAYLOAD_SIZE = 255;
44
45
  class AlephiumApp {
45
46
  constructor(transport) {
46
47
  this.transport = transport;
@@ -80,30 +81,42 @@ class AlephiumApp {
80
81
  console.log(`response ${response.length} - ${response.toString('hex')}`);
81
82
  return decodeSignature(response);
82
83
  }
83
- async signUnsignedTx(path, unsignedTx, tokenMetadata = []) {
84
+ async signUnsignedTx(path, unsignedTx) {
84
85
  console.log(`unsigned tx size: ${unsignedTx.length}`);
85
- const encodedPath = serde.serializePath(path);
86
- const encodedTokenMetadata = serde.serializeTokenMetadata(tokenMetadata);
87
- const firstFrameTxLength = MAX_PAYLOAD_SIZE - 20 - encodedTokenMetadata.length;
88
- const txData = unsignedTx.slice(0, unsignedTx.length > firstFrameTxLength ? firstFrameTxLength : unsignedTx.length);
89
- const data = Buffer.concat([encodedPath, encodedTokenMetadata, txData]);
90
- let response = await this.transport.send(exports.CLA, INS.SIGN_TX, 0x00, 0x00, data, [hw_transport_1.StatusCodes.OK]);
91
- if (unsignedTx.length <= firstFrameTxLength) {
92
- return decodeSignature(response);
93
- }
94
- const frameLength = MAX_PAYLOAD_SIZE;
95
- let fromIndex = firstFrameTxLength;
96
- while (fromIndex < unsignedTx.length) {
97
- const remain = unsignedTx.length - fromIndex;
98
- const toIndex = remain > frameLength ? (fromIndex + frameLength) : unsignedTx.length;
99
- const data = unsignedTx.slice(fromIndex, toIndex);
100
- response = await this.transport.send(exports.CLA, INS.SIGN_TX, 0x01, 0x00, data, [hw_transport_1.StatusCodes.OK]);
101
- fromIndex = toIndex;
86
+ const tokenMetadata = getTokenMetadata(unsignedTx);
87
+ serde.checkTokenMetadata(tokenMetadata);
88
+ const tokenMetadataFrames = (0, tx_encoder_1.encodeTokenMetadata)(tokenMetadata);
89
+ const txFrames = (0, tx_encoder_1.encodeUnsignedTx)(path, unsignedTx);
90
+ const allFrames = [...tokenMetadataFrames, ...txFrames];
91
+ let response = undefined;
92
+ for (const frame of allFrames) {
93
+ response = await this.transport.send(exports.CLA, INS.SIGN_TX, frame.p1, frame.p2, frame.data, [hw_transport_1.StatusCodes.OK]);
102
94
  }
103
95
  return decodeSignature(response);
104
96
  }
105
97
  }
106
- exports.default = AlephiumApp;
98
+ exports.AlephiumApp = AlephiumApp;
99
+ function getTokenMetadata(unsignedTx) {
100
+ const result = [];
101
+ const outputs = web3_1.codec.unsignedTxCodec.decode(unsignedTx).fixedOutputs;
102
+ outputs.forEach((output) => {
103
+ output.tokens.forEach((t) => {
104
+ const tokenIdHex = (0, web3_1.binToHex)(t.tokenId);
105
+ if (result.find((t) => isTokenIdEqual(t.tokenId, tokenIdHex)) !== undefined) {
106
+ return;
107
+ }
108
+ const metadata = merkle_1.merkleTokens.find((t) => isTokenIdEqual(t.tokenId, tokenIdHex));
109
+ if (metadata !== undefined && metadata.symbol.length <= types_1.MAX_TOKEN_SYMBOL_LENGTH) {
110
+ result.push(metadata);
111
+ }
112
+ });
113
+ });
114
+ const size = Math.min(result.length, types_1.MAX_TOKEN_SIZE);
115
+ return result.slice(0, size);
116
+ }
117
+ function isTokenIdEqual(a, b) {
118
+ return a.toLowerCase() === b.toLowerCase();
119
+ }
107
120
  function decodeSignature(response) {
108
121
  // Decode signature: https://bitcoin.stackexchange.com/a/12556
109
122
  const rLen = response.slice(3, 4)[0];
@@ -0,0 +1,9 @@
1
+ import { TokenMetadata } from './types';
2
+ export declare const merkleTokens: TokenMetadata[];
3
+ export declare function hashPair(a: Uint8Array, b: Uint8Array): Uint8Array;
4
+ export declare function generateProofs(): {
5
+ proofs: Record<string, string>;
6
+ root: string;
7
+ };
8
+ export declare const tokenMerkleRoot: Uint8Array;
9
+ export declare const tokenMerkleProofs: Record<string, string>;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tokenMerkleProofs = exports.tokenMerkleRoot = exports.generateProofs = exports.hashPair = exports.merkleTokens = void 0;
7
+ const web3_1 = require("@alephium/web3");
8
+ const token_json_1 = __importDefault(require("../merkle-tree/token.json"));
9
+ const proofs_json_1 = __importDefault(require("../merkle-tree/proofs.json"));
10
+ const serde_1 = require("./serde");
11
+ const blakejs_1 = require("blakejs");
12
+ exports.merkleTokens = token_json_1.default.tokens.map((token) => {
13
+ return {
14
+ version: 0,
15
+ tokenId: token.id,
16
+ symbol: token.symbol,
17
+ decimals: token.decimals
18
+ };
19
+ });
20
+ function hashPair(a, b) {
21
+ return (0, blakejs_1.blake2b)(Buffer.concat([a, b].sort(Buffer.compare)), undefined, 32);
22
+ }
23
+ exports.hashPair = hashPair;
24
+ function generateMerkleTree(tokens) {
25
+ let level = tokens.map((token) => (0, blakejs_1.blake2b)((0, serde_1.serializeSingleTokenMetadata)(token), undefined, 32));
26
+ const tree = [];
27
+ while (level.length > 1) {
28
+ tree.push(level);
29
+ level = level.reduce((acc, _, i, arr) => {
30
+ if (i % 2 === 0) {
31
+ acc.push(i + 1 < arr.length ? hashPair(arr[i], arr[i + 1]) : arr[i]);
32
+ }
33
+ return acc;
34
+ }, []);
35
+ }
36
+ tree.push(level); // Root
37
+ return tree;
38
+ }
39
+ function generateProofs() {
40
+ const tree = generateMerkleTree(exports.merkleTokens);
41
+ const proofs = exports.merkleTokens.reduce((acc, token, tokenIndex) => {
42
+ const proof = tree.slice(0, -1).reduce((proofAcc, level, levelIndex) => {
43
+ const index = Math.floor(tokenIndex / 2 ** levelIndex);
44
+ const pairIndex = index % 2 === 0 ? index + 1 : index - 1;
45
+ const siblingOrUncle = level[pairIndex];
46
+ if (siblingOrUncle) {
47
+ proofAcc.push(siblingOrUncle);
48
+ }
49
+ return proofAcc;
50
+ }, []);
51
+ acc[token.tokenId] = proof.map((hash) => (0, web3_1.binToHex)(hash)).join('');
52
+ return acc;
53
+ }, {});
54
+ console.log('root', tree[tree.length - 1].map((hash) => (0, web3_1.binToHex)(hash)).join(''));
55
+ return { proofs, root: (0, web3_1.binToHex)(tree[tree.length - 1][0]) };
56
+ }
57
+ exports.generateProofs = generateProofs;
58
+ exports.tokenMerkleRoot = (0, web3_1.hexToBinUnsafe)('b3380866c595544781e9da0ccd79399de8878abfb0bf40545b57a287387d419d');
59
+ exports.tokenMerkleProofs = proofs_json_1.default;
@@ -1,7 +1,9 @@
1
1
  /// <reference types="node" />
2
- import { TokenMetadata } from "./types";
2
+ import { TokenMetadata } from './types';
3
3
  export declare const TRUE = 16;
4
4
  export declare const FALSE = 0;
5
5
  export declare function splitPath(path: string): number[];
6
6
  export declare function serializePath(path: string): Buffer;
7
+ export declare function checkTokenMetadata(tokens: TokenMetadata[]): void;
8
+ export declare function serializeSingleTokenMetadata(metadata: TokenMetadata): Buffer;
7
9
  export declare function serializeTokenMetadata(tokens: TokenMetadata[]): Buffer;
package/dist/src/serde.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serializeTokenMetadata = exports.serializePath = exports.splitPath = exports.FALSE = exports.TRUE = void 0;
3
+ exports.serializeTokenMetadata = exports.serializeSingleTokenMetadata = exports.checkTokenMetadata = exports.serializePath = exports.splitPath = exports.FALSE = exports.TRUE = void 0;
4
4
  const web3_1 = require("@alephium/web3");
5
5
  const types_1 = require("./types");
6
6
  exports.TRUE = 0x10;
@@ -39,7 +39,7 @@ function symbolToBytes(symbol) {
39
39
  }
40
40
  return buffer;
41
41
  }
42
- function check(tokens) {
42
+ function checkTokenMetadata(tokens) {
43
43
  const hasDuplicate = tokens.some((token, index) => index !== tokens.findIndex((t) => t.tokenId === token.tokenId));
44
44
  if (hasDuplicate) {
45
45
  throw new Error(`There are duplicate tokens`);
@@ -56,22 +56,24 @@ function check(tokens) {
56
56
  throw new Error(`The token size exceeds maximum size`);
57
57
  }
58
58
  }
59
+ exports.checkTokenMetadata = checkTokenMetadata;
60
+ function serializeSingleTokenMetadata(metadata) {
61
+ const symbolBytes = symbolToBytes(metadata.symbol);
62
+ const buffer = Buffer.concat([
63
+ Buffer.from([metadata.version]),
64
+ Buffer.from(metadata.tokenId, 'hex'),
65
+ symbolBytes,
66
+ Buffer.from([metadata.decimals]),
67
+ ]);
68
+ if (buffer.length !== types_1.TOKEN_METADATA_SIZE) {
69
+ throw new Error(`Invalid token metadata: ${metadata}`);
70
+ }
71
+ return buffer;
72
+ }
73
+ exports.serializeSingleTokenMetadata = serializeSingleTokenMetadata;
59
74
  function serializeTokenMetadata(tokens) {
60
- check(tokens);
61
- const array = tokens
62
- .map((metadata) => {
63
- const symbolBytes = symbolToBytes(metadata.symbol);
64
- const buffer = Buffer.concat([
65
- Buffer.from([metadata.version]),
66
- Buffer.from(metadata.tokenId, 'hex'),
67
- symbolBytes,
68
- Buffer.from([metadata.decimals])
69
- ]);
70
- if (buffer.length !== types_1.TOKEN_METADATA_SIZE) {
71
- throw new Error(`Invalid token metadata: ${metadata}`);
72
- }
73
- return buffer;
74
- });
75
+ checkTokenMetadata(tokens);
76
+ const array = tokens.map((metadata) => serializeSingleTokenMetadata(metadata));
75
77
  return Buffer.concat([Buffer.from([array.length]), ...array]);
76
78
  }
77
79
  exports.serializeTokenMetadata = serializeTokenMetadata;
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ import { TokenMetadata } from "./types";
3
+ export interface Frame {
4
+ p1: number;
5
+ p2: number;
6
+ data: Buffer;
7
+ }
8
+ export declare function encodeTokenMetadata(tokenMetadata: TokenMetadata[]): Frame[];
9
+ export declare function encodeProofLength(length: number): Uint8Array;
10
+ export declare function encodeUnsignedTx(path: string, unsignedTx: Buffer): Frame[];
11
+ export declare function assert(condition: boolean, msg: string): void;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assert = exports.encodeUnsignedTx = exports.encodeProofLength = exports.encodeTokenMetadata = void 0;
4
+ const merkle_1 = require("./merkle");
5
+ const serde_1 = require("./serde");
6
+ const types_1 = require("./types");
7
+ function encodeTokenMetadata(tokenMetadata) {
8
+ const frames = tokenMetadata.flatMap((metadata, index) => {
9
+ const isFirstToken = index === 0;
10
+ const firstFramePrefix = isFirstToken ? Buffer.from([tokenMetadata.length]) : Buffer.alloc(0);
11
+ const buffers = encodeTokenAndProof(metadata, firstFramePrefix);
12
+ if (buffers.length === 0)
13
+ return [];
14
+ assert(buffers.every((buffer) => buffer.length <= types_1.MAX_PAYLOAD_SIZE), 'Invalid token frame size');
15
+ const frames = [];
16
+ const firstFrameP2 = isFirstToken ? 0 : 1;
17
+ frames.push({ p1: 0, p2: firstFrameP2, data: buffers[0] });
18
+ buffers.slice(1).forEach((data) => frames.push({ p1: 0, p2: 2, data }));
19
+ return frames;
20
+ });
21
+ if (frames.length === 0) {
22
+ return [{ p1: 0, p2: 0, data: Buffer.from([0]) }];
23
+ }
24
+ else {
25
+ return frames;
26
+ }
27
+ }
28
+ exports.encodeTokenMetadata = encodeTokenMetadata;
29
+ function encodeTokenAndProof(tokenMetadata, firstFramePrefix) {
30
+ const proof = merkle_1.tokenMerkleProofs[tokenMetadata.tokenId];
31
+ if (proof === undefined)
32
+ return [];
33
+ const proofBytes = Buffer.from(proof, 'hex');
34
+ const encodedProofLength = encodeProofLength(proofBytes.length);
35
+ const encodedTokenMetadata = (0, serde_1.serializeSingleTokenMetadata)(tokenMetadata);
36
+ const firstFrameRemainSize = types_1.MAX_PAYLOAD_SIZE - encodedTokenMetadata.length - encodedProofLength.length - firstFramePrefix.length;
37
+ const firstFrameProofSize = Math.floor(firstFrameRemainSize / 32) * 32;
38
+ if (firstFrameProofSize >= proofBytes.length) {
39
+ return [Buffer.concat([firstFramePrefix, encodedTokenMetadata, encodedProofLength, proofBytes])];
40
+ }
41
+ const firstFrameProof = proofBytes.slice(0, firstFrameProofSize);
42
+ const result = [Buffer.concat([firstFramePrefix, encodedTokenMetadata, encodedProofLength, firstFrameProof])];
43
+ let from_index = firstFrameProofSize;
44
+ while (from_index < proofBytes.length) {
45
+ const remainProofLength = proofBytes.length - from_index;
46
+ const frameProofSize = Math.min(Math.floor(types_1.MAX_PAYLOAD_SIZE / 32) * 32, remainProofLength);
47
+ const frameProof = proofBytes.slice(from_index, from_index + frameProofSize);
48
+ from_index += frameProofSize;
49
+ result.push(frameProof);
50
+ }
51
+ return result;
52
+ }
53
+ function encodeProofLength(length) {
54
+ assert((length % 32 === 0) && (length > 0 && length < 0xffff), 'Invalid token proof size');
55
+ const buffer = Buffer.alloc(2);
56
+ buffer.writeUint16BE(length);
57
+ return buffer;
58
+ }
59
+ exports.encodeProofLength = encodeProofLength;
60
+ function encodeUnsignedTx(path, unsignedTx) {
61
+ const encodedPath = (0, serde_1.serializePath)(path);
62
+ const firstFrameTxLength = types_1.MAX_PAYLOAD_SIZE - 20;
63
+ if (firstFrameTxLength >= unsignedTx.length) {
64
+ return [{ p1: 1, p2: 0, data: Buffer.concat([encodedPath, unsignedTx]) }];
65
+ }
66
+ const firstFrameTxData = unsignedTx.slice(0, firstFrameTxLength);
67
+ const frames = [{ p1: 1, p2: 0, data: Buffer.concat([encodedPath, firstFrameTxData]) }];
68
+ let fromIndex = firstFrameTxLength;
69
+ while (fromIndex < unsignedTx.length) {
70
+ const remain = unsignedTx.length - fromIndex;
71
+ const frameTxLength = Math.min(types_1.MAX_PAYLOAD_SIZE, remain);
72
+ frames.push({ p1: 1, p2: 1, data: unsignedTx.slice(fromIndex, fromIndex + frameTxLength) });
73
+ fromIndex += frameTxLength;
74
+ }
75
+ return frames;
76
+ }
77
+ exports.encodeUnsignedTx = encodeUnsignedTx;
78
+ function assert(condition, msg) {
79
+ if (!condition)
80
+ throw Error(msg);
81
+ }
82
+ exports.assert = assert;
@@ -1,6 +1,7 @@
1
1
  export declare const MAX_TOKEN_SIZE = 5;
2
2
  export declare const MAX_TOKEN_SYMBOL_LENGTH = 12;
3
3
  export declare const TOKEN_METADATA_SIZE = 46;
4
+ export declare const MAX_PAYLOAD_SIZE = 255;
4
5
  export interface TokenMetadata {
5
6
  version: number;
6
7
  tokenId: string;
package/dist/src/types.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TOKEN_METADATA_SIZE = exports.MAX_TOKEN_SYMBOL_LENGTH = exports.MAX_TOKEN_SIZE = void 0;
3
+ exports.MAX_PAYLOAD_SIZE = exports.TOKEN_METADATA_SIZE = exports.MAX_TOKEN_SYMBOL_LENGTH = exports.MAX_TOKEN_SIZE = void 0;
4
4
  exports.MAX_TOKEN_SIZE = 5;
5
5
  exports.MAX_TOKEN_SYMBOL_LENGTH = 12;
6
6
  exports.TOKEN_METADATA_SIZE = 46;
7
+ // The maximum payload size is 255: https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/hw-transport/src/Transport.ts#L261
8
+ exports.MAX_PAYLOAD_SIZE = 255;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const merkle_1 = require("../src/merkle");
4
+ const serde_1 = require("../src/serde");
5
+ const blakejs_1 = require("blakejs");
6
+ describe('Merkle', () => {
7
+ it('should verify proofs', () => {
8
+ for (const token of merkle_1.merkleTokens) {
9
+ const proof = merkle_1.tokenMerkleProofs[token.tokenId];
10
+ let currentHash = (0, blakejs_1.blake2b)((0, serde_1.serializeSingleTokenMetadata)(token), undefined, 32);
11
+ for (let i = 0; i < proof.length; i += 64) {
12
+ const sibling = proof.slice(i, i + 64);
13
+ currentHash = (0, merkle_1.hashPair)(currentHash, Buffer.from(sibling, 'hex'));
14
+ }
15
+ expect(JSON.stringify(currentHash)).toBe(JSON.stringify(merkle_1.tokenMerkleRoot));
16
+ }
17
+ });
18
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const merkle_1 = require("../src/merkle");
4
+ const tx_encoder_1 = require("../src/tx-encoder");
5
+ const src_1 = require("../src");
6
+ const serde_1 = require("../src/serde");
7
+ const crypto_1 = require("crypto");
8
+ describe('TxEncoder', () => {
9
+ function shuffle(array) {
10
+ for (let i = array.length - 1; i > 0; i--) {
11
+ const j = Math.floor(Math.random() * (i + 1));
12
+ [array[i], array[j]] = [array[j], array[i]];
13
+ }
14
+ return array;
15
+ }
16
+ function getFrameSize(proofLength) {
17
+ if (proofLength <= 192)
18
+ return 1;
19
+ if (proofLength <= 416)
20
+ return 2;
21
+ if (proofLength <= 640)
22
+ return 3;
23
+ throw Error(`Invalid proof length: ${proofLength}`);
24
+ }
25
+ it('should encode token metadata and proof', () => {
26
+ const frames0 = (0, tx_encoder_1.encodeTokenMetadata)([]);
27
+ expect(frames0).toEqual([{ p1: 0, p2: 0, data: Buffer.from([0]) }]);
28
+ const tokenSize = Math.floor(Math.random() * src_1.MAX_TOKEN_SIZE) + 1;
29
+ (0, tx_encoder_1.assert)(tokenSize >= 1 && tokenSize <= 5, 'Invalid token size');
30
+ const tokens = shuffle(Object.entries(merkle_1.tokenMerkleProofs));
31
+ const selectedTokens = tokens.slice(0, tokenSize);
32
+ const tokenMetadatas = selectedTokens.map(([tokenId]) => merkle_1.merkleTokens.find((t) => t.tokenId === tokenId));
33
+ const frames = (0, tx_encoder_1.encodeTokenMetadata)(tokenMetadatas);
34
+ const tokenAndProofs = Buffer.concat(frames.map((frame, index) => index === 0 ? frame.data.slice(1) : frame.data));
35
+ const expected = Buffer.concat(tokenMetadatas.map((metadata, index) => {
36
+ const proof = Buffer.from(selectedTokens[index][1], 'hex');
37
+ const encodedProofLength = (0, tx_encoder_1.encodeProofLength)(proof.length);
38
+ const encodedTokenMetadata = (0, serde_1.serializeSingleTokenMetadata)(metadata);
39
+ return Buffer.concat([encodedTokenMetadata, encodedProofLength, proof]);
40
+ }));
41
+ expect(tokenAndProofs).toEqual(expected);
42
+ let frameIndex = 0;
43
+ tokenMetadatas.forEach((_, index) => {
44
+ const proof = Buffer.from(selectedTokens[index][1], 'hex');
45
+ const isFirstToken = index === 0;
46
+ const prefixLength = isFirstToken ? 1 + src_1.TOKEN_METADATA_SIZE + 2 : src_1.TOKEN_METADATA_SIZE + 2;
47
+ const tokenFrames = frames.slice(frameIndex, frameIndex + getFrameSize(proof.length));
48
+ const firstFrameP2 = isFirstToken ? 0 : 1;
49
+ expect(tokenFrames[0].p1).toEqual(0);
50
+ expect(tokenFrames[0].p2).toEqual(firstFrameP2);
51
+ tokenFrames.slice(1).forEach((frame) => {
52
+ expect(frame.p1).toEqual(0);
53
+ expect(frame.p2).toEqual(2);
54
+ });
55
+ const expectedProof = Buffer.concat([tokenFrames[0].data.slice(prefixLength), ...tokenFrames.slice(1).map((f) => f.data)]);
56
+ expect(proof).toEqual(expectedProof);
57
+ frameIndex += tokenFrames.length;
58
+ });
59
+ });
60
+ it('should encode tx', () => {
61
+ const path = `m/44'/1234'/0'/0/0`;
62
+ const encodedPath = (0, serde_1.serializePath)(path);
63
+ const unsignedTx0 = (0, crypto_1.randomBytes)(200);
64
+ const frames0 = (0, tx_encoder_1.encodeUnsignedTx)(path, unsignedTx0);
65
+ expect(frames0).toEqual([{ p1: 1, p2: 0, data: Buffer.concat([encodedPath, unsignedTx0]) }]);
66
+ const unsignedTx1 = (0, crypto_1.randomBytes)(250);
67
+ const frames1 = (0, tx_encoder_1.encodeUnsignedTx)(path, unsignedTx1);
68
+ expect(frames1).toEqual([
69
+ { p1: 1, p2: 0, data: Buffer.concat([encodedPath, unsignedTx1.slice(0, src_1.MAX_PAYLOAD_SIZE - 20)]) },
70
+ { p1: 1, p2: 1, data: unsignedTx1.slice(src_1.MAX_PAYLOAD_SIZE - 20) },
71
+ ]);
72
+ });
73
+ });
@@ -2,14 +2,17 @@ import Transport from '@ledgerhq/hw-transport';
2
2
  export declare enum OutputType {
3
3
  Base = 0,
4
4
  Multisig = 1,
5
- Token = 2,
6
- BaseAndToken = 3,
7
- MultisigAndToken = 4
5
+ Nanos10 = 2,
6
+ Nanos11 = 3,
7
+ Token = 4,
8
+ BaseAndToken = 5,
9
+ MultisigAndToken = 6
8
10
  }
9
11
  export declare function staxFlexApproveOnce(): Promise<void>;
10
12
  export declare function approveTx(outputs: OutputType[], hasExternalInputs?: boolean): Promise<void>;
11
13
  export declare function approveHash(): Promise<void>;
12
14
  export declare function approveAddress(): Promise<void>;
15
+ export declare function isNanos(): boolean;
13
16
  export declare function skipBlindSigningWarning(): void;
14
17
  export declare function enableBlindSigning(): Promise<void>;
15
18
  export declare function getRandomInt(min: number, max: number): number;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createTransport = exports.needToAutoApprove = exports.getRandomInt = exports.enableBlindSigning = exports.skipBlindSigningWarning = exports.approveAddress = exports.approveHash = exports.approveTx = exports.staxFlexApproveOnce = exports.OutputType = void 0;
6
+ exports.createTransport = exports.needToAutoApprove = exports.getRandomInt = exports.enableBlindSigning = exports.skipBlindSigningWarning = exports.isNanos = exports.approveAddress = exports.approveHash = exports.approveTx = exports.staxFlexApproveOnce = exports.OutputType = void 0;
7
7
  const hw_transport_node_speculos_1 = __importDefault(require("@ledgerhq/hw-transport-node-speculos"));
8
8
  const node_fetch_1 = __importDefault(require("node-fetch"));
9
9
  const web3_1 = require("@alephium/web3");
@@ -29,13 +29,17 @@ var OutputType;
29
29
  (function (OutputType) {
30
30
  OutputType[OutputType["Base"] = 0] = "Base";
31
31
  OutputType[OutputType["Multisig"] = 1] = "Multisig";
32
- OutputType[OutputType["Token"] = 2] = "Token";
33
- OutputType[OutputType["BaseAndToken"] = 3] = "BaseAndToken";
34
- OutputType[OutputType["MultisigAndToken"] = 4] = "MultisigAndToken";
32
+ OutputType[OutputType["Nanos10"] = 2] = "Nanos10";
33
+ OutputType[OutputType["Nanos11"] = 3] = "Nanos11";
34
+ OutputType[OutputType["Token"] = 4] = "Token";
35
+ OutputType[OutputType["BaseAndToken"] = 5] = "BaseAndToken";
36
+ OutputType[OutputType["MultisigAndToken"] = 6] = "MultisigAndToken";
35
37
  })(OutputType = exports.OutputType || (exports.OutputType = {}));
36
38
  const NanosClickTable = new Map([
37
39
  [OutputType.Base, 5],
38
40
  [OutputType.Multisig, 10],
41
+ [OutputType.Nanos10, 10],
42
+ [OutputType.Nanos11, 11],
39
43
  [OutputType.Token, 11],
40
44
  [OutputType.BaseAndToken, 12],
41
45
  [OutputType.MultisigAndToken, 16],
@@ -123,6 +127,7 @@ async function touch(outputs, hasExternalInputs) {
123
127
  async function approveTx(outputs, hasExternalInputs = false) {
124
128
  if (!needToAutoApprove())
125
129
  return;
130
+ await (0, web3_1.sleep)(2000);
126
131
  const isSelfTransfer = outputs.length === 0 && !hasExternalInputs;
127
132
  if (isSelfTransfer) {
128
133
  if (isStaxOrFlex()) {
@@ -172,6 +177,10 @@ exports.approveAddress = approveAddress;
172
177
  function isStaxOrFlex() {
173
178
  return !getModel().startsWith('nano');
174
179
  }
180
+ function isNanos() {
181
+ return getModel() === 'nanos';
182
+ }
183
+ exports.isNanos = isNanos;
175
184
  function skipBlindSigningWarning() {
176
185
  if (!needToAutoApprove())
177
186
  return;