@ton-community/ton-ledger 4.0.0 → 4.1.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/CHANGELOG.md +12 -0
- package/dist/TonTransport.d.ts +17 -0
- package/dist/TonTransport.js +115 -68
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [4.1.0] - 2023-06-16
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added `signData` method along with `SignDataRequest` type
|
|
12
|
+
|
|
13
|
+
## [4.0.1] - 2023-06-16
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Fixed the address flags communication
|
|
18
|
+
|
|
7
19
|
## [4.0.0] - 2023-06-09
|
|
8
20
|
|
|
9
21
|
### Added
|
package/dist/TonTransport.d.ts
CHANGED
|
@@ -27,6 +27,16 @@ export type TonPayloadFormat = {
|
|
|
27
27
|
forwardAmount: bigint;
|
|
28
28
|
forwardPayload: Cell | null;
|
|
29
29
|
};
|
|
30
|
+
export type SignDataRequest = {
|
|
31
|
+
type: 'plaintext';
|
|
32
|
+
text: string;
|
|
33
|
+
} | {
|
|
34
|
+
type: 'app-data';
|
|
35
|
+
address?: Address;
|
|
36
|
+
domain?: string;
|
|
37
|
+
data: Cell;
|
|
38
|
+
ext?: Cell;
|
|
39
|
+
};
|
|
30
40
|
export declare class TonTransport {
|
|
31
41
|
#private;
|
|
32
42
|
readonly transport: Transport;
|
|
@@ -61,6 +71,13 @@ export declare class TonTransport {
|
|
|
61
71
|
signature: Buffer;
|
|
62
72
|
hash: Buffer;
|
|
63
73
|
}>;
|
|
74
|
+
signData(path: number[], req: SignDataRequest, opts?: {
|
|
75
|
+
timestamp?: number;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
signature: Buffer;
|
|
78
|
+
cell: Cell;
|
|
79
|
+
timestamp: number;
|
|
80
|
+
}>;
|
|
64
81
|
signTransaction: (path: number[], transaction: {
|
|
65
82
|
to: Address;
|
|
66
83
|
sendMode: SendMode;
|
package/dist/TonTransport.js
CHANGED
|
@@ -10,7 +10,9 @@ const LEDGER_SYSTEM = 0xB0;
|
|
|
10
10
|
const LEDGER_CLA = 0xe0;
|
|
11
11
|
const INS_VERSION = 0x03;
|
|
12
12
|
const INS_ADDRESS = 0x05;
|
|
13
|
+
const INS_SIGN_TX = 0x06;
|
|
13
14
|
const INS_PROOF = 0x08;
|
|
15
|
+
const INS_SIGN_DATA = 0x09;
|
|
14
16
|
function chunks(buf, n) {
|
|
15
17
|
const nc = Math.ceil(buf.length / n);
|
|
16
18
|
const cs = [];
|
|
@@ -19,6 +21,19 @@ function chunks(buf, n) {
|
|
|
19
21
|
}
|
|
20
22
|
return cs;
|
|
21
23
|
}
|
|
24
|
+
function processAddressFlags(opts) {
|
|
25
|
+
const bounceable = opts?.bounceable ?? true;
|
|
26
|
+
const testOnly = opts?.testOnly ?? false;
|
|
27
|
+
const chain = opts?.chain ?? 0;
|
|
28
|
+
let flags = 0x00;
|
|
29
|
+
if (testOnly) {
|
|
30
|
+
flags |= 0x01;
|
|
31
|
+
}
|
|
32
|
+
if (chain === -1) {
|
|
33
|
+
flags |= 0x02;
|
|
34
|
+
}
|
|
35
|
+
return { bounceable, testOnly, chain, flags };
|
|
36
|
+
}
|
|
22
37
|
class TonTransport {
|
|
23
38
|
transport;
|
|
24
39
|
#lock = new teslabot_1.AsyncLock();
|
|
@@ -57,27 +72,7 @@ class TonTransport {
|
|
|
57
72
|
// Check path
|
|
58
73
|
validatePath(path);
|
|
59
74
|
// Resolve flags
|
|
60
|
-
|
|
61
|
-
let chain = 0;
|
|
62
|
-
let test = false;
|
|
63
|
-
let flags = 0x00;
|
|
64
|
-
if (opts && opts.bounceable !== undefined && !opts.bounceable) {
|
|
65
|
-
flags |= 0x01;
|
|
66
|
-
bounceable = false;
|
|
67
|
-
}
|
|
68
|
-
if (opts && opts.testOnly) {
|
|
69
|
-
flags |= 0x02;
|
|
70
|
-
test = true;
|
|
71
|
-
}
|
|
72
|
-
if (opts && opts.chain !== undefined) {
|
|
73
|
-
if (opts.chain !== 0 && opts.chain !== -1) {
|
|
74
|
-
throw Error('Invalid chain');
|
|
75
|
-
}
|
|
76
|
-
chain = opts.chain;
|
|
77
|
-
if (opts.chain === -1) {
|
|
78
|
-
flags |= 0x04;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
75
|
+
const { bounceable, testOnly, chain } = processAddressFlags(opts);
|
|
81
76
|
// Get public key
|
|
82
77
|
let response = await this.#doRequest(INS_ADDRESS, 0x00, 0x00, pathElementsToBuffer(path.map((v) => v + 0x80000000)));
|
|
83
78
|
if (response.length !== 32) {
|
|
@@ -86,33 +81,13 @@ class TonTransport {
|
|
|
86
81
|
// Contract
|
|
87
82
|
const contract = (0, getInit_1.getInit)(chain, response);
|
|
88
83
|
const address = (0, ton_core_1.contractAddress)(chain, contract);
|
|
89
|
-
return { address: address.toString({ bounceable
|
|
84
|
+
return { address: address.toString({ bounceable, testOnly }), publicKey: response };
|
|
90
85
|
}
|
|
91
86
|
async validateAddress(path, opts) {
|
|
92
87
|
// Check path
|
|
93
88
|
validatePath(path);
|
|
94
89
|
// Resolve flags
|
|
95
|
-
|
|
96
|
-
let chain = 0;
|
|
97
|
-
let test = false;
|
|
98
|
-
let flags = 0x00;
|
|
99
|
-
if (opts && opts.bounceable !== undefined && !opts.bounceable) {
|
|
100
|
-
flags |= 0x01;
|
|
101
|
-
bounceable = false;
|
|
102
|
-
}
|
|
103
|
-
if (opts && opts.testOnly) {
|
|
104
|
-
flags |= 0x02;
|
|
105
|
-
test = true;
|
|
106
|
-
}
|
|
107
|
-
if (opts && opts.chain !== undefined) {
|
|
108
|
-
if (opts.chain !== 0 && opts.chain !== -1) {
|
|
109
|
-
throw Error('Invalid chain');
|
|
110
|
-
}
|
|
111
|
-
chain = opts.chain;
|
|
112
|
-
if (opts.chain === -1) {
|
|
113
|
-
flags |= 0x04;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
90
|
+
const { bounceable, testOnly, chain, flags } = processAddressFlags(opts);
|
|
116
91
|
// Get public key
|
|
117
92
|
let response = await this.#doRequest(INS_ADDRESS, 0x01, flags, pathElementsToBuffer(path.map((v) => v + 0x80000000)));
|
|
118
93
|
if (response.length !== 32) {
|
|
@@ -121,34 +96,14 @@ class TonTransport {
|
|
|
121
96
|
// Contract
|
|
122
97
|
const contract = (0, getInit_1.getInit)(chain, response);
|
|
123
98
|
const address = (0, ton_core_1.contractAddress)(chain, contract);
|
|
124
|
-
return { address: address.toString({ bounceable
|
|
99
|
+
return { address: address.toString({ bounceable, testOnly }), publicKey: response };
|
|
125
100
|
}
|
|
126
101
|
async getAddressProof(path, params, opts) {
|
|
127
102
|
// Check path
|
|
128
103
|
validatePath(path);
|
|
129
104
|
let publicKey = (await this.getAddress(path)).publicKey;
|
|
130
105
|
// Resolve flags
|
|
131
|
-
|
|
132
|
-
let chain = 0;
|
|
133
|
-
let test = false;
|
|
134
|
-
let flags = 0x00;
|
|
135
|
-
if (opts && opts.bounceable !== undefined && !opts.bounceable) {
|
|
136
|
-
flags |= 0x01;
|
|
137
|
-
bounceable = false;
|
|
138
|
-
}
|
|
139
|
-
if (opts && opts.testOnly) {
|
|
140
|
-
flags |= 0x02;
|
|
141
|
-
test = true;
|
|
142
|
-
}
|
|
143
|
-
if (opts && opts.chain !== undefined) {
|
|
144
|
-
if (opts.chain !== 0 && opts.chain !== -1) {
|
|
145
|
-
throw Error('Invalid chain');
|
|
146
|
-
}
|
|
147
|
-
chain = opts.chain;
|
|
148
|
-
if (opts.chain === -1) {
|
|
149
|
-
flags |= 0x04;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
106
|
+
const { flags } = processAddressFlags(opts);
|
|
152
107
|
const domainBuf = Buffer.from(params.domain, 'utf-8');
|
|
153
108
|
const reqBuf = Buffer.concat([
|
|
154
109
|
pathElementsToBuffer(path.map((v) => v + 0x80000000)),
|
|
@@ -166,6 +121,98 @@ class TonTransport {
|
|
|
166
121
|
}
|
|
167
122
|
return { signature, hash };
|
|
168
123
|
}
|
|
124
|
+
async signData(path, req, opts) {
|
|
125
|
+
validatePath(path);
|
|
126
|
+
const publicKey = (await this.getAddress(path)).publicKey;
|
|
127
|
+
const timestamp = opts?.timestamp ?? Math.floor(Date.now() / 1000);
|
|
128
|
+
let schema;
|
|
129
|
+
let data;
|
|
130
|
+
let cell;
|
|
131
|
+
switch (req.type) {
|
|
132
|
+
case 'plaintext': {
|
|
133
|
+
schema = 0x754bf91b;
|
|
134
|
+
data = Buffer.from(req.text, 'ascii');
|
|
135
|
+
cell = (0, ton_core_1.beginCell)().storeStringTail(req.text).endCell();
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case 'app-data': {
|
|
139
|
+
if (req.address === undefined && req.domain === undefined) {
|
|
140
|
+
throw new Error('At least one of `address` and `domain` must be set when using \'app-data\' request');
|
|
141
|
+
}
|
|
142
|
+
schema = 0x54b58535;
|
|
143
|
+
let b = (0, ton_core_1.beginCell)();
|
|
144
|
+
let dp = [];
|
|
145
|
+
if (req.address !== undefined) {
|
|
146
|
+
b.storeBit(1);
|
|
147
|
+
b.storeAddress(req.address);
|
|
148
|
+
dp.push((0, ledgerWriter_1.writeUint8)(1), (0, ledgerWriter_1.writeAddress)(req.address));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
b.storeBit(0);
|
|
152
|
+
dp.push((0, ledgerWriter_1.writeUint8)(0));
|
|
153
|
+
}
|
|
154
|
+
if (req.domain !== undefined) {
|
|
155
|
+
b.storeBit(1);
|
|
156
|
+
let inner = (0, ton_core_1.beginCell)();
|
|
157
|
+
req.domain.split('.').reverse().forEach(p => {
|
|
158
|
+
inner.storeBuffer(Buffer.from(p, 'ascii'));
|
|
159
|
+
inner.storeUint(0, 8);
|
|
160
|
+
});
|
|
161
|
+
b.storeRef(inner);
|
|
162
|
+
const db = Buffer.from(req.domain, 'ascii');
|
|
163
|
+
dp.push((0, ledgerWriter_1.writeUint8)(1), (0, ledgerWriter_1.writeUint8)(db.length), db);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
b.storeBit(0);
|
|
167
|
+
dp.push((0, ledgerWriter_1.writeUint8)(0));
|
|
168
|
+
}
|
|
169
|
+
b.storeRef(req.data);
|
|
170
|
+
dp.push((0, ledgerWriter_1.writeCellRef)(req.data));
|
|
171
|
+
if (req.ext !== undefined) {
|
|
172
|
+
b.storeBit(1);
|
|
173
|
+
b.storeRef(req.ext);
|
|
174
|
+
dp.push((0, ledgerWriter_1.writeUint8)(1), (0, ledgerWriter_1.writeCellRef)(req.ext));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
b.storeBit(0);
|
|
178
|
+
dp.push((0, ledgerWriter_1.writeUint8)(0));
|
|
179
|
+
}
|
|
180
|
+
data = Buffer.concat(dp);
|
|
181
|
+
cell = b.endCell();
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
default: {
|
|
185
|
+
throw new Error(`Sign data request type '${req.type}' not supported`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const commonPart = Buffer.concat([
|
|
189
|
+
(0, ledgerWriter_1.writeUint32)(schema),
|
|
190
|
+
(0, ledgerWriter_1.writeUint64)(BigInt(timestamp)),
|
|
191
|
+
]);
|
|
192
|
+
const pkg = Buffer.concat([
|
|
193
|
+
commonPart,
|
|
194
|
+
data,
|
|
195
|
+
]);
|
|
196
|
+
await this.#doRequest(INS_SIGN_DATA, 0x00, 0x03, pathElementsToBuffer(path.map((v) => v + 0x80000000)));
|
|
197
|
+
const pkgCs = chunks(pkg, 255);
|
|
198
|
+
for (let i = 0; i < pkgCs.length - 1; i++) {
|
|
199
|
+
await this.#doRequest(INS_SIGN_DATA, 0x00, 0x02, pkgCs[i]);
|
|
200
|
+
}
|
|
201
|
+
const res = await this.#doRequest(INS_SIGN_DATA, 0x00, 0x00, pkgCs[pkgCs.length - 1]);
|
|
202
|
+
let signature = res.subarray(1, 1 + 64);
|
|
203
|
+
let hash = res.subarray(2 + 64, 2 + 64 + 32);
|
|
204
|
+
if (!hash.equals(cell.hash())) {
|
|
205
|
+
throw Error('Hash mismatch. Expected: ' + cell.hash().toString('hex') + ', got: ' + hash.toString('hex'));
|
|
206
|
+
}
|
|
207
|
+
if (!(0, ton_crypto_1.signVerify)(Buffer.concat([commonPart, hash]), signature, publicKey)) {
|
|
208
|
+
throw Error('Received signature is invalid');
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
signature,
|
|
212
|
+
cell,
|
|
213
|
+
timestamp,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
169
216
|
signTransaction = async (path, transaction) => {
|
|
170
217
|
// Check path
|
|
171
218
|
validatePath(path);
|
|
@@ -304,12 +351,12 @@ class TonTransport {
|
|
|
304
351
|
//
|
|
305
352
|
// Send package
|
|
306
353
|
//
|
|
307
|
-
await this.#doRequest(
|
|
354
|
+
await this.#doRequest(INS_SIGN_TX, 0x00, 0x03, pathElementsToBuffer(path.map((v) => v + 0x80000000)));
|
|
308
355
|
const pkgCs = chunks(pkg, 255);
|
|
309
356
|
for (let i = 0; i < pkgCs.length - 1; i++) {
|
|
310
|
-
await this.#doRequest(
|
|
357
|
+
await this.#doRequest(INS_SIGN_TX, 0x00, 0x02, pkgCs[i]);
|
|
311
358
|
}
|
|
312
|
-
let res = await this.#doRequest(
|
|
359
|
+
let res = await this.#doRequest(INS_SIGN_TX, 0x00, 0x00, pkgCs[pkgCs.length - 1]);
|
|
313
360
|
//
|
|
314
361
|
// Parse response
|
|
315
362
|
//
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { TonPayloadFormat, TonTransport } from './TonTransport';
|
|
1
|
+
export { TonPayloadFormat, TonTransport, SignDataRequest } from './TonTransport';
|