@ledgerhq/hw-app-solana 7.2.4 → 7.3.0-nightly.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/.turbo/turbo-build.log +1 -1
- package/.unimportedrc.json +2 -1
- package/CHANGELOG.md +6 -0
- package/README.md +19 -0
- package/jest.config.ts +17 -0
- package/lib/Solana.d.ts +14 -0
- package/lib/Solana.d.ts.map +1 -1
- package/lib/Solana.js +96 -79
- package/lib/Solana.js.map +1 -1
- package/lib-es/Solana.d.ts +14 -0
- package/lib-es/Solana.d.ts.map +1 -1
- package/lib-es/Solana.js +96 -79
- package/lib-es/Solana.js.map +1 -1
- package/package.json +6 -4
- package/src/Solana.ts +50 -3
package/.turbo/turbo-build.log
CHANGED
package/.unimportedrc.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @ledgerhq/hw-app-solana
|
|
2
2
|
|
|
3
|
+
## 7.3.0-nightly.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#9298](https://github.com/LedgerHQ/ledger-live/pull/9298) [`2785d49`](https://github.com/LedgerHQ/ledger-live/commit/2785d49ac320498f98ed39b4eccc48310ad35fe1) Thanks [@Canestin](https://github.com/Canestin)! - config coin-integration env for sonarqube
|
|
8
|
+
|
|
3
9
|
## 7.2.4
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -53,6 +53,9 @@ If ledger returns error `6808` - enable blind signature in settings (not needed
|
|
|
53
53
|
* [Examples](#examples-3)
|
|
54
54
|
* [getAppConfiguration](#getappconfiguration)
|
|
55
55
|
* [Examples](#examples-4)
|
|
56
|
+
* [getChallenge](#getchallenge)
|
|
57
|
+
* [provideTrustedName](#providetrustedname)
|
|
58
|
+
* [Parameters](#parameters-4)
|
|
56
59
|
|
|
57
60
|
### Solana
|
|
58
61
|
|
|
@@ -135,3 +138,19 @@ solana.getAppConfiguration().then(r => r.version)
|
|
|
135
138
|
```
|
|
136
139
|
|
|
137
140
|
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<AppConfig>** application config object
|
|
141
|
+
|
|
142
|
+
#### getChallenge
|
|
143
|
+
|
|
144
|
+
Method returning a 4 bytes TLV challenge as an hex string
|
|
145
|
+
|
|
146
|
+
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** 
|
|
147
|
+
|
|
148
|
+
#### provideTrustedName
|
|
149
|
+
|
|
150
|
+
Provides a trusted name to be displayed during transactions in place of the token address it is associated to. It shall be run just before a transaction involving the associated address that would be displayed on the device.
|
|
151
|
+
|
|
152
|
+
##### Parameters
|
|
153
|
+
|
|
154
|
+
* `data` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** a stringified buffer of some TLV encoded data to represent the trusted name
|
|
155
|
+
|
|
156
|
+
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)>** a boolean
|
package/jest.config.ts
CHANGED
|
@@ -3,4 +3,21 @@ import baseConfig from "../../jest.config";
|
|
|
3
3
|
export default {
|
|
4
4
|
...baseConfig,
|
|
5
5
|
rootDir: __dirname,
|
|
6
|
+
collectCoverageFrom: [
|
|
7
|
+
"src/**/*.ts",
|
|
8
|
+
"!src/**/*.test.{ts,tsx}",
|
|
9
|
+
"!src/**/*.spec.{ts,tsx}",
|
|
10
|
+
"!src/**/__tests__/**",
|
|
11
|
+
"!tests/**",
|
|
12
|
+
],
|
|
13
|
+
coverageReporters: ["json", ["lcov", { projectRoot: "../" }], "json-summary", "text"],
|
|
14
|
+
reporters: [
|
|
15
|
+
[
|
|
16
|
+
"jest-sonar",
|
|
17
|
+
{
|
|
18
|
+
outputName: "hw-app-solana-sonar-executionTests-report.xml",
|
|
19
|
+
reportedFilePath: "absolute",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
],
|
|
6
23
|
};
|
package/lib/Solana.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import Transport from "@ledgerhq/hw-transport";
|
|
3
4
|
/**
|
|
4
5
|
* Solana API
|
|
@@ -66,6 +67,19 @@ export default class Solana {
|
|
|
66
67
|
* solana.getAppConfiguration().then(r => r.version)
|
|
67
68
|
*/
|
|
68
69
|
getAppConfiguration(): Promise<AppConfig>;
|
|
70
|
+
/**
|
|
71
|
+
* Method returning a 4 bytes TLV challenge as an hex string
|
|
72
|
+
*
|
|
73
|
+
* @returns {Promise<string>}
|
|
74
|
+
*/
|
|
75
|
+
getChallenge(): Promise<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Provides a trusted name to be displayed during transactions in place of the token address it is associated to. It shall be run just before a transaction involving the associated address that would be displayed on the device.
|
|
78
|
+
*
|
|
79
|
+
* @param data a stringified buffer of some TLV encoded data to represent the trusted name
|
|
80
|
+
* @returns a boolean
|
|
81
|
+
*/
|
|
82
|
+
provideTrustedName(data: string): Promise<boolean>;
|
|
69
83
|
private pathToBuffer;
|
|
70
84
|
private serializePath;
|
|
71
85
|
private sendToDevice;
|
package/lib/Solana.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Solana.d.ts","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Solana.d.ts","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":";;AAAA,OAAO,SAAS,MAAM,wBAAwB,CAAC;AA8B/C;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,OAAO,CAAC,SAAS,CAAY;gBAG3B,SAAS,EAAE,SAAS,EAGpB,WAAW,GAAE,MAAsC;IAgBrD;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EAGZ,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC;QACT,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IAcF;;;;;;;;;;OAUG;IACG,eAAe,CACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QACT,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAeF;;;;;;;;;;OAUG;IACG,mBAAmB,CACvB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QACT,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAeF;;;;;;;OAOG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAa/C;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAerC;;;;;OAKG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYxD,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;YAUP,YAAY;IAuC1B,OAAO,CAAC,cAAc;CAWvB;AAED,aAAK,iBAAiB;IACpB,IAAI,IAAA;IACJ,KAAK,IAAA;CACN;AAED,KAAK,SAAS,GAAG;IACf,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
|
package/lib/Solana.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -16,6 +7,7 @@ const errors_1 = require("@ledgerhq/errors");
|
|
|
16
7
|
const bip32_path_1 = __importDefault(require("bip32-path"));
|
|
17
8
|
const P1_NON_CONFIRM = 0x00;
|
|
18
9
|
const P1_CONFIRM = 0x01;
|
|
10
|
+
const P2_INIT = 0x00;
|
|
19
11
|
const P2_EXTEND = 0x01;
|
|
20
12
|
const P2_MORE = 0x02;
|
|
21
13
|
const MAX_PAYLOAD = 255;
|
|
@@ -25,6 +17,8 @@ const INS = {
|
|
|
25
17
|
GET_ADDR: 0x05,
|
|
26
18
|
SIGN: 0x06,
|
|
27
19
|
SIGN_OFFCHAIN: 0x07,
|
|
20
|
+
GET_CHALLENGE: 0x20,
|
|
21
|
+
PROVIDE_TRUSTED_NAME: 0x21,
|
|
28
22
|
};
|
|
29
23
|
var EXTRA_STATUS_CODES;
|
|
30
24
|
(function (EXTRA_STATUS_CODES) {
|
|
@@ -41,12 +35,19 @@ var EXTRA_STATUS_CODES;
|
|
|
41
35
|
* const solana = new Solana(transport);
|
|
42
36
|
*/
|
|
43
37
|
class Solana {
|
|
38
|
+
transport;
|
|
44
39
|
constructor(transport,
|
|
45
40
|
// the type annotation is needed for doc generator
|
|
46
41
|
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
|
47
42
|
scrambleKey = "solana_default_scramble_key") {
|
|
48
43
|
this.transport = transport;
|
|
49
|
-
this.transport.decorateAppAPIMethods(this, [
|
|
44
|
+
this.transport.decorateAppAPIMethods(this, [
|
|
45
|
+
"getAddress",
|
|
46
|
+
"signTransaction",
|
|
47
|
+
"getAppConfiguration",
|
|
48
|
+
"getChallenge",
|
|
49
|
+
"provideTrustedName",
|
|
50
|
+
], scrambleKey);
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
52
53
|
* Get Solana address (public key) for a BIP32 path.
|
|
@@ -61,17 +62,15 @@ class Solana {
|
|
|
61
62
|
* @example
|
|
62
63
|
* solana.getAddress("44'/501'/0'").then(r => r.address)
|
|
63
64
|
*/
|
|
64
|
-
getAddress(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
});
|
|
65
|
+
async getAddress(path,
|
|
66
|
+
// the type annotation is needed for doc generator
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
|
68
|
+
display = false) {
|
|
69
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
70
|
+
const addressBuffer = await this.sendToDevice(INS.GET_ADDR, display ? P1_CONFIRM : P1_NON_CONFIRM, pathBuffer);
|
|
71
|
+
return {
|
|
72
|
+
address: addressBuffer,
|
|
73
|
+
};
|
|
75
74
|
}
|
|
76
75
|
/**
|
|
77
76
|
* Sign a Solana transaction.
|
|
@@ -84,18 +83,16 @@ class Solana {
|
|
|
84
83
|
* @example
|
|
85
84
|
* solana.signTransaction("44'/501'/0'", txBuffer).then(r => r.signature)
|
|
86
85
|
*/
|
|
87
|
-
signTransaction(path, txBuffer) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
};
|
|
98
|
-
});
|
|
86
|
+
async signTransaction(path, txBuffer) {
|
|
87
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
88
|
+
// Ledger app supports only a single derivation path per call ATM
|
|
89
|
+
const pathsCountBuffer = Buffer.alloc(1);
|
|
90
|
+
pathsCountBuffer.writeUInt8(1, 0);
|
|
91
|
+
const payload = Buffer.concat([pathsCountBuffer, pathBuffer, txBuffer]);
|
|
92
|
+
const signatureBuffer = await this.sendToDevice(INS.SIGN, P1_CONFIRM, payload);
|
|
93
|
+
return {
|
|
94
|
+
signature: signatureBuffer,
|
|
95
|
+
};
|
|
99
96
|
}
|
|
100
97
|
/**
|
|
101
98
|
* Sign a Solana off-chain message.
|
|
@@ -108,18 +105,16 @@ class Solana {
|
|
|
108
105
|
* @example
|
|
109
106
|
* solana.signOffchainMessage("44'/501'/0'", msgBuffer).then(r => r.signature)
|
|
110
107
|
*/
|
|
111
|
-
signOffchainMessage(path, msgBuffer) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
};
|
|
122
|
-
});
|
|
108
|
+
async signOffchainMessage(path, msgBuffer) {
|
|
109
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
110
|
+
// Ledger app supports only a single derivation path per call ATM
|
|
111
|
+
const pathsCountBuffer = Buffer.alloc(1);
|
|
112
|
+
pathsCountBuffer.writeUInt8(1, 0);
|
|
113
|
+
const payload = Buffer.concat([pathsCountBuffer, pathBuffer, msgBuffer]);
|
|
114
|
+
const signatureBuffer = await this.sendToDevice(INS.SIGN_OFFCHAIN, P1_CONFIRM, payload);
|
|
115
|
+
return {
|
|
116
|
+
signature: signatureBuffer,
|
|
117
|
+
};
|
|
123
118
|
}
|
|
124
119
|
/**
|
|
125
120
|
* Get application configuration.
|
|
@@ -129,16 +124,40 @@ class Solana {
|
|
|
129
124
|
* @example
|
|
130
125
|
* solana.getAppConfiguration().then(r => r.version)
|
|
131
126
|
*/
|
|
132
|
-
getAppConfiguration() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
127
|
+
async getAppConfiguration() {
|
|
128
|
+
const [blindSigningEnabled, pubKeyDisplayMode, major, minor, patch] = await this.sendToDevice(INS.GET_VERSION, P1_NON_CONFIRM, Buffer.alloc(0));
|
|
129
|
+
return {
|
|
130
|
+
blindSigningEnabled: Boolean(blindSigningEnabled),
|
|
131
|
+
pubKeyDisplayMode,
|
|
132
|
+
version: `${major}.${minor}.${patch}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Method returning a 4 bytes TLV challenge as an hex string
|
|
137
|
+
*
|
|
138
|
+
* @returns {Promise<string>}
|
|
139
|
+
*/
|
|
140
|
+
async getChallenge() {
|
|
141
|
+
return this.transport.send(LEDGER_CLA, INS.GET_CHALLENGE, P1_NON_CONFIRM, P2_INIT).then(res => {
|
|
142
|
+
const data = res.toString("hex");
|
|
143
|
+
const fourBytesChallenge = data.slice(0, -4);
|
|
144
|
+
const statusCode = data.slice(-4);
|
|
145
|
+
if (statusCode !== "9000") {
|
|
146
|
+
throw new Error(`An error happened while generating the challenge. Status code: ${statusCode}`);
|
|
147
|
+
}
|
|
148
|
+
return `0x${fourBytesChallenge}`;
|
|
140
149
|
});
|
|
141
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Provides a trusted name to be displayed during transactions in place of the token address it is associated to. It shall be run just before a transaction involving the associated address that would be displayed on the device.
|
|
153
|
+
*
|
|
154
|
+
* @param data a stringified buffer of some TLV encoded data to represent the trusted name
|
|
155
|
+
* @returns a boolean
|
|
156
|
+
*/
|
|
157
|
+
async provideTrustedName(data) {
|
|
158
|
+
await this.transport.send(LEDGER_CLA, INS.PROVIDE_TRUSTED_NAME, P1_NON_CONFIRM, P2_INIT, Buffer.from(data, "hex"));
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
142
161
|
pathToBuffer(originalPath) {
|
|
143
162
|
const path = originalPath
|
|
144
163
|
.split("/")
|
|
@@ -156,33 +175,31 @@ class Solana {
|
|
|
156
175
|
return buf;
|
|
157
176
|
}
|
|
158
177
|
// send chunked if payload size exceeds maximum for a call
|
|
159
|
-
sendToDevice(instruction, p1, payload) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
p2 |= P2_EXTEND;
|
|
178
|
-
}
|
|
178
|
+
async sendToDevice(instruction, p1, payload) {
|
|
179
|
+
/*
|
|
180
|
+
* By default transport will throw if status code is not OK.
|
|
181
|
+
* For some payloads we need to enable blind sign in the app settings
|
|
182
|
+
* and this is reported with StatusCodes.MISSING_CRITICAL_PARAMETER first byte prefix
|
|
183
|
+
* so we handle it and show a user friendly error message.
|
|
184
|
+
*/
|
|
185
|
+
const acceptStatusList = [errors_1.StatusCodes.OK, EXTRA_STATUS_CODES.BLIND_SIGNATURE_REQUIRED];
|
|
186
|
+
let p2 = P2_INIT;
|
|
187
|
+
let payload_offset = 0;
|
|
188
|
+
if (payload.length > MAX_PAYLOAD) {
|
|
189
|
+
while (payload.length - payload_offset > MAX_PAYLOAD) {
|
|
190
|
+
const buf = payload.slice(payload_offset, payload_offset + MAX_PAYLOAD);
|
|
191
|
+
payload_offset += MAX_PAYLOAD;
|
|
192
|
+
// console.log( "send", (p2 | P2_MORE).toString(16), buf.length.toString(16), buf);
|
|
193
|
+
const reply = await this.transport.send(LEDGER_CLA, instruction, p1, p2 | P2_MORE, buf, acceptStatusList);
|
|
194
|
+
this.throwOnFailure(reply);
|
|
195
|
+
p2 |= P2_EXTEND;
|
|
179
196
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
197
|
+
}
|
|
198
|
+
const buf = payload.slice(payload_offset);
|
|
199
|
+
// console.log("send", p2.toString(16), buf.length.toString(16), buf);
|
|
200
|
+
const reply = await this.transport.send(LEDGER_CLA, instruction, p1, p2, buf, acceptStatusList);
|
|
201
|
+
this.throwOnFailure(reply);
|
|
202
|
+
return reply.slice(0, reply.length - 2);
|
|
186
203
|
}
|
|
187
204
|
throwOnFailure(reply) {
|
|
188
205
|
// transport makes sure reply has a valid length
|
package/lib/Solana.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Solana.js","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Solana.js","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":";;;;;AAEA,6CAA+C;AAE/C,4DAAiC;AAEjC,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,MAAM,SAAS,GAAG,IAAI,CAAC;AACvB,MAAM,OAAO,GAAG,IAAI,CAAC;AAErB,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,GAAG,GAAG;IACV,WAAW,EAAE,IAAI;IACjB,QAAQ,EAAE,IAAI;IACd,IAAI,EAAE,IAAI;IACV,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,IAAI;IACnB,oBAAoB,EAAE,IAAI;CAC3B,CAAC;AAEF,IAAK,kBAEJ;AAFD,WAAK,kBAAkB;IACrB,uGAAiC,CAAA;AACnC,CAAC,EAFI,kBAAkB,KAAlB,kBAAkB,QAEtB;AAED;;;;;;;;;GASG;AACH,MAAqB,MAAM;IACjB,SAAS,CAAY;IAE7B,YACE,SAAoB;IACpB,kDAAkD;IAClD,kEAAkE;IAClE,cAAsB,6BAA6B;QAEnD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAClC,IAAI,EACJ;YACE,YAAY;YACZ,iBAAiB;YACjB,qBAAqB;YACrB,cAAc;YACd,oBAAoB;SACrB,EACD,WAAW,CACZ,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,UAAU,CACd,IAAY;IACZ,kDAAkD;IAClD,kEAAkE;IAClE,UAAmB,KAAK;QAIxB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAE3C,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,YAAY,CAC3C,GAAG,CAAC,QAAQ,EACZ,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,EACrC,UAAU,CACX,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,aAAa;SACvB,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,eAAe,CACnB,IAAY,EACZ,QAAgB;QAIhB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC3C,iEAAiE;QACjE,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzC,gBAAgB,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAElC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QAExE,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QAE/E,OAAO;YACL,SAAS,EAAE,eAAe;SAC3B,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,mBAAmB,CACvB,IAAY,EACZ,SAAiB;QAIjB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC3C,iEAAiE;QACjE,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzC,gBAAgB,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAElC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;QAEzE,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QAExF,OAAO;YACL,SAAS,EAAE,eAAe;SAC3B,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,mBAAmB;QACvB,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,YAAY,CAC3F,GAAG,CAAC,WAAW,EACf,cAAc,EACd,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAChB,CAAC;QACF,OAAO;YACL,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,CAAC;YACjD,iBAAiB;YACjB,OAAO,EAAE,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE;SACtC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YAC5F,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAElC,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CACb,kEAAkE,UAAU,EAAE,CAC/E,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,kBAAkB,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,kBAAkB,CAAC,IAAY;QACnC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACvB,UAAU,EACV,GAAG,CAAC,oBAAoB,EACxB,cAAc,EACd,OAAO,EACP,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CACzB,CAAC;QAEF,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,YAAY,CAAC,YAAoB;QACvC,MAAM,IAAI,GAAG,YAAY;aACtB,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;aAChF,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,MAAM,QAAQ,GAAa,oBAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAEO,aAAa,CAAC,IAAc;QAClC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9C,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/B,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACtC,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,0DAA0D;IAClD,KAAK,CAAC,YAAY,CAAC,WAAmB,EAAE,EAAU,EAAE,OAAe;QACzE;;;;;WAKG;QACH,MAAM,gBAAgB,GAAG,CAAC,oBAAW,CAAC,EAAE,EAAE,kBAAkB,CAAC,wBAAwB,CAAC,CAAC;QAEvF,IAAI,EAAE,GAAG,OAAO,CAAC;QACjB,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;YACjC,OAAO,OAAO,CAAC,MAAM,GAAG,cAAc,GAAG,WAAW,EAAE,CAAC;gBACrD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,cAAc,GAAG,WAAW,CAAC,CAAC;gBACxE,cAAc,IAAI,WAAW,CAAC;gBAC9B,mFAAmF;gBACnF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACrC,UAAU,EACV,WAAW,EACX,EAAE,EACF,EAAE,GAAG,OAAO,EACZ,GAAG,EACH,gBAAgB,CACjB,CAAC;gBACF,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBAC3B,EAAE,IAAI,SAAS,CAAC;YAClB,CAAC;QACH,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC1C,sEAAsE;QACtE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAEhG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAE3B,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1C,CAAC;IAEO,cAAc,CAAC,KAAa;QAClC,gDAAgD;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEpD,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,kBAAkB,CAAC,wBAAwB;gBAC9C,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;YAClF;gBACE,OAAO;QACX,CAAC;IACH,CAAC;CACF;AAvPD,yBAuPC;AAED,IAAK,iBAGJ;AAHD,WAAK,iBAAiB;IACpB,yDAAI,CAAA;IACJ,2DAAK,CAAA;AACP,CAAC,EAHI,iBAAiB,KAAjB,iBAAiB,QAGrB"}
|
package/lib-es/Solana.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import Transport from "@ledgerhq/hw-transport";
|
|
3
4
|
/**
|
|
4
5
|
* Solana API
|
|
@@ -66,6 +67,19 @@ export default class Solana {
|
|
|
66
67
|
* solana.getAppConfiguration().then(r => r.version)
|
|
67
68
|
*/
|
|
68
69
|
getAppConfiguration(): Promise<AppConfig>;
|
|
70
|
+
/**
|
|
71
|
+
* Method returning a 4 bytes TLV challenge as an hex string
|
|
72
|
+
*
|
|
73
|
+
* @returns {Promise<string>}
|
|
74
|
+
*/
|
|
75
|
+
getChallenge(): Promise<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Provides a trusted name to be displayed during transactions in place of the token address it is associated to. It shall be run just before a transaction involving the associated address that would be displayed on the device.
|
|
78
|
+
*
|
|
79
|
+
* @param data a stringified buffer of some TLV encoded data to represent the trusted name
|
|
80
|
+
* @returns a boolean
|
|
81
|
+
*/
|
|
82
|
+
provideTrustedName(data: string): Promise<boolean>;
|
|
69
83
|
private pathToBuffer;
|
|
70
84
|
private serializePath;
|
|
71
85
|
private sendToDevice;
|
package/lib-es/Solana.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Solana.d.ts","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Solana.d.ts","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":";;AAAA,OAAO,SAAS,MAAM,wBAAwB,CAAC;AA8B/C;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,OAAO,CAAC,SAAS,CAAY;gBAG3B,SAAS,EAAE,SAAS,EAGpB,WAAW,GAAE,MAAsC;IAgBrD;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EAGZ,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC;QACT,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IAcF;;;;;;;;;;OAUG;IACG,eAAe,CACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QACT,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAeF;;;;;;;;;;OAUG;IACG,mBAAmB,CACvB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QACT,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAeF;;;;;;;OAOG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAa/C;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAerC;;;;;OAKG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYxD,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;YAUP,YAAY;IAuC1B,OAAO,CAAC,cAAc;CAWvB;AAED,aAAK,iBAAiB;IACpB,IAAI,IAAA;IACJ,KAAK,IAAA;CACN;AAED,KAAK,SAAS,GAAG;IACf,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
|
package/lib-es/Solana.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { StatusCodes } from "@ledgerhq/errors";
|
|
11
2
|
import BIPPath from "bip32-path";
|
|
12
3
|
const P1_NON_CONFIRM = 0x00;
|
|
13
4
|
const P1_CONFIRM = 0x01;
|
|
5
|
+
const P2_INIT = 0x00;
|
|
14
6
|
const P2_EXTEND = 0x01;
|
|
15
7
|
const P2_MORE = 0x02;
|
|
16
8
|
const MAX_PAYLOAD = 255;
|
|
@@ -20,6 +12,8 @@ const INS = {
|
|
|
20
12
|
GET_ADDR: 0x05,
|
|
21
13
|
SIGN: 0x06,
|
|
22
14
|
SIGN_OFFCHAIN: 0x07,
|
|
15
|
+
GET_CHALLENGE: 0x20,
|
|
16
|
+
PROVIDE_TRUSTED_NAME: 0x21,
|
|
23
17
|
};
|
|
24
18
|
var EXTRA_STATUS_CODES;
|
|
25
19
|
(function (EXTRA_STATUS_CODES) {
|
|
@@ -36,12 +30,19 @@ var EXTRA_STATUS_CODES;
|
|
|
36
30
|
* const solana = new Solana(transport);
|
|
37
31
|
*/
|
|
38
32
|
export default class Solana {
|
|
33
|
+
transport;
|
|
39
34
|
constructor(transport,
|
|
40
35
|
// the type annotation is needed for doc generator
|
|
41
36
|
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
|
42
37
|
scrambleKey = "solana_default_scramble_key") {
|
|
43
38
|
this.transport = transport;
|
|
44
|
-
this.transport.decorateAppAPIMethods(this, [
|
|
39
|
+
this.transport.decorateAppAPIMethods(this, [
|
|
40
|
+
"getAddress",
|
|
41
|
+
"signTransaction",
|
|
42
|
+
"getAppConfiguration",
|
|
43
|
+
"getChallenge",
|
|
44
|
+
"provideTrustedName",
|
|
45
|
+
], scrambleKey);
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
48
|
* Get Solana address (public key) for a BIP32 path.
|
|
@@ -56,17 +57,15 @@ export default class Solana {
|
|
|
56
57
|
* @example
|
|
57
58
|
* solana.getAddress("44'/501'/0'").then(r => r.address)
|
|
58
59
|
*/
|
|
59
|
-
getAddress(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
});
|
|
60
|
+
async getAddress(path,
|
|
61
|
+
// the type annotation is needed for doc generator
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
|
63
|
+
display = false) {
|
|
64
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
65
|
+
const addressBuffer = await this.sendToDevice(INS.GET_ADDR, display ? P1_CONFIRM : P1_NON_CONFIRM, pathBuffer);
|
|
66
|
+
return {
|
|
67
|
+
address: addressBuffer,
|
|
68
|
+
};
|
|
70
69
|
}
|
|
71
70
|
/**
|
|
72
71
|
* Sign a Solana transaction.
|
|
@@ -79,18 +78,16 @@ export default class Solana {
|
|
|
79
78
|
* @example
|
|
80
79
|
* solana.signTransaction("44'/501'/0'", txBuffer).then(r => r.signature)
|
|
81
80
|
*/
|
|
82
|
-
signTransaction(path, txBuffer) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
});
|
|
81
|
+
async signTransaction(path, txBuffer) {
|
|
82
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
83
|
+
// Ledger app supports only a single derivation path per call ATM
|
|
84
|
+
const pathsCountBuffer = Buffer.alloc(1);
|
|
85
|
+
pathsCountBuffer.writeUInt8(1, 0);
|
|
86
|
+
const payload = Buffer.concat([pathsCountBuffer, pathBuffer, txBuffer]);
|
|
87
|
+
const signatureBuffer = await this.sendToDevice(INS.SIGN, P1_CONFIRM, payload);
|
|
88
|
+
return {
|
|
89
|
+
signature: signatureBuffer,
|
|
90
|
+
};
|
|
94
91
|
}
|
|
95
92
|
/**
|
|
96
93
|
* Sign a Solana off-chain message.
|
|
@@ -103,18 +100,16 @@ export default class Solana {
|
|
|
103
100
|
* @example
|
|
104
101
|
* solana.signOffchainMessage("44'/501'/0'", msgBuffer).then(r => r.signature)
|
|
105
102
|
*/
|
|
106
|
-
signOffchainMessage(path, msgBuffer) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
117
|
-
});
|
|
103
|
+
async signOffchainMessage(path, msgBuffer) {
|
|
104
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
105
|
+
// Ledger app supports only a single derivation path per call ATM
|
|
106
|
+
const pathsCountBuffer = Buffer.alloc(1);
|
|
107
|
+
pathsCountBuffer.writeUInt8(1, 0);
|
|
108
|
+
const payload = Buffer.concat([pathsCountBuffer, pathBuffer, msgBuffer]);
|
|
109
|
+
const signatureBuffer = await this.sendToDevice(INS.SIGN_OFFCHAIN, P1_CONFIRM, payload);
|
|
110
|
+
return {
|
|
111
|
+
signature: signatureBuffer,
|
|
112
|
+
};
|
|
118
113
|
}
|
|
119
114
|
/**
|
|
120
115
|
* Get application configuration.
|
|
@@ -124,16 +119,40 @@ export default class Solana {
|
|
|
124
119
|
* @example
|
|
125
120
|
* solana.getAppConfiguration().then(r => r.version)
|
|
126
121
|
*/
|
|
127
|
-
getAppConfiguration() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
122
|
+
async getAppConfiguration() {
|
|
123
|
+
const [blindSigningEnabled, pubKeyDisplayMode, major, minor, patch] = await this.sendToDevice(INS.GET_VERSION, P1_NON_CONFIRM, Buffer.alloc(0));
|
|
124
|
+
return {
|
|
125
|
+
blindSigningEnabled: Boolean(blindSigningEnabled),
|
|
126
|
+
pubKeyDisplayMode,
|
|
127
|
+
version: `${major}.${minor}.${patch}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Method returning a 4 bytes TLV challenge as an hex string
|
|
132
|
+
*
|
|
133
|
+
* @returns {Promise<string>}
|
|
134
|
+
*/
|
|
135
|
+
async getChallenge() {
|
|
136
|
+
return this.transport.send(LEDGER_CLA, INS.GET_CHALLENGE, P1_NON_CONFIRM, P2_INIT).then(res => {
|
|
137
|
+
const data = res.toString("hex");
|
|
138
|
+
const fourBytesChallenge = data.slice(0, -4);
|
|
139
|
+
const statusCode = data.slice(-4);
|
|
140
|
+
if (statusCode !== "9000") {
|
|
141
|
+
throw new Error(`An error happened while generating the challenge. Status code: ${statusCode}`);
|
|
142
|
+
}
|
|
143
|
+
return `0x${fourBytesChallenge}`;
|
|
135
144
|
});
|
|
136
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Provides a trusted name to be displayed during transactions in place of the token address it is associated to. It shall be run just before a transaction involving the associated address that would be displayed on the device.
|
|
148
|
+
*
|
|
149
|
+
* @param data a stringified buffer of some TLV encoded data to represent the trusted name
|
|
150
|
+
* @returns a boolean
|
|
151
|
+
*/
|
|
152
|
+
async provideTrustedName(data) {
|
|
153
|
+
await this.transport.send(LEDGER_CLA, INS.PROVIDE_TRUSTED_NAME, P1_NON_CONFIRM, P2_INIT, Buffer.from(data, "hex"));
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
137
156
|
pathToBuffer(originalPath) {
|
|
138
157
|
const path = originalPath
|
|
139
158
|
.split("/")
|
|
@@ -151,33 +170,31 @@ export default class Solana {
|
|
|
151
170
|
return buf;
|
|
152
171
|
}
|
|
153
172
|
// send chunked if payload size exceeds maximum for a call
|
|
154
|
-
sendToDevice(instruction, p1, payload) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
p2 |= P2_EXTEND;
|
|
173
|
-
}
|
|
173
|
+
async sendToDevice(instruction, p1, payload) {
|
|
174
|
+
/*
|
|
175
|
+
* By default transport will throw if status code is not OK.
|
|
176
|
+
* For some payloads we need to enable blind sign in the app settings
|
|
177
|
+
* and this is reported with StatusCodes.MISSING_CRITICAL_PARAMETER first byte prefix
|
|
178
|
+
* so we handle it and show a user friendly error message.
|
|
179
|
+
*/
|
|
180
|
+
const acceptStatusList = [StatusCodes.OK, EXTRA_STATUS_CODES.BLIND_SIGNATURE_REQUIRED];
|
|
181
|
+
let p2 = P2_INIT;
|
|
182
|
+
let payload_offset = 0;
|
|
183
|
+
if (payload.length > MAX_PAYLOAD) {
|
|
184
|
+
while (payload.length - payload_offset > MAX_PAYLOAD) {
|
|
185
|
+
const buf = payload.slice(payload_offset, payload_offset + MAX_PAYLOAD);
|
|
186
|
+
payload_offset += MAX_PAYLOAD;
|
|
187
|
+
// console.log( "send", (p2 | P2_MORE).toString(16), buf.length.toString(16), buf);
|
|
188
|
+
const reply = await this.transport.send(LEDGER_CLA, instruction, p1, p2 | P2_MORE, buf, acceptStatusList);
|
|
189
|
+
this.throwOnFailure(reply);
|
|
190
|
+
p2 |= P2_EXTEND;
|
|
174
191
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
192
|
+
}
|
|
193
|
+
const buf = payload.slice(payload_offset);
|
|
194
|
+
// console.log("send", p2.toString(16), buf.length.toString(16), buf);
|
|
195
|
+
const reply = await this.transport.send(LEDGER_CLA, instruction, p1, p2, buf, acceptStatusList);
|
|
196
|
+
this.throwOnFailure(reply);
|
|
197
|
+
return reply.slice(0, reply.length - 2);
|
|
181
198
|
}
|
|
182
199
|
throwOnFailure(reply) {
|
|
183
200
|
// transport makes sure reply has a valid length
|
package/lib-es/Solana.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Solana.js","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Solana.js","sourceRoot":"","sources":["../src/Solana.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/hw-app-solana",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0-nightly.0",
|
|
4
4
|
"description": "Ledger Hardware Wallet Solana Application API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -33,22 +33,24 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/jest": "^29.5.10",
|
|
36
|
-
"@types/node": "^
|
|
37
|
-
"axios": "1.7.7",
|
|
36
|
+
"@types/node": "^22.10.10",
|
|
38
37
|
"documentation": "14.0.2",
|
|
39
38
|
"jest": "^29.7.0",
|
|
40
39
|
"rimraf": "^4.4.1",
|
|
41
40
|
"source-map-support": "^0.5.21",
|
|
42
41
|
"ts-jest": "^29.1.1",
|
|
43
42
|
"ts-node": "^10.4.0",
|
|
43
|
+
"jest-sonar": "0.2.16",
|
|
44
44
|
"@ledgerhq/hw-transport-mocker": "^6.29.4",
|
|
45
45
|
"@ledgerhq/hw-transport-node-speculos": "^6.29.4"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"clean": "rimraf lib lib-es",
|
|
49
|
-
"build": "tsc && tsc -m
|
|
49
|
+
"build": "tsc && tsc -m esnext --moduleResolution bundler --outDir lib-es",
|
|
50
|
+
"coverage": "jest --coverage --passWithNoTests && mv coverage/coverage-final.json coverage/coverage-hw-app-solana.json && mv coverage/lcov.info coverage/hw-app-solana-lcov.info",
|
|
50
51
|
"prewatch": "pnpm build",
|
|
51
52
|
"watch": "tsc --watch",
|
|
53
|
+
"watch:es": "tsc --watch -m esnext --moduleResolution bundler --outDir lib-es",
|
|
52
54
|
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
|
|
53
55
|
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
|
|
54
56
|
"lint:fix": "pnpm lint --fix",
|
package/src/Solana.ts
CHANGED
|
@@ -7,6 +7,7 @@ import BIPPath from "bip32-path";
|
|
|
7
7
|
const P1_NON_CONFIRM = 0x00;
|
|
8
8
|
const P1_CONFIRM = 0x01;
|
|
9
9
|
|
|
10
|
+
const P2_INIT = 0x00;
|
|
10
11
|
const P2_EXTEND = 0x01;
|
|
11
12
|
const P2_MORE = 0x02;
|
|
12
13
|
|
|
@@ -19,6 +20,8 @@ const INS = {
|
|
|
19
20
|
GET_ADDR: 0x05,
|
|
20
21
|
SIGN: 0x06,
|
|
21
22
|
SIGN_OFFCHAIN: 0x07,
|
|
23
|
+
GET_CHALLENGE: 0x20,
|
|
24
|
+
PROVIDE_TRUSTED_NAME: 0x21,
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
enum EXTRA_STATUS_CODES {
|
|
@@ -47,7 +50,13 @@ export default class Solana {
|
|
|
47
50
|
this.transport = transport;
|
|
48
51
|
this.transport.decorateAppAPIMethods(
|
|
49
52
|
this,
|
|
50
|
-
[
|
|
53
|
+
[
|
|
54
|
+
"getAddress",
|
|
55
|
+
"signTransaction",
|
|
56
|
+
"getAppConfiguration",
|
|
57
|
+
"getChallenge",
|
|
58
|
+
"provideTrustedName",
|
|
59
|
+
],
|
|
51
60
|
scrambleKey,
|
|
52
61
|
);
|
|
53
62
|
}
|
|
@@ -169,6 +178,44 @@ export default class Solana {
|
|
|
169
178
|
};
|
|
170
179
|
}
|
|
171
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Method returning a 4 bytes TLV challenge as an hex string
|
|
183
|
+
*
|
|
184
|
+
* @returns {Promise<string>}
|
|
185
|
+
*/
|
|
186
|
+
async getChallenge(): Promise<string> {
|
|
187
|
+
return this.transport.send(LEDGER_CLA, INS.GET_CHALLENGE, P1_NON_CONFIRM, P2_INIT).then(res => {
|
|
188
|
+
const data = res.toString("hex");
|
|
189
|
+
const fourBytesChallenge = data.slice(0, -4);
|
|
190
|
+
const statusCode = data.slice(-4);
|
|
191
|
+
|
|
192
|
+
if (statusCode !== "9000") {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`An error happened while generating the challenge. Status code: ${statusCode}`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return `0x${fourBytesChallenge}`;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Provides a trusted name to be displayed during transactions in place of the token address it is associated to. It shall be run just before a transaction involving the associated address that would be displayed on the device.
|
|
203
|
+
*
|
|
204
|
+
* @param data a stringified buffer of some TLV encoded data to represent the trusted name
|
|
205
|
+
* @returns a boolean
|
|
206
|
+
*/
|
|
207
|
+
async provideTrustedName(data: string): Promise<boolean> {
|
|
208
|
+
await this.transport.send(
|
|
209
|
+
LEDGER_CLA,
|
|
210
|
+
INS.PROVIDE_TRUSTED_NAME,
|
|
211
|
+
P1_NON_CONFIRM,
|
|
212
|
+
P2_INIT,
|
|
213
|
+
Buffer.from(data, "hex"),
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
|
|
172
219
|
private pathToBuffer(originalPath: string) {
|
|
173
220
|
const path = originalPath
|
|
174
221
|
.split("/")
|
|
@@ -191,13 +238,13 @@ export default class Solana {
|
|
|
191
238
|
private async sendToDevice(instruction: number, p1: number, payload: Buffer) {
|
|
192
239
|
/*
|
|
193
240
|
* By default transport will throw if status code is not OK.
|
|
194
|
-
* For some
|
|
241
|
+
* For some payloads we need to enable blind sign in the app settings
|
|
195
242
|
* and this is reported with StatusCodes.MISSING_CRITICAL_PARAMETER first byte prefix
|
|
196
243
|
* so we handle it and show a user friendly error message.
|
|
197
244
|
*/
|
|
198
245
|
const acceptStatusList = [StatusCodes.OK, EXTRA_STATUS_CODES.BLIND_SIGNATURE_REQUIRED];
|
|
199
246
|
|
|
200
|
-
let p2 =
|
|
247
|
+
let p2 = P2_INIT;
|
|
201
248
|
let payload_offset = 0;
|
|
202
249
|
|
|
203
250
|
if (payload.length > MAX_PAYLOAD) {
|