@alephium/ledger-app 0.6.2 → 0.6.3-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.
- package/dist/src/ledger-app.d.ts +2 -3
- package/dist/src/ledger-app.js +24 -14
- package/dist/src/merkle.d.ts +1 -1
- package/dist/src/merkle.js +3 -3
- package/dist/src/serde.d.ts +0 -1
- package/dist/src/serde.js +6 -6
- package/dist/src/tx-encoder.d.ts +0 -1
- package/dist/src/tx-encoder.js +4 -5
- package/dist/test/utils.d.ts +6 -3
- package/dist/test/utils.js +48 -25
- package/dist/test/wallet.test.js +11 -32
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docker/devnet.conf +0 -3
- package/docker/docker-compose.yaml +1 -1
- package/eslint.config.js +17 -0
- package/package.json +24 -28
- package/src/ledger-app.ts +8 -9
- package/src/tx-encoder.ts +1 -1
- package/test/utils.ts +28 -6
- package/test/wallet.test.ts +7 -36
- package/tsconfig.json +0 -1
- package/.eslintignore +0 -2
- package/.eslintrc.json +0 -16
package/dist/src/ledger-app.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
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 [
|
|
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
|
}
|
package/dist/src/ledger-app.js
CHANGED
|
@@ -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 (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
62
|
-
throw Error(
|
|
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
|
|
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
|
-
|
|
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) {
|
package/dist/src/merkle.d.ts
CHANGED
|
@@ -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>;
|
package/dist/src/merkle.js
CHANGED
|
@@ -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.
|
|
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;
|
package/dist/src/serde.d.ts
CHANGED
package/dist/src/serde.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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;
|
package/dist/src/tx-encoder.d.ts
CHANGED
package/dist/src/tx-encoder.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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;
|
package/dist/test/utils.d.ts
CHANGED
|
@@ -2,15 +2,18 @@ import Transport from '@ledgerhq/hw-transport';
|
|
|
2
2
|
export declare enum OutputType {
|
|
3
3
|
Base = 0,
|
|
4
4
|
Multisig = 1,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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>;
|
|
13
15
|
export declare function isStaxOrFlex(): boolean;
|
|
16
|
+
export declare function isNanos(): boolean;
|
|
14
17
|
export declare function skipBlindSigningWarning(): Promise<void>;
|
|
15
18
|
export declare function staxFlexAcceptRisk(): Promise<void>;
|
|
16
19
|
export declare function enableBlindSigning(): Promise<void>;
|
package/dist/test/utils.js
CHANGED
|
@@ -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.
|
|
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 (
|
|
24
|
+
return fetch(`http://localhost:25000/button/${button}`, {
|
|
14
25
|
method: 'POST',
|
|
15
26
|
body: JSON.stringify({ action: 'press-and-release' })
|
|
16
27
|
});
|
|
@@ -23,16 +34,27 @@ async function clickAndApprove(times) {
|
|
|
23
34
|
}
|
|
24
35
|
function getModel() {
|
|
25
36
|
const model = process.env.MODEL;
|
|
26
|
-
return model ? model : '
|
|
37
|
+
return model ? model : 'nanos';
|
|
27
38
|
}
|
|
28
39
|
var OutputType;
|
|
29
40
|
(function (OutputType) {
|
|
30
41
|
OutputType[OutputType["Base"] = 0] = "Base";
|
|
31
42
|
OutputType[OutputType["Multisig"] = 1] = "Multisig";
|
|
32
|
-
OutputType[OutputType["
|
|
33
|
-
OutputType[OutputType["
|
|
34
|
-
OutputType[OutputType["
|
|
35
|
-
|
|
43
|
+
OutputType[OutputType["Nanos10"] = 2] = "Nanos10";
|
|
44
|
+
OutputType[OutputType["Nanos11"] = 3] = "Nanos11";
|
|
45
|
+
OutputType[OutputType["Token"] = 4] = "Token";
|
|
46
|
+
OutputType[OutputType["BaseAndToken"] = 5] = "BaseAndToken";
|
|
47
|
+
OutputType[OutputType["MultisigAndToken"] = 6] = "MultisigAndToken";
|
|
48
|
+
})(OutputType || (exports.OutputType = OutputType = {}));
|
|
49
|
+
const NanosClickTable = new Map([
|
|
50
|
+
[OutputType.Base, 5],
|
|
51
|
+
[OutputType.Multisig, 10],
|
|
52
|
+
[OutputType.Nanos10, 10],
|
|
53
|
+
[OutputType.Nanos11, 11],
|
|
54
|
+
[OutputType.Token, 11],
|
|
55
|
+
[OutputType.BaseAndToken, 12],
|
|
56
|
+
[OutputType.MultisigAndToken, 16],
|
|
57
|
+
]);
|
|
36
58
|
const NanospClickTable = new Map([
|
|
37
59
|
[OutputType.Base, 3],
|
|
38
60
|
[OutputType.Multisig, 5],
|
|
@@ -57,6 +79,7 @@ const FlexClickTable = new Map([
|
|
|
57
79
|
function getOutputClickSize(outputType) {
|
|
58
80
|
const model = getModel();
|
|
59
81
|
switch (model) {
|
|
82
|
+
case 'nanos': return NanosClickTable.get(outputType);
|
|
60
83
|
case 'nanosp':
|
|
61
84
|
case 'nanox': return NanospClickTable.get(outputType);
|
|
62
85
|
case 'stax': return StaxClickTable.get(outputType);
|
|
@@ -76,28 +99,26 @@ async function click(outputs, hasExternalInputs) {
|
|
|
76
99
|
}
|
|
77
100
|
const STAX_CONTINUE_POSITION = { x: 342, y: 606 };
|
|
78
101
|
const STAX_APPROVE_POSITION = { x: 200, y: 515 };
|
|
79
|
-
const STAX_REJECT_POSITION = { x: 36, y: 606 };
|
|
80
102
|
const STAX_SETTINGS_POSITION = { x: 342, y: 55 };
|
|
81
103
|
const STAX_BLIND_SETTING_POSITION = { x: 342, y: 90 };
|
|
82
104
|
const STAX_GO_TO_SETTINGS = { x: 36, y: 606 };
|
|
83
105
|
const STAX_ACCEPT_RISK_POSITION = { x: 36, y: 606 };
|
|
84
106
|
const FLEX_CONTINUE_POSITION = { x: 430, y: 550 };
|
|
85
107
|
const FLEX_APPROVE_POSITION = { x: 240, y: 435 };
|
|
86
|
-
const FLEX_REJECT_POSITION = { x: 55, y: 530 };
|
|
87
108
|
const FLEX_SETTINGS_POSITION = { x: 405, y: 75 };
|
|
88
109
|
const FLEX_BLIND_SETTING_POSITION = { x: 405, y: 96 };
|
|
89
110
|
const FLEX_GO_TO_SETTINGS = { x: 55, y: 530 };
|
|
90
111
|
const FLEX_ACCEPT_RISK_POSITION = { x: 55, y: 530 };
|
|
91
112
|
async function touchPosition(pos) {
|
|
92
113
|
await (0, web3_1.sleep)(1000);
|
|
93
|
-
return (
|
|
114
|
+
return fetch(`http://localhost:25000/finger`, {
|
|
94
115
|
method: 'POST',
|
|
95
116
|
body: JSON.stringify({ action: 'press-and-release', x: pos.x, y: pos.y })
|
|
96
117
|
});
|
|
97
118
|
}
|
|
98
119
|
async function longPress(pos) {
|
|
99
120
|
await (0, web3_1.sleep)(1000);
|
|
100
|
-
return (
|
|
121
|
+
return fetch(`http://localhost:25000/finger`, {
|
|
101
122
|
method: 'POST',
|
|
102
123
|
body: JSON.stringify({ action: 'press-and-release', x: pos.x, y: pos.y, delay: 3 })
|
|
103
124
|
});
|
|
@@ -121,7 +142,6 @@ async function staxFlexApproveOnce() {
|
|
|
121
142
|
await touchPosition(FLEX_APPROVE_POSITION);
|
|
122
143
|
}
|
|
123
144
|
}
|
|
124
|
-
exports.staxFlexApproveOnce = staxFlexApproveOnce;
|
|
125
145
|
async function touch(outputs, hasExternalInputs) {
|
|
126
146
|
await (0, web3_1.sleep)(3000);
|
|
127
147
|
if (hasExternalInputs) {
|
|
@@ -155,16 +175,19 @@ async function approveTx(outputs, hasExternalInputs = false) {
|
|
|
155
175
|
await click(outputs, hasExternalInputs);
|
|
156
176
|
}
|
|
157
177
|
}
|
|
158
|
-
exports.approveTx = approveTx;
|
|
159
178
|
async function approveHash() {
|
|
160
179
|
if (!needToAutoApprove())
|
|
161
180
|
return;
|
|
162
181
|
if (isStaxOrFlex()) {
|
|
163
182
|
return await _touch(2, true);
|
|
164
183
|
}
|
|
165
|
-
|
|
184
|
+
if (getModel() === 'nanos') {
|
|
185
|
+
await clickAndApprove(5);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
await clickAndApprove(3);
|
|
189
|
+
}
|
|
166
190
|
}
|
|
167
|
-
exports.approveHash = approveHash;
|
|
168
191
|
async function approveAddress() {
|
|
169
192
|
if (!needToAutoApprove())
|
|
170
193
|
return;
|
|
@@ -173,13 +196,19 @@ async function approveAddress() {
|
|
|
173
196
|
await staxFlexApproveOnce();
|
|
174
197
|
return;
|
|
175
198
|
}
|
|
176
|
-
|
|
199
|
+
if (getModel() === 'nanos') {
|
|
200
|
+
await clickAndApprove(4);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
await clickAndApprove(2);
|
|
204
|
+
}
|
|
177
205
|
}
|
|
178
|
-
exports.approveAddress = approveAddress;
|
|
179
206
|
function isStaxOrFlex() {
|
|
180
207
|
return !getModel().startsWith('nano');
|
|
181
208
|
}
|
|
182
|
-
|
|
209
|
+
function isNanos() {
|
|
210
|
+
return getModel() === 'nanos';
|
|
211
|
+
}
|
|
183
212
|
async function skipBlindSigningWarning() {
|
|
184
213
|
if (!needToAutoApprove())
|
|
185
214
|
return;
|
|
@@ -192,7 +221,6 @@ async function skipBlindSigningWarning() {
|
|
|
192
221
|
await clickAndApprove(3);
|
|
193
222
|
}
|
|
194
223
|
}
|
|
195
|
-
exports.skipBlindSigningWarning = skipBlindSigningWarning;
|
|
196
224
|
async function staxFlexAcceptRisk() {
|
|
197
225
|
if (!needToAutoApprove())
|
|
198
226
|
return;
|
|
@@ -204,7 +232,6 @@ async function staxFlexAcceptRisk() {
|
|
|
204
232
|
await touchPosition(FLEX_ACCEPT_RISK_POSITION);
|
|
205
233
|
}
|
|
206
234
|
}
|
|
207
|
-
exports.staxFlexAcceptRisk = staxFlexAcceptRisk;
|
|
208
235
|
async function enableBlindSigning() {
|
|
209
236
|
if (!needToAutoApprove())
|
|
210
237
|
return;
|
|
@@ -220,13 +247,11 @@ async function enableBlindSigning() {
|
|
|
220
247
|
await clickAndApprove(2);
|
|
221
248
|
}
|
|
222
249
|
}
|
|
223
|
-
exports.enableBlindSigning = enableBlindSigning;
|
|
224
250
|
function getRandomInt(min, max) {
|
|
225
251
|
min = Math.ceil(min);
|
|
226
252
|
max = Math.floor(max);
|
|
227
253
|
return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
|
|
228
254
|
}
|
|
229
|
-
exports.getRandomInt = getRandomInt;
|
|
230
255
|
function needToAutoApprove() {
|
|
231
256
|
switch (process.env.BACKEND) {
|
|
232
257
|
case "speculos": return true;
|
|
@@ -234,7 +259,6 @@ function needToAutoApprove() {
|
|
|
234
259
|
default: throw new Error(`Invalid backend: ${process.env.BACKEND}`);
|
|
235
260
|
}
|
|
236
261
|
}
|
|
237
|
-
exports.needToAutoApprove = needToAutoApprove;
|
|
238
262
|
const ApduPort = 9999;
|
|
239
263
|
async function createTransport() {
|
|
240
264
|
switch (process.env.BACKEND) {
|
|
@@ -243,4 +267,3 @@ async function createTransport() {
|
|
|
243
267
|
default: throw new Error(`Invalid backend: ${process.env.BACKEND}`);
|
|
244
268
|
}
|
|
245
269
|
}
|
|
246
|
-
exports.createTransport = createTransport;
|
package/dist/test/wallet.test.js
CHANGED
|
@@ -36,7 +36,7 @@ describe('ledger wallet', () => {
|
|
|
36
36
|
const transport = await (0, utils_1.createTransport)();
|
|
37
37
|
const app = new ledger_app_1.AlephiumApp(transport);
|
|
38
38
|
const version = await app.getVersion();
|
|
39
|
-
expect(version).toBe('0.4.
|
|
39
|
+
expect(version).toBe('0.4.0');
|
|
40
40
|
await app.close();
|
|
41
41
|
});
|
|
42
42
|
it('should get public key', async () => {
|
|
@@ -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) => {
|
|
@@ -269,7 +243,12 @@ describe('ledger wallet', () => {
|
|
|
269
243
|
fixedOutputs: outputs
|
|
270
244
|
};
|
|
271
245
|
const encodedUnsignedTx = web3_1.codec.unsignedTxCodec.encodeApiUnsignedTx(unsignedTx);
|
|
272
|
-
(0, utils_1.
|
|
246
|
+
if ((0, utils_1.isNanos)()) {
|
|
247
|
+
(0, utils_1.approveTx)([utils_1.OutputType.Nanos11, utils_1.OutputType.Nanos10, utils_1.OutputType.Nanos10, utils_1.OutputType.Nanos10, utils_1.OutputType.Nanos11]);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
(0, utils_1.approveTx)(Array(5).fill(utils_1.OutputType.BaseAndToken));
|
|
251
|
+
}
|
|
273
252
|
const signature = await app.signUnsignedTx(path, Buffer.from(encodedUnsignedTx));
|
|
274
253
|
const txId = blakejs_1.default.blake2b(encodedUnsignedTx, undefined, 32);
|
|
275
254
|
expect((0, web3_1.transactionVerifySignature)((0, web3_1.binToHex)(txId), testAccount.publicKey, signature)).toBe(true);
|
|
@@ -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"}
|
package/docker/devnet.conf
CHANGED
|
@@ -29,11 +29,8 @@ alephium.consensus.mainnet.block-target-time = 10 millis
|
|
|
29
29
|
alephium.consensus.mainnet.uncle-dependency-gap-time = 0 seconds
|
|
30
30
|
alephium.consensus.rhone.block-target-time = 5 millis
|
|
31
31
|
alephium.consensus.rhone.uncle-dependency-gap-time = 0 seconds
|
|
32
|
-
alephium.consensus.danube.block-target-time = 5 millis
|
|
33
|
-
alephium.consensus.danube.uncle-dependency-gap-time = 0 seconds
|
|
34
32
|
alephium.network.leman-hard-fork-timestamp = 0
|
|
35
33
|
alephium.network.rhone-hard-fork-timestamp = 0
|
|
36
|
-
alephium.network.danube-hard-fork-timestamp = 0
|
|
37
34
|
|
|
38
35
|
alephium.network.network-id = 4
|
|
39
36
|
alephium.discovery.bootstrap = []
|
package/eslint.config.js
ADDED
|
@@ -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.
|
|
3
|
+
"version": "0.6.3-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": "
|
|
11
|
-
"
|
|
12
|
-
"
|
|
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": "
|
|
31
|
-
"@ledgerhq/hw-transport": "
|
|
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": "
|
|
36
|
-
"@alephium/web3-test": "
|
|
37
|
-
"@alephium/web3-wallet": "
|
|
38
|
-
"@ledgerhq/hw-transport-node-hid": "
|
|
39
|
-
"@ledgerhq/hw-transport-node-speculos": "
|
|
40
|
-
"@types/
|
|
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
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"eslint": "^
|
|
46
|
-
"
|
|
47
|
-
"
|
|
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": "
|
|
49
|
+
"typescript": "~5.8.0"
|
|
53
50
|
},
|
|
54
51
|
"engines": {
|
|
55
|
-
"node": ">=
|
|
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 {
|
|
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 [
|
|
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
|
|
45
|
-
throw Error(
|
|
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
|
|
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
|
-
|
|
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 {
|
|
2
|
+
import { serializePath, serializeSingleTokenMetadata } from "./serde"
|
|
3
3
|
import { MAX_PAYLOAD_SIZE, TokenMetadata } from "./types"
|
|
4
4
|
|
|
5
5
|
export interface Frame {
|
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'
|
|
@@ -21,17 +20,29 @@ async function clickAndApprove(times: number) {
|
|
|
21
20
|
|
|
22
21
|
function getModel(): string {
|
|
23
22
|
const model = process.env.MODEL
|
|
24
|
-
return model ? model as string : '
|
|
23
|
+
return model ? model as string : 'nanos'
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
export enum OutputType {
|
|
28
27
|
Base,
|
|
29
28
|
Multisig,
|
|
29
|
+
Nanos10,
|
|
30
|
+
Nanos11,
|
|
30
31
|
Token,
|
|
31
32
|
BaseAndToken,
|
|
32
33
|
MultisigAndToken
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
const NanosClickTable = new Map([
|
|
37
|
+
[OutputType.Base, 5],
|
|
38
|
+
[OutputType.Multisig, 10],
|
|
39
|
+
[OutputType.Nanos10, 10],
|
|
40
|
+
[OutputType.Nanos11, 11],
|
|
41
|
+
[OutputType.Token, 11],
|
|
42
|
+
[OutputType.BaseAndToken, 12],
|
|
43
|
+
[OutputType.MultisigAndToken, 16],
|
|
44
|
+
])
|
|
45
|
+
|
|
35
46
|
const NanospClickTable = new Map([
|
|
36
47
|
[OutputType.Base, 3],
|
|
37
48
|
[OutputType.Multisig, 5],
|
|
@@ -59,6 +70,7 @@ const FlexClickTable = new Map([
|
|
|
59
70
|
function getOutputClickSize(outputType: OutputType) {
|
|
60
71
|
const model = getModel()
|
|
61
72
|
switch (model) {
|
|
73
|
+
case 'nanos': return NanosClickTable.get(outputType)!
|
|
62
74
|
case 'nanosp':
|
|
63
75
|
case 'nanox': return NanospClickTable.get(outputType)!
|
|
64
76
|
case 'stax': return StaxClickTable.get(outputType)!
|
|
@@ -87,7 +99,6 @@ interface Position {
|
|
|
87
99
|
|
|
88
100
|
const STAX_CONTINUE_POSITION = { x: 342, y: 606 }
|
|
89
101
|
const STAX_APPROVE_POSITION = { x: 200, y: 515 }
|
|
90
|
-
const STAX_REJECT_POSITION = { x: 36, y: 606 }
|
|
91
102
|
const STAX_SETTINGS_POSITION = { x: 342, y: 55 }
|
|
92
103
|
const STAX_BLIND_SETTING_POSITION = { x: 342, y: 90 }
|
|
93
104
|
const STAX_GO_TO_SETTINGS = { x: 36, y: 606 }
|
|
@@ -95,7 +106,6 @@ const STAX_ACCEPT_RISK_POSITION = { x: 36, y: 606 }
|
|
|
95
106
|
|
|
96
107
|
const FLEX_CONTINUE_POSITION = { x: 430, y: 550 }
|
|
97
108
|
const FLEX_APPROVE_POSITION = { x: 240, y: 435 }
|
|
98
|
-
const FLEX_REJECT_POSITION = { x: 55, y: 530 }
|
|
99
109
|
const FLEX_SETTINGS_POSITION = { x: 405, y: 75 }
|
|
100
110
|
const FLEX_BLIND_SETTING_POSITION = { x: 405, y: 96 }
|
|
101
111
|
const FLEX_GO_TO_SETTINGS = { x: 55, y: 530 }
|
|
@@ -178,7 +188,11 @@ export async function approveHash() {
|
|
|
178
188
|
if (isStaxOrFlex()) {
|
|
179
189
|
return await _touch(2, true)
|
|
180
190
|
}
|
|
181
|
-
|
|
191
|
+
if (getModel() === 'nanos') {
|
|
192
|
+
await clickAndApprove(5)
|
|
193
|
+
} else {
|
|
194
|
+
await clickAndApprove(3)
|
|
195
|
+
}
|
|
182
196
|
}
|
|
183
197
|
|
|
184
198
|
export async function approveAddress() {
|
|
@@ -188,13 +202,21 @@ export async function approveAddress() {
|
|
|
188
202
|
await staxFlexApproveOnce()
|
|
189
203
|
return
|
|
190
204
|
}
|
|
191
|
-
|
|
205
|
+
if (getModel() === 'nanos') {
|
|
206
|
+
await clickAndApprove(4)
|
|
207
|
+
} else {
|
|
208
|
+
await clickAndApprove(2)
|
|
209
|
+
}
|
|
192
210
|
}
|
|
193
211
|
|
|
194
212
|
export function isStaxOrFlex(): boolean {
|
|
195
213
|
return !getModel().startsWith('nano')
|
|
196
214
|
}
|
|
197
215
|
|
|
216
|
+
export function isNanos(): boolean {
|
|
217
|
+
return getModel() === 'nanos'
|
|
218
|
+
}
|
|
219
|
+
|
|
198
220
|
export async function skipBlindSigningWarning() {
|
|
199
221
|
if (!needToAutoApprove()) return
|
|
200
222
|
if (isStaxOrFlex()) {
|
package/test/wallet.test.ts
CHANGED
|
@@ -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, isStaxOrFlex, needToAutoApprove, OutputType, skipBlindSigningWarning, staxFlexAcceptRisk
|
|
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
|
|
|
@@ -37,7 +36,7 @@ describe('ledger wallet', () => {
|
|
|
37
36
|
const transport = await createTransport()
|
|
38
37
|
const app = new AlephiumApp(transport)
|
|
39
38
|
const version = await app.getVersion()
|
|
40
|
-
expect(version).toBe('0.4.
|
|
39
|
+
expect(version).toBe('0.4.0')
|
|
41
40
|
await app.close()
|
|
42
41
|
})
|
|
43
42
|
|
|
@@ -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)
|
|
@@ -309,7 +276,11 @@ describe('ledger wallet', () => {
|
|
|
309
276
|
}
|
|
310
277
|
const encodedUnsignedTx = codec.unsignedTxCodec.encodeApiUnsignedTx(unsignedTx)
|
|
311
278
|
|
|
312
|
-
|
|
279
|
+
if (isNanos()) {
|
|
280
|
+
approveTx([OutputType.Nanos11, OutputType.Nanos10, OutputType.Nanos10, OutputType.Nanos10, OutputType.Nanos11])
|
|
281
|
+
} else {
|
|
282
|
+
approveTx(Array(5).fill(OutputType.BaseAndToken))
|
|
283
|
+
}
|
|
313
284
|
const signature = await app.signUnsignedTx(path, Buffer.from(encodedUnsignedTx))
|
|
314
285
|
const txId = blake.blake2b(encodedUnsignedTx, undefined, 32)
|
|
315
286
|
expect(transactionVerifySignature(binToHex(txId), testAccount.publicKey, signature)).toBe(true)
|
package/tsconfig.json
CHANGED
package/.eslintignore
DELETED
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
|
-
}
|