@alephium/ledger-app 0.6.0 → 0.6.1-rc.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,5 +1,4 @@
1
- /// <reference types="node" />
2
- import { Account, KeyType } from '@alephium/web3';
1
+ import { GroupedAccount, KeyType } from '@alephium/web3';
3
2
  import Transport from '@ledgerhq/hw-transport';
4
3
  export declare const CLA = 128;
5
4
  export declare enum INS {
@@ -15,7 +14,7 @@ export declare class AlephiumApp {
15
14
  constructor(transport: Transport);
16
15
  close(): Promise<void>;
17
16
  getVersion(): Promise<string>;
18
- getAccount(startPath: string, targetGroup?: number, keyType?: KeyType, display?: boolean): Promise<readonly [Account, number]>;
17
+ getAccount(startPath: string, targetGroup?: number, keyType?: KeyType, display?: boolean): Promise<readonly [GroupedAccount, number]>;
19
18
  signHash(path: string, hash: Buffer): Promise<string>;
20
19
  signUnsignedTx(path: string, unsignedTx: Buffer): Promise<string>;
21
20
  }
@@ -15,23 +15,31 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  Object.defineProperty(exports, "__esModule", { value: true });
26
36
  exports.AlephiumApp = exports.HASH_LEN = exports.GROUP_NUM = exports.INS = exports.CLA = void 0;
27
37
  const web3_1 = require("@alephium/web3");
28
38
  const hw_transport_1 = require("@ledgerhq/hw-transport");
29
39
  const serde = __importStar(require("./serde"));
30
- const elliptic_1 = require("elliptic");
31
40
  const types_1 = require("./types");
32
41
  const tx_encoder_1 = require("./tx-encoder");
33
42
  const merkle_1 = require("./merkle");
34
- const ec = new elliptic_1.ec('secp256k1');
35
43
  exports.CLA = 0x80;
36
44
  var INS;
37
45
  (function (INS) {
@@ -39,7 +47,7 @@ var INS;
39
47
  INS[INS["GET_PUBLIC_KEY"] = 1] = "GET_PUBLIC_KEY";
40
48
  INS[INS["SIGN_HASH"] = 2] = "SIGN_HASH";
41
49
  INS[INS["SIGN_TX"] = 3] = "SIGN_TX";
42
- })(INS = exports.INS || (exports.INS = {}));
50
+ })(INS || (exports.INS = INS = {}));
43
51
  exports.GROUP_NUM = 4;
44
52
  exports.HASH_LEN = 32;
45
53
  class AlephiumApp {
@@ -58,18 +66,20 @@ class AlephiumApp {
58
66
  if ((targetGroup ?? 0) >= exports.GROUP_NUM) {
59
67
  throw Error(`Invalid targetGroup: ${targetGroup}`);
60
68
  }
61
- if (keyType === 'bip340-schnorr') {
62
- throw Error('BIP340-Schnorr is not supported yet');
69
+ if (keyType !== undefined && keyType !== 'default') {
70
+ throw Error(`Unsupported key type: ${keyType}`);
63
71
  }
64
72
  const p1 = targetGroup === undefined ? 0x00 : exports.GROUP_NUM;
65
73
  const p2 = targetGroup === undefined ? 0x00 : targetGroup;
66
74
  const payload = Buffer.concat([serde.serializePath(startPath), Buffer.from([display ? 1 : 0])]);
67
75
  const response = await this.transport.send(exports.CLA, INS.GET_PUBLIC_KEY, p1, p2, payload);
68
- const publicKey = ec.keyFromPublic(response.slice(0, 65)).getPublic(true, 'hex');
76
+ const prefix = (response[64] & 1) === 1 ? '03' : '02';
77
+ const publicKey = prefix + response.slice(1, 33).toString('hex');
69
78
  const address = (0, web3_1.addressFromPublicKey)(publicKey);
70
79
  const group = (0, web3_1.groupOfAddress)(address);
71
80
  const hdIndex = response.slice(65, 69).readUInt32BE(0);
72
- return [{ publicKey: publicKey, address: address, group: group, keyType: keyType ?? 'default' }, hdIndex];
81
+ const resolvedKeyType = 'default';
82
+ return [{ publicKey, address, group, keyType: resolvedKeyType }, hdIndex];
73
83
  }
74
84
  async signHash(path, hash) {
75
85
  if (hash.length !== exports.HASH_LEN) {
@@ -5,5 +5,5 @@ export declare function generateProofs(): {
5
5
  proofs: Record<string, string>;
6
6
  root: string;
7
7
  };
8
- export declare const tokenMerkleRoot: Uint8Array;
8
+ export declare const tokenMerkleRoot: Uint8Array<ArrayBufferLike>;
9
9
  export declare const tokenMerkleProofs: Record<string, string>;
@@ -3,7 +3,9 @@ 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.tokenMerkleProofs = exports.tokenMerkleRoot = exports.generateProofs = exports.hashPair = exports.merkleTokens = void 0;
6
+ exports.tokenMerkleProofs = exports.tokenMerkleRoot = exports.merkleTokens = void 0;
7
+ exports.hashPair = hashPair;
8
+ exports.generateProofs = generateProofs;
7
9
  const web3_1 = require("@alephium/web3");
8
10
  const token_json_1 = __importDefault(require("../merkle-tree/token.json"));
9
11
  const proofs_json_1 = __importDefault(require("../merkle-tree/proofs.json"));
@@ -20,7 +22,6 @@ exports.merkleTokens = token_json_1.default.tokens.map((token) => {
20
22
  function hashPair(a, b) {
21
23
  return (0, blakejs_1.blake2b)(Buffer.concat([a, b].sort(Buffer.compare)), undefined, 32);
22
24
  }
23
- exports.hashPair = hashPair;
24
25
  function generateMerkleTree(tokens) {
25
26
  let level = tokens.map((token) => (0, blakejs_1.blake2b)((0, serde_1.serializeSingleTokenMetadata)(token), undefined, 32));
26
27
  const tree = [];
@@ -54,6 +55,5 @@ function generateProofs() {
54
55
  console.log('root', tree[tree.length - 1].map((hash) => (0, web3_1.binToHex)(hash)).join(''));
55
56
  return { proofs, root: (0, web3_1.binToHex)(tree[tree.length - 1][0]) };
56
57
  }
57
- exports.generateProofs = generateProofs;
58
58
  exports.tokenMerkleRoot = (0, web3_1.hexToBinUnsafe)('b3380866c595544781e9da0ccd79399de8878abfb0bf40545b57a287387d419d');
59
59
  exports.tokenMerkleProofs = proofs_json_1.default;
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { TokenMetadata } from './types';
3
2
  export declare const TRUE = 16;
4
3
  export declare const FALSE = 0;
package/dist/src/serde.js CHANGED
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serializeTokenMetadata = exports.serializeSingleTokenMetadata = exports.checkTokenMetadata = exports.serializePath = exports.splitPath = exports.FALSE = exports.TRUE = void 0;
3
+ exports.FALSE = exports.TRUE = void 0;
4
+ exports.splitPath = splitPath;
5
+ exports.serializePath = serializePath;
6
+ exports.checkTokenMetadata = checkTokenMetadata;
7
+ exports.serializeSingleTokenMetadata = serializeSingleTokenMetadata;
8
+ exports.serializeTokenMetadata = serializeTokenMetadata;
4
9
  const web3_1 = require("@alephium/web3");
5
10
  const types_1 = require("./types");
6
11
  exports.TRUE = 0x10;
@@ -21,7 +26,6 @@ function splitPath(path) {
21
26
  });
22
27
  return result;
23
28
  }
24
- exports.splitPath = splitPath;
25
29
  function serializePath(path) {
26
30
  const nodes = splitPath(path);
27
31
  if (nodes.length != 5) {
@@ -31,7 +35,6 @@ function serializePath(path) {
31
35
  nodes.forEach((element, index) => buffer.writeUInt32BE(element, 4 * index));
32
36
  return buffer;
33
37
  }
34
- exports.serializePath = serializePath;
35
38
  function symbolToBytes(symbol) {
36
39
  const buffer = Buffer.alloc(types_1.MAX_TOKEN_SYMBOL_LENGTH, 0);
37
40
  for (let i = 0; i < symbol.length; i++) {
@@ -56,7 +59,6 @@ function checkTokenMetadata(tokens) {
56
59
  throw new Error(`The token size exceeds maximum size`);
57
60
  }
58
61
  }
59
- exports.checkTokenMetadata = checkTokenMetadata;
60
62
  function serializeSingleTokenMetadata(metadata) {
61
63
  const symbolBytes = symbolToBytes(metadata.symbol);
62
64
  const buffer = Buffer.concat([
@@ -70,10 +72,8 @@ function serializeSingleTokenMetadata(metadata) {
70
72
  }
71
73
  return buffer;
72
74
  }
73
- exports.serializeSingleTokenMetadata = serializeSingleTokenMetadata;
74
75
  function serializeTokenMetadata(tokens) {
75
76
  checkTokenMetadata(tokens);
76
77
  const array = tokens.map((metadata) => serializeSingleTokenMetadata(metadata));
77
78
  return Buffer.concat([Buffer.from([array.length]), ...array]);
78
79
  }
79
- exports.serializeTokenMetadata = serializeTokenMetadata;
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { TokenMetadata } from "./types";
3
2
  export interface Frame {
4
3
  p1: number;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assert = exports.encodeUnsignedTx = exports.encodeProofLength = exports.encodeTokenMetadata = void 0;
3
+ exports.encodeTokenMetadata = encodeTokenMetadata;
4
+ exports.encodeProofLength = encodeProofLength;
5
+ exports.encodeUnsignedTx = encodeUnsignedTx;
6
+ exports.assert = assert;
4
7
  const merkle_1 = require("./merkle");
5
8
  const serde_1 = require("./serde");
6
9
  const types_1 = require("./types");
@@ -25,7 +28,6 @@ function encodeTokenMetadata(tokenMetadata) {
25
28
  return frames;
26
29
  }
27
30
  }
28
- exports.encodeTokenMetadata = encodeTokenMetadata;
29
31
  function encodeTokenAndProof(tokenMetadata, firstFramePrefix) {
30
32
  const proof = merkle_1.tokenMerkleProofs[tokenMetadata.tokenId];
31
33
  if (proof === undefined)
@@ -56,7 +58,6 @@ function encodeProofLength(length) {
56
58
  buffer.writeUint16BE(length);
57
59
  return buffer;
58
60
  }
59
- exports.encodeProofLength = encodeProofLength;
60
61
  function encodeUnsignedTx(path, unsignedTx) {
61
62
  const encodedPath = (0, serde_1.serializePath)(path);
62
63
  const firstFrameTxLength = types_1.MAX_PAYLOAD_SIZE - 20;
@@ -74,9 +75,7 @@ function encodeUnsignedTx(path, unsignedTx) {
74
75
  }
75
76
  return frames;
76
77
  }
77
- exports.encodeUnsignedTx = encodeUnsignedTx;
78
78
  function assert(condition, msg) {
79
79
  if (!condition)
80
80
  throw Error(msg);
81
81
  }
82
- exports.assert = assert;
@@ -3,7 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const merkle_1 = require("../src/merkle");
4
4
  const serde_1 = require("../src/serde");
5
5
  const blakejs_1 = require("blakejs");
6
+ const web3_1 = require("@alephium/web3");
6
7
  describe('Merkle', () => {
8
+ it('should generate the correct proofs', () => {
9
+ const { proofs, root } = (0, merkle_1.generateProofs)();
10
+ expect(JSON.stringify(proofs)).toBe(JSON.stringify(merkle_1.tokenMerkleProofs));
11
+ expect(root).toBe((0, web3_1.binToHex)(merkle_1.tokenMerkleRoot));
12
+ });
7
13
  it('should verify proofs', () => {
8
14
  for (const token of merkle_1.merkleTokens) {
9
15
  const proof = merkle_1.tokenMerkleProofs[token.tokenId];
@@ -12,8 +12,10 @@ export declare function staxFlexApproveOnce(): Promise<void>;
12
12
  export declare function approveTx(outputs: OutputType[], hasExternalInputs?: boolean): Promise<void>;
13
13
  export declare function approveHash(): Promise<void>;
14
14
  export declare function approveAddress(): Promise<void>;
15
+ export declare function isStaxOrFlex(): boolean;
15
16
  export declare function isNanos(): boolean;
16
- export declare function skipBlindSigningWarning(): void;
17
+ export declare function skipBlindSigningWarning(): Promise<void>;
18
+ export declare function staxFlexAcceptRisk(): Promise<void>;
17
19
  export declare function enableBlindSigning(): Promise<void>;
18
20
  export declare function getRandomInt(min: number, max: number): number;
19
21
  export declare function needToAutoApprove(): boolean;
@@ -3,14 +3,25 @@ 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.isNanos = exports.approveAddress = exports.approveHash = exports.approveTx = exports.staxFlexApproveOnce = exports.OutputType = void 0;
6
+ exports.OutputType = void 0;
7
+ exports.staxFlexApproveOnce = staxFlexApproveOnce;
8
+ exports.approveTx = approveTx;
9
+ exports.approveHash = approveHash;
10
+ exports.approveAddress = approveAddress;
11
+ exports.isStaxOrFlex = isStaxOrFlex;
12
+ exports.isNanos = isNanos;
13
+ exports.skipBlindSigningWarning = skipBlindSigningWarning;
14
+ exports.staxFlexAcceptRisk = staxFlexAcceptRisk;
15
+ exports.enableBlindSigning = enableBlindSigning;
16
+ exports.getRandomInt = getRandomInt;
17
+ exports.needToAutoApprove = needToAutoApprove;
18
+ exports.createTransport = createTransport;
7
19
  const hw_transport_node_speculos_1 = __importDefault(require("@ledgerhq/hw-transport-node-speculos"));
8
- const node_fetch_1 = __importDefault(require("node-fetch"));
9
20
  const web3_1 = require("@alephium/web3");
10
21
  const hw_transport_node_hid_1 = __importDefault(require("@ledgerhq/hw-transport-node-hid"));
11
22
  async function pressButton(button) {
12
23
  await (0, web3_1.sleep)(1000);
13
- return (0, node_fetch_1.default)(`http://localhost:25000/button/${button}`, {
24
+ return fetch(`http://localhost:25000/button/${button}`, {
14
25
  method: 'POST',
15
26
  body: JSON.stringify({ action: 'press-and-release' })
16
27
  });
@@ -34,7 +45,7 @@ var OutputType;
34
45
  OutputType[OutputType["Token"] = 4] = "Token";
35
46
  OutputType[OutputType["BaseAndToken"] = 5] = "BaseAndToken";
36
47
  OutputType[OutputType["MultisigAndToken"] = 6] = "MultisigAndToken";
37
- })(OutputType = exports.OutputType || (exports.OutputType = {}));
48
+ })(OutputType || (exports.OutputType = OutputType = {}));
38
49
  const NanosClickTable = new Map([
39
50
  [OutputType.Base, 5],
40
51
  [OutputType.Multisig, 10],
@@ -52,11 +63,18 @@ const NanospClickTable = new Map([
52
63
  [OutputType.MultisigAndToken, 8],
53
64
  ]);
54
65
  const StaxClickTable = new Map([
55
- [OutputType.Base, 2],
56
- [OutputType.Multisig, 3],
57
- [OutputType.Token, 3],
58
- [OutputType.BaseAndToken, 3],
59
- [OutputType.MultisigAndToken, 4],
66
+ [OutputType.Base, 1],
67
+ [OutputType.Multisig, 2],
68
+ [OutputType.Token, 2],
69
+ [OutputType.BaseAndToken, 2],
70
+ [OutputType.MultisigAndToken, 2],
71
+ ]);
72
+ const FlexClickTable = new Map([
73
+ [OutputType.Base, 1],
74
+ [OutputType.Multisig, 2],
75
+ [OutputType.Token, 2],
76
+ [OutputType.BaseAndToken, 2],
77
+ [OutputType.MultisigAndToken, 3],
60
78
  ]);
61
79
  function getOutputClickSize(outputType) {
62
80
  const model = getModel();
@@ -64,8 +82,8 @@ function getOutputClickSize(outputType) {
64
82
  case 'nanos': return NanosClickTable.get(outputType);
65
83
  case 'nanosp':
66
84
  case 'nanox': return NanospClickTable.get(outputType);
67
- case 'stax':
68
- case 'flex': return StaxClickTable.get(outputType);
85
+ case 'stax': return StaxClickTable.get(outputType);
86
+ case 'flex': return FlexClickTable.get(outputType);
69
87
  default: throw new Error(`Unknown model ${model}`);
70
88
  }
71
89
  }
@@ -81,29 +99,40 @@ async function click(outputs, hasExternalInputs) {
81
99
  }
82
100
  const STAX_CONTINUE_POSITION = { x: 342, y: 606 };
83
101
  const STAX_APPROVE_POSITION = { x: 200, y: 515 };
84
- const STAX_REJECT_POSITION = { x: 36, y: 606 };
85
102
  const STAX_SETTINGS_POSITION = { x: 342, y: 55 };
86
103
  const STAX_BLIND_SETTING_POSITION = { x: 342, y: 90 };
104
+ const STAX_GO_TO_SETTINGS = { x: 36, y: 606 };
105
+ const STAX_ACCEPT_RISK_POSITION = { x: 36, y: 606 };
87
106
  const FLEX_CONTINUE_POSITION = { x: 430, y: 550 };
88
107
  const FLEX_APPROVE_POSITION = { x: 240, y: 435 };
89
- const FLEX_REJECT_POSITION = { x: 55, y: 530 };
90
108
  const FLEX_SETTINGS_POSITION = { x: 405, y: 75 };
91
109
  const FLEX_BLIND_SETTING_POSITION = { x: 405, y: 96 };
110
+ const FLEX_GO_TO_SETTINGS = { x: 55, y: 530 };
111
+ const FLEX_ACCEPT_RISK_POSITION = { x: 55, y: 530 };
92
112
  async function touchPosition(pos) {
93
113
  await (0, web3_1.sleep)(1000);
94
- return (0, node_fetch_1.default)(`http://localhost:25000/finger`, {
114
+ return fetch(`http://localhost:25000/finger`, {
95
115
  method: 'POST',
96
116
  body: JSON.stringify({ action: 'press-and-release', x: pos.x, y: pos.y })
97
117
  });
98
118
  }
99
- async function _touch(times) {
119
+ async function longPress(pos) {
120
+ await (0, web3_1.sleep)(1000);
121
+ return fetch(`http://localhost:25000/finger`, {
122
+ method: 'POST',
123
+ body: JSON.stringify({ action: 'press-and-release', x: pos.x, y: pos.y, delay: 3 })
124
+ });
125
+ }
126
+ async function _touch(times, approve = false) {
100
127
  const model = getModel();
101
128
  const continuePos = model === 'stax' ? STAX_CONTINUE_POSITION : FLEX_CONTINUE_POSITION;
102
129
  for (let i = 0; i < times; i += 1) {
103
130
  await touchPosition(continuePos);
104
131
  }
105
- const approvePos = model === 'stax' ? STAX_APPROVE_POSITION : FLEX_APPROVE_POSITION;
106
- await touchPosition(approvePos);
132
+ if (approve) {
133
+ const approvePos = model === 'stax' ? STAX_APPROVE_POSITION : FLEX_APPROVE_POSITION;
134
+ await longPress(approvePos);
135
+ }
107
136
  }
108
137
  async function staxFlexApproveOnce() {
109
138
  if (getModel() === 'stax') {
@@ -113,16 +142,17 @@ async function staxFlexApproveOnce() {
113
142
  await touchPosition(FLEX_APPROVE_POSITION);
114
143
  }
115
144
  }
116
- exports.staxFlexApproveOnce = staxFlexApproveOnce;
117
145
  async function touch(outputs, hasExternalInputs) {
118
- await (0, web3_1.sleep)(1000);
146
+ await (0, web3_1.sleep)(3000);
119
147
  if (hasExternalInputs) {
120
148
  await staxFlexApproveOnce();
121
149
  }
150
+ _touch(1); // the first review page
151
+ await (0, web3_1.sleep)(1000);
122
152
  for (let index = 0; index < outputs.length; index += 1) {
123
153
  await _touch(getOutputClickSize(outputs[index]));
124
154
  }
125
- await _touch(2); // fees
155
+ await _touch(1, true); // fees
126
156
  }
127
157
  async function approveTx(outputs, hasExternalInputs = false) {
128
158
  if (!needToAutoApprove())
@@ -131,7 +161,7 @@ async function approveTx(outputs, hasExternalInputs = false) {
131
161
  const isSelfTransfer = outputs.length === 0 && !hasExternalInputs;
132
162
  if (isSelfTransfer) {
133
163
  if (isStaxOrFlex()) {
134
- await _touch(2);
164
+ await _touch(2, true);
135
165
  }
136
166
  else {
137
167
  await clickAndApprove(2);
@@ -145,12 +175,11 @@ async function approveTx(outputs, hasExternalInputs = false) {
145
175
  await click(outputs, hasExternalInputs);
146
176
  }
147
177
  }
148
- exports.approveTx = approveTx;
149
178
  async function approveHash() {
150
179
  if (!needToAutoApprove())
151
180
  return;
152
181
  if (isStaxOrFlex()) {
153
- return await _touch(3);
182
+ return await _touch(2, true);
154
183
  }
155
184
  if (getModel() === 'nanos') {
156
185
  await clickAndApprove(5);
@@ -159,12 +188,13 @@ async function approveHash() {
159
188
  await clickAndApprove(3);
160
189
  }
161
190
  }
162
- exports.approveHash = approveHash;
163
191
  async function approveAddress() {
164
192
  if (!needToAutoApprove())
165
193
  return;
166
194
  if (isStaxOrFlex()) {
167
- return await _touch(2);
195
+ await _touch(1);
196
+ await staxFlexApproveOnce();
197
+ return;
168
198
  }
169
199
  if (getModel() === 'nanos') {
170
200
  await clickAndApprove(4);
@@ -173,26 +203,35 @@ async function approveAddress() {
173
203
  await clickAndApprove(2);
174
204
  }
175
205
  }
176
- exports.approveAddress = approveAddress;
177
206
  function isStaxOrFlex() {
178
207
  return !getModel().startsWith('nano');
179
208
  }
180
209
  function isNanos() {
181
210
  return getModel() === 'nanos';
182
211
  }
183
- exports.isNanos = isNanos;
184
- function skipBlindSigningWarning() {
212
+ async function skipBlindSigningWarning() {
185
213
  if (!needToAutoApprove())
186
214
  return;
187
215
  if (isStaxOrFlex()) {
188
- const rejectPos = getModel() === 'stax' ? STAX_REJECT_POSITION : FLEX_REJECT_POSITION;
189
- touchPosition(rejectPos);
216
+ await (0, web3_1.sleep)(3000);
217
+ const goToSettings = getModel() === 'stax' ? STAX_GO_TO_SETTINGS : FLEX_GO_TO_SETTINGS;
218
+ await touchPosition(goToSettings);
190
219
  }
191
220
  else {
192
- clickAndApprove(3);
221
+ await clickAndApprove(3);
222
+ }
223
+ }
224
+ async function staxFlexAcceptRisk() {
225
+ if (!needToAutoApprove())
226
+ return;
227
+ await (0, web3_1.sleep)(3000);
228
+ if (getModel() === 'stax') {
229
+ await touchPosition(STAX_ACCEPT_RISK_POSITION);
230
+ }
231
+ else {
232
+ await touchPosition(FLEX_ACCEPT_RISK_POSITION);
193
233
  }
194
234
  }
195
- exports.skipBlindSigningWarning = skipBlindSigningWarning;
196
235
  async function enableBlindSigning() {
197
236
  if (!needToAutoApprove())
198
237
  return;
@@ -208,13 +247,11 @@ async function enableBlindSigning() {
208
247
  await clickAndApprove(2);
209
248
  }
210
249
  }
211
- exports.enableBlindSigning = enableBlindSigning;
212
250
  function getRandomInt(min, max) {
213
251
  min = Math.ceil(min);
214
252
  max = Math.floor(max);
215
253
  return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
216
254
  }
217
- exports.getRandomInt = getRandomInt;
218
255
  function needToAutoApprove() {
219
256
  switch (process.env.BACKEND) {
220
257
  case "speculos": return true;
@@ -222,7 +259,6 @@ function needToAutoApprove() {
222
259
  default: throw new Error(`Invalid backend: ${process.env.BACKEND}`);
223
260
  }
224
261
  }
225
- exports.needToAutoApprove = needToAutoApprove;
226
262
  const ApduPort = 9999;
227
263
  async function createTransport() {
228
264
  switch (process.env.BACKEND) {
@@ -231,4 +267,3 @@ async function createTransport() {
231
267
  default: throw new Error(`Invalid backend: ${process.env.BACKEND}`);
232
268
  }
233
269
  }
234
- exports.createTransport = createTransport;
@@ -209,32 +209,6 @@ describe('ledger wallet', () => {
209
209
  expect(token.amount).toEqual('1111111111111111111111111');
210
210
  await app.close();
211
211
  }, 120000);
212
- async function genTokensAndDestinations(fromAddress, toAddress, mintAmount, transferAmount) {
213
- const tokens = [];
214
- const tokenSymbol = 'TestTokenABC';
215
- const destinations = [];
216
- for (let i = 0; i < 5; i += 1) {
217
- const tokenInfo = await (0, web3_test_1.mintToken)(fromAddress, mintAmount);
218
- const tokenMetadata = {
219
- version: 0,
220
- tokenId: tokenInfo.contractId,
221
- symbol: tokenSymbol.slice(0, tokenSymbol.length - i),
222
- decimals: 18 - i
223
- };
224
- tokens.push(tokenMetadata);
225
- destinations.push({
226
- address: toAddress,
227
- attoAlphAmount: web3_1.DUST_AMOUNT.toString(),
228
- tokens: [
229
- {
230
- id: tokenMetadata.tokenId,
231
- amount: transferAmount.toString()
232
- }
233
- ]
234
- });
235
- }
236
- return { tokens, destinations };
237
- }
238
212
  it('should transfer tokens with proof', async () => {
239
213
  const transport = await (0, utils_1.createTransport)();
240
214
  const app = new ledger_app_1.AlephiumApp(transport);
@@ -242,10 +216,10 @@ describe('ledger wallet', () => {
242
216
  await transferToAddress(testAccount.address);
243
217
  const newAccount = await (0, web3_test_1.getSigner)();
244
218
  const selectedTokens = [
245
- merkle_1.merkleTokens[5],
246
- merkle_1.merkleTokens[6],
247
- merkle_1.merkleTokens[8],
248
- merkle_1.merkleTokens[11],
219
+ merkle_1.merkleTokens[5], // decimals is 0
220
+ merkle_1.merkleTokens[6], // decimals is 18
221
+ merkle_1.merkleTokens[8], // decimals is 9
222
+ merkle_1.merkleTokens[11], // decimals is 8
249
223
  merkle_1.merkleTokens[13], // decimals is 6
250
224
  ];
251
225
  const outputs = selectedTokens.map((token, index) => {
@@ -479,7 +453,12 @@ describe('ledger wallet', () => {
479
453
  await expect(app.signUnsignedTx(path, Buffer.from(buildTxResult.unsignedTx, 'hex'))).rejects.toThrow();
480
454
  await (0, utils_1.enableBlindSigning)();
481
455
  if ((0, utils_1.needToAutoApprove)()) {
482
- (0, utils_1.staxFlexApproveOnce)().then(() => (0, utils_1.approveTx)([]));
456
+ if ((0, utils_1.isStaxOrFlex)()) {
457
+ (0, utils_1.staxFlexAcceptRisk)().then(() => (0, utils_1.approveTx)([]));
458
+ }
459
+ else {
460
+ (0, utils_1.approveTx)([]);
461
+ }
483
462
  }
484
463
  else {
485
464
  // waiting for blind signing setting to be enabled
@@ -0,0 +1 @@
1
+ {"root":["../src/index.ts","../src/ledger-app.ts","../src/merkle.ts","../src/serde.test.ts","../src/serde.ts","../src/tx-encoder.ts","../src/types.ts","../test/merkle.test.ts","../test/tx-encoder.test.ts","../test/utils.ts","../test/wallet.test.ts"],"version":"5.8.3"}
@@ -0,0 +1,17 @@
1
+ const tseslint = require('typescript-eslint')
2
+ const eslintConfigPrettier = require('eslint-config-prettier')
3
+
4
+ module.exports = tseslint.config(
5
+ {
6
+ ignores: ['**/dist/', '**/templates/', '**/coverage/', 'eslint.config.js']
7
+ },
8
+ ...tseslint.configs.recommended,
9
+ eslintConfigPrettier,
10
+ {
11
+ languageOptions: {
12
+ parserOptions: {
13
+ project: 'tsconfig.json'
14
+ }
15
+ }
16
+ }
17
+ )
package/package.json CHANGED
@@ -1,21 +1,22 @@
1
1
  {
2
2
  "name": "@alephium/ledger-app",
3
- "version": "0.6.0",
3
+ "version": "0.6.1-rc.0",
4
4
  "license": "GPL",
5
+ "main": "dist/src/index.js",
5
6
  "types": "dist/src/index.d.ts",
7
+ "repository": {
8
+ "url": "https://github.com/alephium/ledger-alephium"
9
+ },
6
10
  "exports": {
7
11
  ".": "./dist/src/index.js"
8
12
  },
9
13
  "scripts": {
10
- "build": "npm run clean:windows && npm run clean:unix && npx --yes tsc --build .",
11
- "clean:unix": "node -e \"if (process.platform !== 'win32') process.exit(1)\" || rm -rf dist",
12
- "clean:windows": "node -e \"if (process.platform === 'win32') process.exit(1)\" || , if exist dist rmdir /Q /S dist",
13
- "lint": "eslint . --ext ts",
14
- "lint:fix": "eslint . --fix --ext ts",
14
+ "build": "rm -rf dist && tsc --build .",
15
+ "lint": "eslint .",
16
+ "lint:fix": "eslint . --fix",
15
17
  "test": "BACKEND=speculos jest -i --config ./jest-config.json",
16
18
  "speculos-test": "BACKEND=speculos jest -i --config ./jest-config.json",
17
- "device-test": "BACKEND=device jest -i --config ./jest-config.json",
18
- "pub": "npm run build && npm publish --access public"
19
+ "device-test": "BACKEND=device jest -i --config ./jest-config.json"
19
20
  },
20
21
  "prettier": {
21
22
  "printWidth": 120,
@@ -27,32 +28,27 @@
27
28
  "trailingComma": "none"
28
29
  },
29
30
  "dependencies": {
30
- "@alephium/web3": "^1.5.0",
31
- "@ledgerhq/hw-transport": "6.31.0",
31
+ "@alephium/web3": "3.0.0-test.9",
32
+ "@ledgerhq/hw-transport": "6.34.1",
32
33
  "blakejs": "^1.2.1"
33
34
  },
34
35
  "devDependencies": {
35
- "@alephium/cli": "^1.5.0",
36
- "@alephium/web3-test": "^1.5.0",
37
- "@alephium/web3-wallet": "^1.5.0",
38
- "@ledgerhq/hw-transport-node-hid": "6.29.1",
39
- "@ledgerhq/hw-transport-node-speculos": "6.29.0",
40
- "@types/elliptic": "^6.4.13",
41
- "@types/jest": "^27.5.1",
36
+ "@alephium/cli": "3.0.0-test.9",
37
+ "@alephium/web3-test": "3.0.0-test.9",
38
+ "@alephium/web3-wallet": "3.0.0-test.9",
39
+ "@ledgerhq/hw-transport-node-hid": "6.32.1",
40
+ "@ledgerhq/hw-transport-node-speculos": "6.33.1",
41
+ "@types/jest": "^29.5.0",
42
42
  "@types/node": "^20.8.10",
43
- "@typescript-eslint/eslint-plugin": "^4.30.0",
44
- "@typescript-eslint/parser": "^4.30.0",
45
- "eslint": "^7.32.0",
46
- "eslint-config-prettier": "^8.5.0",
47
- "eslint-plugin-prettier": "^4.0.0",
48
- "jest": "^28.1.0",
49
- "node-fetch": "^2.6.7",
50
- "ts-jest": "^28.0.2",
43
+ "eslint": "^9.0.0",
44
+ "eslint-config-prettier": "^10.0.0",
45
+ "typescript-eslint": "^8.0.0",
46
+ "jest": "^29.7.0",
47
+ "ts-jest": "^29.1.0",
51
48
  "ts-node": "^10.7.0",
52
- "typescript": "^4.4.2"
49
+ "typescript": "~5.8.0"
53
50
  },
54
51
  "engines": {
55
- "node": ">=14.0.0",
56
- "npm": ">=7.0.0"
52
+ "node": ">=18.0.0"
57
53
  }
58
54
  }
package/src/ledger-app.ts CHANGED
@@ -1,13 +1,10 @@
1
- import { Account, KeyType, addressFromPublicKey, binToHex, codec, encodeHexSignature, groupOfAddress } from '@alephium/web3'
1
+ import { GroupedAccount, GroupedKeyType, KeyType, addressFromPublicKey, binToHex, codec, encodeHexSignature, groupOfAddress } from '@alephium/web3'
2
2
  import Transport, { StatusCodes } from '@ledgerhq/hw-transport'
3
3
  import * as serde from './serde'
4
- import { ec as EC } from 'elliptic'
5
4
  import { MAX_TOKEN_SIZE, MAX_TOKEN_SYMBOL_LENGTH, TokenMetadata } from './types'
6
5
  import { encodeTokenMetadata, encodeUnsignedTx } from './tx-encoder'
7
6
  import { merkleTokens } from './merkle'
8
7
 
9
- const ec = new EC('secp256k1')
10
-
11
8
  export const CLA = 0x80
12
9
  export enum INS {
13
10
  GET_VERSION = 0x00,
@@ -36,25 +33,27 @@ export class AlephiumApp {
36
33
  return `${response[0]}.${response[1]}.${response[2]}`
37
34
  }
38
35
 
39
- async getAccount(startPath: string, targetGroup?: number, keyType?: KeyType, display = false): Promise<readonly [Account, number]> {
36
+ async getAccount(startPath: string, targetGroup?: number, keyType?: KeyType, display = false): Promise<readonly [GroupedAccount, number]> {
40
37
  if ((targetGroup ?? 0) >= GROUP_NUM) {
41
38
  throw Error(`Invalid targetGroup: ${targetGroup}`)
42
39
  }
43
40
 
44
- if (keyType === 'bip340-schnorr') {
45
- throw Error('BIP340-Schnorr is not supported yet')
41
+ if (keyType !== undefined && keyType !== 'default') {
42
+ throw Error(`Unsupported key type: ${keyType}`)
46
43
  }
47
44
 
48
45
  const p1 = targetGroup === undefined ? 0x00 : GROUP_NUM
49
46
  const p2 = targetGroup === undefined ? 0x00 : targetGroup
50
47
  const payload = Buffer.concat([serde.serializePath(startPath), Buffer.from([display ? 1 : 0])]);
51
48
  const response = await this.transport.send(CLA, INS.GET_PUBLIC_KEY, p1, p2, payload)
52
- const publicKey = ec.keyFromPublic(response.slice(0, 65)).getPublic(true, 'hex')
49
+ const prefix = (response[64] & 1) === 1 ? '03' : '02'
50
+ const publicKey = prefix + response.slice(1, 33).toString('hex')
53
51
  const address = addressFromPublicKey(publicKey)
54
52
  const group = groupOfAddress(address)
55
53
  const hdIndex = response.slice(65, 69).readUInt32BE(0)
56
54
 
57
- return [{ publicKey: publicKey, address: address, group: group, keyType: keyType ?? 'default' }, hdIndex] as const
55
+ const resolvedKeyType: GroupedKeyType = 'default'
56
+ return [{ publicKey, address, group, keyType: resolvedKeyType }, hdIndex] as const
58
57
  }
59
58
 
60
59
  async signHash(path: string, hash: Buffer): Promise<string> {
package/src/tx-encoder.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { tokenMerkleProofs } from "./merkle"
2
- import { checkTokenMetadata, serializePath, serializeSingleTokenMetadata } from "./serde"
2
+ import { serializePath, serializeSingleTokenMetadata } from "./serde"
3
3
  import { MAX_PAYLOAD_SIZE, TokenMetadata } from "./types"
4
4
 
5
5
  export interface Frame {
@@ -1,9 +1,15 @@
1
- import { hashPair, merkleTokens, tokenMerkleProofs, tokenMerkleRoot } from '../src/merkle'
1
+ import { generateProofs, hashPair, merkleTokens, tokenMerkleProofs, tokenMerkleRoot } from '../src/merkle'
2
2
  import { serializeSingleTokenMetadata } from '../src/serde'
3
3
  import { blake2b } from 'blakejs'
4
4
  import { binToHex } from '@alephium/web3'
5
5
 
6
6
  describe('Merkle', () => {
7
+ it('should generate the correct proofs', () => {
8
+ const { proofs, root } = generateProofs()
9
+ expect(JSON.stringify(proofs)).toBe(JSON.stringify(tokenMerkleProofs))
10
+ expect(root).toBe(binToHex(tokenMerkleRoot))
11
+ })
12
+
7
13
  it('should verify proofs', () => {
8
14
  for (const token of merkleTokens) {
9
15
  const proof = tokenMerkleProofs[token.tokenId]
package/test/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import SpeculosTransport from '@ledgerhq/hw-transport-node-speculos'
2
- import fetch from 'node-fetch'
3
2
  import { sleep } from '@alephium/web3'
4
3
  import Transport from '@ledgerhq/hw-transport'
5
4
  import NodeTransport from '@ledgerhq/hw-transport-node-hid'
@@ -53,11 +52,19 @@ const NanospClickTable = new Map([
53
52
  ])
54
53
 
55
54
  const StaxClickTable = new Map([
56
- [OutputType.Base, 2],
57
- [OutputType.Multisig, 3],
58
- [OutputType.Token, 3],
59
- [OutputType.BaseAndToken, 3],
60
- [OutputType.MultisigAndToken, 4],
55
+ [OutputType.Base, 1],
56
+ [OutputType.Multisig, 2],
57
+ [OutputType.Token, 2],
58
+ [OutputType.BaseAndToken, 2],
59
+ [OutputType.MultisigAndToken, 2],
60
+ ])
61
+
62
+ const FlexClickTable = new Map([
63
+ [OutputType.Base, 1],
64
+ [OutputType.Multisig, 2],
65
+ [OutputType.Token, 2],
66
+ [OutputType.BaseAndToken, 2],
67
+ [OutputType.MultisigAndToken, 3],
61
68
  ])
62
69
 
63
70
  function getOutputClickSize(outputType: OutputType) {
@@ -66,8 +73,8 @@ function getOutputClickSize(outputType: OutputType) {
66
73
  case 'nanos': return NanosClickTable.get(outputType)!
67
74
  case 'nanosp':
68
75
  case 'nanox': return NanospClickTable.get(outputType)!
69
- case 'stax':
70
- case 'flex': return StaxClickTable.get(outputType)!
76
+ case 'stax': return StaxClickTable.get(outputType)!
77
+ case 'flex': return FlexClickTable.get(outputType)!
71
78
  default: throw new Error(`Unknown model ${model}`)
72
79
  }
73
80
  }
@@ -92,15 +99,17 @@ interface Position {
92
99
 
93
100
  const STAX_CONTINUE_POSITION = { x: 342, y: 606 }
94
101
  const STAX_APPROVE_POSITION = { x: 200, y: 515 }
95
- const STAX_REJECT_POSITION = { x: 36, y: 606 }
96
102
  const STAX_SETTINGS_POSITION = { x: 342, y: 55 }
97
103
  const STAX_BLIND_SETTING_POSITION = { x: 342, y: 90 }
104
+ const STAX_GO_TO_SETTINGS = { x: 36, y: 606 }
105
+ const STAX_ACCEPT_RISK_POSITION = { x: 36, y: 606 }
98
106
 
99
107
  const FLEX_CONTINUE_POSITION = { x: 430, y: 550 }
100
108
  const FLEX_APPROVE_POSITION = { x: 240, y: 435 }
101
- const FLEX_REJECT_POSITION = { x: 55, y: 530 }
102
109
  const FLEX_SETTINGS_POSITION = { x: 405, y: 75 }
103
110
  const FLEX_BLIND_SETTING_POSITION = { x: 405, y: 96 }
111
+ const FLEX_GO_TO_SETTINGS = { x: 55, y: 530 }
112
+ const FLEX_ACCEPT_RISK_POSITION = { x: 55, y: 530 }
104
113
 
105
114
  async function touchPosition(pos: Position) {
106
115
  await sleep(1000)
@@ -110,14 +119,24 @@ async function touchPosition(pos: Position) {
110
119
  })
111
120
  }
112
121
 
113
- async function _touch(times: number) {
122
+ async function longPress(pos: Position) {
123
+ await sleep(1000)
124
+ return fetch(`http://localhost:25000/finger`, {
125
+ method: 'POST',
126
+ body: JSON.stringify({ action: 'press-and-release', x: pos.x, y: pos.y, delay: 3 })
127
+ })
128
+ }
129
+
130
+ async function _touch(times: number, approve: boolean = false) {
114
131
  const model = getModel()
115
132
  const continuePos = model === 'stax' ? STAX_CONTINUE_POSITION : FLEX_CONTINUE_POSITION
116
133
  for (let i = 0; i < times; i += 1) {
117
134
  await touchPosition(continuePos)
118
135
  }
119
- const approvePos = model === 'stax' ? STAX_APPROVE_POSITION : FLEX_APPROVE_POSITION
120
- await touchPosition(approvePos)
136
+ if (approve) {
137
+ const approvePos = model === 'stax' ? STAX_APPROVE_POSITION : FLEX_APPROVE_POSITION
138
+ await longPress(approvePos)
139
+ }
121
140
  }
122
141
 
123
142
  export async function staxFlexApproveOnce() {
@@ -129,16 +148,19 @@ export async function staxFlexApproveOnce() {
129
148
  }
130
149
 
131
150
  async function touch(outputs: OutputType[], hasExternalInputs: boolean) {
132
- await sleep(1000);
151
+ await sleep(3000);
133
152
  if (hasExternalInputs) {
134
153
  await staxFlexApproveOnce()
135
154
  }
136
155
 
156
+ _touch(1) // the first review page
157
+ await sleep(1000)
158
+
137
159
  for (let index = 0; index < outputs.length; index += 1) {
138
160
  await _touch(getOutputClickSize(outputs[index]))
139
161
  }
140
162
 
141
- await _touch(2) // fees
163
+ await _touch(1, true) // fees
142
164
  }
143
165
 
144
166
  export async function approveTx(outputs: OutputType[], hasExternalInputs: boolean = false) {
@@ -147,7 +169,7 @@ export async function approveTx(outputs: OutputType[], hasExternalInputs: boolea
147
169
  const isSelfTransfer = outputs.length === 0 && !hasExternalInputs
148
170
  if (isSelfTransfer) {
149
171
  if (isStaxOrFlex()) {
150
- await _touch(2)
172
+ await _touch(2, true)
151
173
  } else {
152
174
  await clickAndApprove(2)
153
175
  }
@@ -164,7 +186,7 @@ export async function approveTx(outputs: OutputType[], hasExternalInputs: boolea
164
186
  export async function approveHash() {
165
187
  if (!needToAutoApprove()) return
166
188
  if (isStaxOrFlex()) {
167
- return await _touch(3)
189
+ return await _touch(2, true)
168
190
  }
169
191
  if (getModel() === 'nanos') {
170
192
  await clickAndApprove(5)
@@ -176,7 +198,9 @@ export async function approveHash() {
176
198
  export async function approveAddress() {
177
199
  if (!needToAutoApprove()) return
178
200
  if (isStaxOrFlex()) {
179
- return await _touch(2)
201
+ await _touch(1)
202
+ await staxFlexApproveOnce()
203
+ return
180
204
  }
181
205
  if (getModel() === 'nanos') {
182
206
  await clickAndApprove(4)
@@ -185,7 +209,7 @@ export async function approveAddress() {
185
209
  }
186
210
  }
187
211
 
188
- function isStaxOrFlex(): boolean {
212
+ export function isStaxOrFlex(): boolean {
189
213
  return !getModel().startsWith('nano')
190
214
  }
191
215
 
@@ -193,13 +217,24 @@ export function isNanos(): boolean {
193
217
  return getModel() === 'nanos'
194
218
  }
195
219
 
196
- export function skipBlindSigningWarning() {
220
+ export async function skipBlindSigningWarning() {
197
221
  if (!needToAutoApprove()) return
198
222
  if (isStaxOrFlex()) {
199
- const rejectPos = getModel() === 'stax' ? STAX_REJECT_POSITION : FLEX_REJECT_POSITION
200
- touchPosition(rejectPos)
223
+ await sleep(3000)
224
+ const goToSettings = getModel() === 'stax' ? STAX_GO_TO_SETTINGS : FLEX_GO_TO_SETTINGS
225
+ await touchPosition(goToSettings)
226
+ } else {
227
+ await clickAndApprove(3)
228
+ }
229
+ }
230
+
231
+ export async function staxFlexAcceptRisk() {
232
+ if (!needToAutoApprove()) return
233
+ await sleep(3000)
234
+ if (getModel() === 'stax') {
235
+ await touchPosition(STAX_ACCEPT_RISK_POSITION)
201
236
  } else {
202
- clickAndApprove(3)
237
+ await touchPosition(FLEX_ACCEPT_RISK_POSITION)
203
238
  }
204
239
  }
205
240
 
@@ -3,8 +3,7 @@ import { ALPH_TOKEN_ID, Address, DUST_AMOUNT, NodeProvider, ONE_ALPH, binToHex,
3
3
  import { getSigner, mintToken, transfer } from '@alephium/web3-test'
4
4
  import { PrivateKeyWallet } from '@alephium/web3-wallet'
5
5
  import blake from 'blakejs'
6
- import { approveAddress, approveHash, approveTx, createTransport, enableBlindSigning, getRandomInt, isNanos, needToAutoApprove, OutputType, skipBlindSigningWarning, staxFlexApproveOnce } from './utils'
7
- import { TokenMetadata } from '../src/types'
6
+ import { approveAddress, approveHash, approveTx, createTransport, enableBlindSigning, getRandomInt, isNanos, isStaxOrFlex, needToAutoApprove, OutputType, skipBlindSigningWarning, staxFlexAcceptRisk } from './utils'
8
7
  import { randomBytes } from 'crypto'
9
8
  import { merkleTokens, tokenMerkleProofs } from '../src/merkle'
10
9
 
@@ -241,38 +240,6 @@ describe('ledger wallet', () => {
241
240
  await app.close()
242
241
  }, 120000)
243
242
 
244
- async function genTokensAndDestinations(
245
- fromAddress: string,
246
- toAddress: string,
247
- mintAmount: bigint,
248
- transferAmount: bigint
249
- ) {
250
- const tokens: TokenMetadata[] = []
251
- const tokenSymbol = 'TestTokenABC'
252
- const destinations: node.Destination[] = []
253
- for (let i = 0; i < 5; i += 1) {
254
- const tokenInfo = await mintToken(fromAddress, mintAmount);
255
- const tokenMetadata: TokenMetadata = {
256
- version: 0,
257
- tokenId: tokenInfo.contractId,
258
- symbol: tokenSymbol.slice(0, tokenSymbol.length - i),
259
- decimals: 18 - i
260
- }
261
- tokens.push(tokenMetadata)
262
- destinations.push({
263
- address: toAddress,
264
- attoAlphAmount: DUST_AMOUNT.toString(),
265
- tokens: [
266
- {
267
- id: tokenMetadata.tokenId,
268
- amount: transferAmount.toString()
269
- }
270
- ]
271
- })
272
- }
273
- return { tokens, destinations }
274
- }
275
-
276
243
  it('should transfer tokens with proof', async () => {
277
244
  const transport = await createTransport()
278
245
  const app = new AlephiumApp(transport)
@@ -547,7 +514,11 @@ describe('ledger wallet', () => {
547
514
 
548
515
  await enableBlindSigning()
549
516
  if (needToAutoApprove()) {
550
- staxFlexApproveOnce().then(() => approveTx([]))
517
+ if (isStaxOrFlex()) {
518
+ staxFlexAcceptRisk().then(() => approveTx([]))
519
+ } else {
520
+ approveTx([])
521
+ }
551
522
  } else {
552
523
  // waiting for blind signing setting to be enabled
553
524
  await sleep(20000)
package/tsconfig.json CHANGED
@@ -12,7 +12,6 @@
12
12
  "declaration": true,
13
13
  "moduleResolution": "node",
14
14
  "resolveJsonModule": true,
15
- "experimentalDecorators": true,
16
15
  "noImplicitOverride": true
17
16
  },
18
17
  "exclude": ["node_modules"],
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- **/dist/
2
- **/templates/
package/.eslintrc.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "extends": [
3
- "prettier",
4
- "plugin:prettier/recommended",
5
- "plugin:@typescript-eslint/recommended"
6
- ],
7
- "rules": {
8
- "header/header": ["off"]
9
- },
10
- "parserOptions": {
11
- "project": "tsconfig.json",
12
- "ecmaVersion": 2020,
13
- "sourceType": "module"
14
- },
15
- "parser": "@typescript-eslint/parser"
16
- }