@ledgerhq/hw-app-aptos 6.30.0-next.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 +4 -0
- package/.unimportedrc.json +4 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE.txt +21 -0
- package/README.md +115 -0
- package/jest.config.ts +6 -0
- package/lib/Aptos.d.ts +95 -0
- package/lib/Aptos.d.ts.map +1 -0
- package/lib/Aptos.js +189 -0
- package/lib/Aptos.js.map +1 -0
- package/lib-es/Aptos.d.ts +95 -0
- package/lib-es/Aptos.d.ts.map +1 -0
- package/lib-es/Aptos.js +183 -0
- package/lib-es/Aptos.js.map +1 -0
- package/package.json +59 -0
- package/src/Aptos.ts +220 -0
- package/tests/aptos.test.ts +66 -0
- package/tsconfig.json +7 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @ledgerhq/hw-app-aptos
|
|
2
|
+
|
|
3
|
+
## 6.30.0-next.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#8396](https://github.com/LedgerHQ/ledger-live/pull/8396) [`d98a964`](https://github.com/LedgerHQ/ledger-live/commit/d98a96476c3d44eab1575f6c7c58ec03b5daf890) Thanks [@hedi-edelbloute](https://github.com/hedi-edelbloute)! - Support for Aptos blockchain
|
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017-present Ledger https://www.ledger.com/
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<img src="https://user-images.githubusercontent.com/4631227/191834116-59cf590e-25cc-4956-ae5c-812ea464f324.png" height="100" />
|
|
2
|
+
|
|
3
|
+
[GitHub](https://github.com/LedgerHQ/ledger-live/),
|
|
4
|
+
[Ledger Devs Discord](https://developers.ledger.com/discord-pro),
|
|
5
|
+
[Developer Portal](https://developers.ledger.com/)
|
|
6
|
+
|
|
7
|
+
## @ledgerhq/hw-app-aptos
|
|
8
|
+
|
|
9
|
+
Ledger Hardware Wallet Aptos JavaScript bindings.
|
|
10
|
+
|
|
11
|
+
***
|
|
12
|
+
|
|
13
|
+
## Are you adding Ledger support to your software wallet?
|
|
14
|
+
|
|
15
|
+
You may be using this package to communicate with the Aptos Nano App.
|
|
16
|
+
|
|
17
|
+
For a smooth and quick integration:
|
|
18
|
+
|
|
19
|
+
* See the developers’ documentation on the [Developer Portal](https://developers.ledger.com/docs/transport/overview/) and
|
|
20
|
+
* Go on [Discord](https://developers.ledger.com/discord-pro/) to chat with developer support and the developer community.
|
|
21
|
+
|
|
22
|
+
***
|
|
23
|
+
|
|
24
|
+
## API
|
|
25
|
+
|
|
26
|
+
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
|
27
|
+
|
|
28
|
+
#### Table of Contents
|
|
29
|
+
|
|
30
|
+
* [Aptos](#aptos)
|
|
31
|
+
* [Parameters](#parameters)
|
|
32
|
+
* [Examples](#examples)
|
|
33
|
+
* [getAddress](#getaddress)
|
|
34
|
+
* [Parameters](#parameters-1)
|
|
35
|
+
* [Examples](#examples-1)
|
|
36
|
+
* [signTransaction](#signtransaction)
|
|
37
|
+
* [Parameters](#parameters-2)
|
|
38
|
+
* [Examples](#examples-2)
|
|
39
|
+
|
|
40
|
+
### Aptos
|
|
41
|
+
|
|
42
|
+
Aptos API
|
|
43
|
+
|
|
44
|
+
#### Parameters
|
|
45
|
+
|
|
46
|
+
* `transport` **Transport** 
|
|
47
|
+
* `scrambleKey` (optional, default `"aptos"`)
|
|
48
|
+
|
|
49
|
+
#### Examples
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
53
|
+
import Aptos from "@ledgerhq/hw-app-aptos";
|
|
54
|
+
|
|
55
|
+
function establishConnection() {
|
|
56
|
+
return Transport.create()
|
|
57
|
+
.then(transport => new Aptos(transport));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fetchAddress(aptosClient) {
|
|
61
|
+
return aptosClient.getAddress("44'/144'/0'/0/0");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function signTransaction(aptosClient, deviceData, seqNo, buffer) { *
|
|
65
|
+
const transactionBlob = encode(buffer);
|
|
66
|
+
|
|
67
|
+
console.log('Sending transaction to device for approval...');
|
|
68
|
+
return aptosClient.signTransaction("44'/144'/0'/0/0", transactionBlob);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function prepareAndSign(aptosClient, seqNo) {
|
|
72
|
+
return fetchAddress(aptosClient)
|
|
73
|
+
.then(deviceData => signTransaction(aptosClient, deviceData, seqNo));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
establishConnection()
|
|
77
|
+
.then(aptos => prepareAndSign(aptos, 123, payload))
|
|
78
|
+
.then(signature => console.log(`Signature: ${signature}`))
|
|
79
|
+
.catch(e => console.log(`An error occurred (${e.message})`));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### getAddress
|
|
83
|
+
|
|
84
|
+
get Aptos address for a given BIP 32 path.
|
|
85
|
+
|
|
86
|
+
##### Parameters
|
|
87
|
+
|
|
88
|
+
* `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** a path in BIP 32 format
|
|
89
|
+
* `display` optionally enable or not the display (optional, default `false`)
|
|
90
|
+
|
|
91
|
+
##### Examples
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const result = await aptos.getAddress("44'/144'/0'/0/0");
|
|
95
|
+
const { publicKey, address } = result;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<AddressData>** an object with a publicKey, address and (optionally) chainCode
|
|
99
|
+
|
|
100
|
+
#### signTransaction
|
|
101
|
+
|
|
102
|
+
sign a Aptos transaction with a given BIP 32 path
|
|
103
|
+
|
|
104
|
+
##### Parameters
|
|
105
|
+
|
|
106
|
+
* `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** a path in BIP 32 format
|
|
107
|
+
* `txBuffer` **[Buffer](https://nodejs.org/api/buffer.html)** the buffer to be signed for transaction
|
|
108
|
+
|
|
109
|
+
##### Examples
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const signature = await aptos.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F");
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<{signature: [Buffer](https://nodejs.org/api/buffer.html)}>** a signature as hex string
|
package/jest.config.ts
ADDED
package/lib/Aptos.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Ledger Node JS API
|
|
3
|
+
* (c) 2017-2018 Ledger
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
/// <reference types="node" />
|
|
18
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
19
|
+
interface AppConfig {
|
|
20
|
+
version: string;
|
|
21
|
+
}
|
|
22
|
+
export interface AddressData {
|
|
23
|
+
publicKey: Buffer;
|
|
24
|
+
chainCode: Buffer;
|
|
25
|
+
address: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Aptos API
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* import Transport from "@ledgerhq/hw-transport";
|
|
32
|
+
* import Aptos from "@ledgerhq/hw-app-aptos";
|
|
33
|
+
*
|
|
34
|
+
* function establishConnection() {
|
|
35
|
+
* return Transport.create()
|
|
36
|
+
* .then(transport => new Aptos(transport));
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* function fetchAddress(aptosClient) {
|
|
40
|
+
* return aptosClient.getAddress("44'/144'/0'/0/0");
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* function signTransaction(aptosClient, deviceData, seqNo, buffer) { *
|
|
44
|
+
* const transactionBlob = encode(buffer);
|
|
45
|
+
*
|
|
46
|
+
* console.log('Sending transaction to device for approval...');
|
|
47
|
+
* return aptosClient.signTransaction("44'/144'/0'/0/0", transactionBlob);
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* function prepareAndSign(aptosClient, seqNo) {
|
|
51
|
+
* return fetchAddress(aptosClient)
|
|
52
|
+
* .then(deviceData => signTransaction(aptosClient, deviceData, seqNo));
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* establishConnection()
|
|
56
|
+
* .then(aptos => prepareAndSign(aptos, 123, payload))
|
|
57
|
+
* .then(signature => console.log(`Signature: ${signature}`))
|
|
58
|
+
* .catch(e => console.log(`An error occurred (${e.message})`));
|
|
59
|
+
*/
|
|
60
|
+
export default class Aptos {
|
|
61
|
+
transport: Transport;
|
|
62
|
+
constructor(transport: Transport, scrambleKey?: string);
|
|
63
|
+
getVersion(): Promise<AppConfig>;
|
|
64
|
+
/**
|
|
65
|
+
* get Aptos address for a given BIP 32 path.
|
|
66
|
+
*
|
|
67
|
+
* @param path a path in BIP 32 format
|
|
68
|
+
* @param display optionally enable or not the display
|
|
69
|
+
* @return an object with a publicKey, address and (optionally) chainCode
|
|
70
|
+
* @example
|
|
71
|
+
* const result = await aptos.getAddress("44'/144'/0'/0/0");
|
|
72
|
+
* const { publicKey, address } = result;
|
|
73
|
+
*/
|
|
74
|
+
getAddress(path: string, display?: boolean): Promise<AddressData>;
|
|
75
|
+
/**
|
|
76
|
+
* sign a Aptos transaction with a given BIP 32 path
|
|
77
|
+
*
|
|
78
|
+
*
|
|
79
|
+
* @param path a path in BIP 32 format
|
|
80
|
+
* @param txBuffer the buffer to be signed for transaction
|
|
81
|
+
* @return a signature as hex string
|
|
82
|
+
* @example
|
|
83
|
+
* const signature = await aptos.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F");
|
|
84
|
+
*/
|
|
85
|
+
signTransaction(path: string, txBuffer: Buffer): Promise<{
|
|
86
|
+
signature: Buffer;
|
|
87
|
+
}>;
|
|
88
|
+
private sendToDevice;
|
|
89
|
+
private pathToBuffer;
|
|
90
|
+
private serializePath;
|
|
91
|
+
private publicKeyToAddress;
|
|
92
|
+
private throwOnFailure;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=Aptos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Aptos.d.ts","sourceRoot":"","sources":["../src/Aptos.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;kFAekF;;AAIlF,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAiB/C,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,SAAS,EAAE,SAAS,CAAC;gBAET,SAAS,EAAE,SAAS,EAAE,WAAW,SAAU;IAKjD,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC;IAYtC;;;;;;;;;OASG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;IAwBrE;;;;;;;;;OASG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;YAWvE,YAAY;IA+B1B,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,cAAc;CAOvB"}
|
package/lib/Aptos.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Ledger Node JS API
|
|
4
|
+
* (c) 2017-2018 Ledger
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
********************************************************************************/
|
|
18
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
19
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
20
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
21
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
22
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
23
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
24
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
28
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
29
|
+
};
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
const bip32_path_1 = __importDefault(require("bip32-path"));
|
|
32
|
+
const sha3_1 = require("@noble/hashes/sha3");
|
|
33
|
+
const errors_1 = require("@ledgerhq/errors");
|
|
34
|
+
const MAX_APDU_LEN = 255;
|
|
35
|
+
const P1_NON_CONFIRM = 0x00;
|
|
36
|
+
const P1_CONFIRM = 0x01;
|
|
37
|
+
const P1_START = 0x00;
|
|
38
|
+
const P2_MORE = 0x80;
|
|
39
|
+
const P2_LAST = 0x00;
|
|
40
|
+
const LEDGER_CLA = 0x5b;
|
|
41
|
+
const INS = {
|
|
42
|
+
GET_VERSION: 0x03,
|
|
43
|
+
GET_PUBLIC_KEY: 0x05,
|
|
44
|
+
SIGN_TX: 0x06,
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Aptos API
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* import Transport from "@ledgerhq/hw-transport";
|
|
51
|
+
* import Aptos from "@ledgerhq/hw-app-aptos";
|
|
52
|
+
*
|
|
53
|
+
* function establishConnection() {
|
|
54
|
+
* return Transport.create()
|
|
55
|
+
* .then(transport => new Aptos(transport));
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* function fetchAddress(aptosClient) {
|
|
59
|
+
* return aptosClient.getAddress("44'/144'/0'/0/0");
|
|
60
|
+
* }
|
|
61
|
+
*
|
|
62
|
+
* function signTransaction(aptosClient, deviceData, seqNo, buffer) { *
|
|
63
|
+
* const transactionBlob = encode(buffer);
|
|
64
|
+
*
|
|
65
|
+
* console.log('Sending transaction to device for approval...');
|
|
66
|
+
* return aptosClient.signTransaction("44'/144'/0'/0/0", transactionBlob);
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* function prepareAndSign(aptosClient, seqNo) {
|
|
70
|
+
* return fetchAddress(aptosClient)
|
|
71
|
+
* .then(deviceData => signTransaction(aptosClient, deviceData, seqNo));
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* establishConnection()
|
|
75
|
+
* .then(aptos => prepareAndSign(aptos, 123, payload))
|
|
76
|
+
* .then(signature => console.log(`Signature: ${signature}`))
|
|
77
|
+
* .catch(e => console.log(`An error occurred (${e.message})`));
|
|
78
|
+
*/
|
|
79
|
+
class Aptos {
|
|
80
|
+
constructor(transport, scrambleKey = "aptos") {
|
|
81
|
+
this.transport = transport;
|
|
82
|
+
transport.decorateAppAPIMethods(this, ["getVersion", "getAddress"], scrambleKey);
|
|
83
|
+
}
|
|
84
|
+
getVersion() {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const [major, minor, patch] = yield this.sendToDevice(INS.GET_VERSION, P1_NON_CONFIRM, P2_LAST, Buffer.alloc(0));
|
|
87
|
+
return {
|
|
88
|
+
version: `${major}.${minor}.${patch}`,
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* get Aptos address for a given BIP 32 path.
|
|
94
|
+
*
|
|
95
|
+
* @param path a path in BIP 32 format
|
|
96
|
+
* @param display optionally enable or not the display
|
|
97
|
+
* @return an object with a publicKey, address and (optionally) chainCode
|
|
98
|
+
* @example
|
|
99
|
+
* const result = await aptos.getAddress("44'/144'/0'/0/0");
|
|
100
|
+
* const { publicKey, address } = result;
|
|
101
|
+
*/
|
|
102
|
+
getAddress(path_1) {
|
|
103
|
+
return __awaiter(this, arguments, void 0, function* (path, display = false) {
|
|
104
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
105
|
+
const responseBuffer = yield this.sendToDevice(INS.GET_PUBLIC_KEY, display ? P1_CONFIRM : P1_NON_CONFIRM, P2_LAST, pathBuffer);
|
|
106
|
+
let offset = 1;
|
|
107
|
+
const pubKeyLen = responseBuffer.subarray(0, offset)[0] - 1;
|
|
108
|
+
const pubKeyBuffer = responseBuffer.subarray(++offset, (offset += pubKeyLen));
|
|
109
|
+
const chainCodeLen = responseBuffer.subarray(offset, ++offset)[0];
|
|
110
|
+
const chainCodeBuffer = responseBuffer.subarray(offset, offset + chainCodeLen);
|
|
111
|
+
const address = "0x" + this.publicKeyToAddress(pubKeyBuffer).toString("hex");
|
|
112
|
+
return {
|
|
113
|
+
publicKey: pubKeyBuffer,
|
|
114
|
+
chainCode: chainCodeBuffer,
|
|
115
|
+
address,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* sign a Aptos transaction with a given BIP 32 path
|
|
121
|
+
*
|
|
122
|
+
*
|
|
123
|
+
* @param path a path in BIP 32 format
|
|
124
|
+
* @param txBuffer the buffer to be signed for transaction
|
|
125
|
+
* @return a signature as hex string
|
|
126
|
+
* @example
|
|
127
|
+
* const signature = await aptos.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F");
|
|
128
|
+
*/
|
|
129
|
+
signTransaction(path, txBuffer) {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
132
|
+
yield this.sendToDevice(INS.SIGN_TX, P1_START, P2_MORE, pathBuffer);
|
|
133
|
+
const responseBuffer = yield this.sendToDevice(INS.SIGN_TX, 1, P2_LAST, txBuffer);
|
|
134
|
+
const signatureLen = responseBuffer[0];
|
|
135
|
+
const signatureBuffer = responseBuffer.subarray(1, 1 + signatureLen);
|
|
136
|
+
return { signature: signatureBuffer };
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// send chunked if payload size exceeds maximum for a call
|
|
140
|
+
sendToDevice(instruction, p1, p2, payload) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
const acceptStatusList = [errors_1.StatusCodes.OK];
|
|
143
|
+
let payloadOffset = 0;
|
|
144
|
+
if (payload.length > MAX_APDU_LEN) {
|
|
145
|
+
while (payload.length - payloadOffset > MAX_APDU_LEN) {
|
|
146
|
+
const buf = payload.subarray(payloadOffset, (payloadOffset += MAX_APDU_LEN));
|
|
147
|
+
const reply = yield this.transport.send(LEDGER_CLA, instruction, p1++, P2_MORE, buf, acceptStatusList);
|
|
148
|
+
this.throwOnFailure(reply);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const buf = payload.subarray(payloadOffset);
|
|
152
|
+
const reply = yield this.transport.send(LEDGER_CLA, instruction, p1, p2, buf, acceptStatusList);
|
|
153
|
+
this.throwOnFailure(reply);
|
|
154
|
+
return reply.subarray(0, reply.length - 2);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
pathToBuffer(originalPath) {
|
|
158
|
+
const path = originalPath
|
|
159
|
+
.split("/")
|
|
160
|
+
.filter(value => value !== "m")
|
|
161
|
+
.map(value => (value.endsWith("'") || value.endsWith("h") ? value : value + "'"))
|
|
162
|
+
.join("/");
|
|
163
|
+
const pathNums = bip32_path_1.default.fromString(path).toPathArray();
|
|
164
|
+
return this.serializePath(pathNums);
|
|
165
|
+
}
|
|
166
|
+
serializePath(path) {
|
|
167
|
+
const buf = Buffer.alloc(1 + path.length * 4);
|
|
168
|
+
buf.writeUInt8(path.length, 0);
|
|
169
|
+
for (const [i, num] of path.entries()) {
|
|
170
|
+
buf.writeUInt32BE(num, 1 + i * 4);
|
|
171
|
+
}
|
|
172
|
+
return buf;
|
|
173
|
+
}
|
|
174
|
+
publicKeyToAddress(pubKey) {
|
|
175
|
+
const hash = sha3_1.sha3_256.create();
|
|
176
|
+
hash.update(pubKey);
|
|
177
|
+
hash.update("\x00");
|
|
178
|
+
return Buffer.from(hash.digest());
|
|
179
|
+
}
|
|
180
|
+
throwOnFailure(reply) {
|
|
181
|
+
// transport makes sure reply has a valid length
|
|
182
|
+
const status = reply.readUInt16BE(reply.length - 2);
|
|
183
|
+
if (status !== errors_1.StatusCodes.OK) {
|
|
184
|
+
throw new Error(`Failure with status code: 0x${status.toString(16)}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.default = Aptos;
|
|
189
|
+
//# sourceMappingURL=Aptos.js.map
|
package/lib/Aptos.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Aptos.js","sourceRoot":"","sources":["../src/Aptos.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Ledger Node JS API
|
|
3
|
+
* (c) 2017-2018 Ledger
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
/// <reference types="node" />
|
|
18
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
19
|
+
interface AppConfig {
|
|
20
|
+
version: string;
|
|
21
|
+
}
|
|
22
|
+
export interface AddressData {
|
|
23
|
+
publicKey: Buffer;
|
|
24
|
+
chainCode: Buffer;
|
|
25
|
+
address: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Aptos API
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* import Transport from "@ledgerhq/hw-transport";
|
|
32
|
+
* import Aptos from "@ledgerhq/hw-app-aptos";
|
|
33
|
+
*
|
|
34
|
+
* function establishConnection() {
|
|
35
|
+
* return Transport.create()
|
|
36
|
+
* .then(transport => new Aptos(transport));
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* function fetchAddress(aptosClient) {
|
|
40
|
+
* return aptosClient.getAddress("44'/144'/0'/0/0");
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* function signTransaction(aptosClient, deviceData, seqNo, buffer) { *
|
|
44
|
+
* const transactionBlob = encode(buffer);
|
|
45
|
+
*
|
|
46
|
+
* console.log('Sending transaction to device for approval...');
|
|
47
|
+
* return aptosClient.signTransaction("44'/144'/0'/0/0", transactionBlob);
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* function prepareAndSign(aptosClient, seqNo) {
|
|
51
|
+
* return fetchAddress(aptosClient)
|
|
52
|
+
* .then(deviceData => signTransaction(aptosClient, deviceData, seqNo));
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* establishConnection()
|
|
56
|
+
* .then(aptos => prepareAndSign(aptos, 123, payload))
|
|
57
|
+
* .then(signature => console.log(`Signature: ${signature}`))
|
|
58
|
+
* .catch(e => console.log(`An error occurred (${e.message})`));
|
|
59
|
+
*/
|
|
60
|
+
export default class Aptos {
|
|
61
|
+
transport: Transport;
|
|
62
|
+
constructor(transport: Transport, scrambleKey?: string);
|
|
63
|
+
getVersion(): Promise<AppConfig>;
|
|
64
|
+
/**
|
|
65
|
+
* get Aptos address for a given BIP 32 path.
|
|
66
|
+
*
|
|
67
|
+
* @param path a path in BIP 32 format
|
|
68
|
+
* @param display optionally enable or not the display
|
|
69
|
+
* @return an object with a publicKey, address and (optionally) chainCode
|
|
70
|
+
* @example
|
|
71
|
+
* const result = await aptos.getAddress("44'/144'/0'/0/0");
|
|
72
|
+
* const { publicKey, address } = result;
|
|
73
|
+
*/
|
|
74
|
+
getAddress(path: string, display?: boolean): Promise<AddressData>;
|
|
75
|
+
/**
|
|
76
|
+
* sign a Aptos transaction with a given BIP 32 path
|
|
77
|
+
*
|
|
78
|
+
*
|
|
79
|
+
* @param path a path in BIP 32 format
|
|
80
|
+
* @param txBuffer the buffer to be signed for transaction
|
|
81
|
+
* @return a signature as hex string
|
|
82
|
+
* @example
|
|
83
|
+
* const signature = await aptos.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F");
|
|
84
|
+
*/
|
|
85
|
+
signTransaction(path: string, txBuffer: Buffer): Promise<{
|
|
86
|
+
signature: Buffer;
|
|
87
|
+
}>;
|
|
88
|
+
private sendToDevice;
|
|
89
|
+
private pathToBuffer;
|
|
90
|
+
private serializePath;
|
|
91
|
+
private publicKeyToAddress;
|
|
92
|
+
private throwOnFailure;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=Aptos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Aptos.d.ts","sourceRoot":"","sources":["../src/Aptos.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;kFAekF;;AAIlF,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAiB/C,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,SAAS,EAAE,SAAS,CAAC;gBAET,SAAS,EAAE,SAAS,EAAE,WAAW,SAAU;IAKjD,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC;IAYtC;;;;;;;;;OASG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;IAwBrE;;;;;;;;;OASG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;YAWvE,YAAY;IA+B1B,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,cAAc;CAOvB"}
|
package/lib-es/Aptos.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Ledger Node JS API
|
|
3
|
+
* (c) 2017-2018 Ledger
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
18
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
19
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
20
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
21
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
22
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
23
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
import BIPPath from "bip32-path";
|
|
27
|
+
import { sha3_256 as sha3Hash } from "@noble/hashes/sha3";
|
|
28
|
+
import { StatusCodes } from "@ledgerhq/errors";
|
|
29
|
+
const MAX_APDU_LEN = 255;
|
|
30
|
+
const P1_NON_CONFIRM = 0x00;
|
|
31
|
+
const P1_CONFIRM = 0x01;
|
|
32
|
+
const P1_START = 0x00;
|
|
33
|
+
const P2_MORE = 0x80;
|
|
34
|
+
const P2_LAST = 0x00;
|
|
35
|
+
const LEDGER_CLA = 0x5b;
|
|
36
|
+
const INS = {
|
|
37
|
+
GET_VERSION: 0x03,
|
|
38
|
+
GET_PUBLIC_KEY: 0x05,
|
|
39
|
+
SIGN_TX: 0x06,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Aptos API
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* import Transport from "@ledgerhq/hw-transport";
|
|
46
|
+
* import Aptos from "@ledgerhq/hw-app-aptos";
|
|
47
|
+
*
|
|
48
|
+
* function establishConnection() {
|
|
49
|
+
* return Transport.create()
|
|
50
|
+
* .then(transport => new Aptos(transport));
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* function fetchAddress(aptosClient) {
|
|
54
|
+
* return aptosClient.getAddress("44'/144'/0'/0/0");
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* function signTransaction(aptosClient, deviceData, seqNo, buffer) { *
|
|
58
|
+
* const transactionBlob = encode(buffer);
|
|
59
|
+
*
|
|
60
|
+
* console.log('Sending transaction to device for approval...');
|
|
61
|
+
* return aptosClient.signTransaction("44'/144'/0'/0/0", transactionBlob);
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* function prepareAndSign(aptosClient, seqNo) {
|
|
65
|
+
* return fetchAddress(aptosClient)
|
|
66
|
+
* .then(deviceData => signTransaction(aptosClient, deviceData, seqNo));
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* establishConnection()
|
|
70
|
+
* .then(aptos => prepareAndSign(aptos, 123, payload))
|
|
71
|
+
* .then(signature => console.log(`Signature: ${signature}`))
|
|
72
|
+
* .catch(e => console.log(`An error occurred (${e.message})`));
|
|
73
|
+
*/
|
|
74
|
+
export default class Aptos {
|
|
75
|
+
constructor(transport, scrambleKey = "aptos") {
|
|
76
|
+
this.transport = transport;
|
|
77
|
+
transport.decorateAppAPIMethods(this, ["getVersion", "getAddress"], scrambleKey);
|
|
78
|
+
}
|
|
79
|
+
getVersion() {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const [major, minor, patch] = yield this.sendToDevice(INS.GET_VERSION, P1_NON_CONFIRM, P2_LAST, Buffer.alloc(0));
|
|
82
|
+
return {
|
|
83
|
+
version: `${major}.${minor}.${patch}`,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* get Aptos address for a given BIP 32 path.
|
|
89
|
+
*
|
|
90
|
+
* @param path a path in BIP 32 format
|
|
91
|
+
* @param display optionally enable or not the display
|
|
92
|
+
* @return an object with a publicKey, address and (optionally) chainCode
|
|
93
|
+
* @example
|
|
94
|
+
* const result = await aptos.getAddress("44'/144'/0'/0/0");
|
|
95
|
+
* const { publicKey, address } = result;
|
|
96
|
+
*/
|
|
97
|
+
getAddress(path_1) {
|
|
98
|
+
return __awaiter(this, arguments, void 0, function* (path, display = false) {
|
|
99
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
100
|
+
const responseBuffer = yield this.sendToDevice(INS.GET_PUBLIC_KEY, display ? P1_CONFIRM : P1_NON_CONFIRM, P2_LAST, pathBuffer);
|
|
101
|
+
let offset = 1;
|
|
102
|
+
const pubKeyLen = responseBuffer.subarray(0, offset)[0] - 1;
|
|
103
|
+
const pubKeyBuffer = responseBuffer.subarray(++offset, (offset += pubKeyLen));
|
|
104
|
+
const chainCodeLen = responseBuffer.subarray(offset, ++offset)[0];
|
|
105
|
+
const chainCodeBuffer = responseBuffer.subarray(offset, offset + chainCodeLen);
|
|
106
|
+
const address = "0x" + this.publicKeyToAddress(pubKeyBuffer).toString("hex");
|
|
107
|
+
return {
|
|
108
|
+
publicKey: pubKeyBuffer,
|
|
109
|
+
chainCode: chainCodeBuffer,
|
|
110
|
+
address,
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* sign a Aptos transaction with a given BIP 32 path
|
|
116
|
+
*
|
|
117
|
+
*
|
|
118
|
+
* @param path a path in BIP 32 format
|
|
119
|
+
* @param txBuffer the buffer to be signed for transaction
|
|
120
|
+
* @return a signature as hex string
|
|
121
|
+
* @example
|
|
122
|
+
* const signature = await aptos.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F");
|
|
123
|
+
*/
|
|
124
|
+
signTransaction(path, txBuffer) {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
127
|
+
yield this.sendToDevice(INS.SIGN_TX, P1_START, P2_MORE, pathBuffer);
|
|
128
|
+
const responseBuffer = yield this.sendToDevice(INS.SIGN_TX, 1, P2_LAST, txBuffer);
|
|
129
|
+
const signatureLen = responseBuffer[0];
|
|
130
|
+
const signatureBuffer = responseBuffer.subarray(1, 1 + signatureLen);
|
|
131
|
+
return { signature: signatureBuffer };
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// send chunked if payload size exceeds maximum for a call
|
|
135
|
+
sendToDevice(instruction, p1, p2, payload) {
|
|
136
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
137
|
+
const acceptStatusList = [StatusCodes.OK];
|
|
138
|
+
let payloadOffset = 0;
|
|
139
|
+
if (payload.length > MAX_APDU_LEN) {
|
|
140
|
+
while (payload.length - payloadOffset > MAX_APDU_LEN) {
|
|
141
|
+
const buf = payload.subarray(payloadOffset, (payloadOffset += MAX_APDU_LEN));
|
|
142
|
+
const reply = yield this.transport.send(LEDGER_CLA, instruction, p1++, P2_MORE, buf, acceptStatusList);
|
|
143
|
+
this.throwOnFailure(reply);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const buf = payload.subarray(payloadOffset);
|
|
147
|
+
const reply = yield this.transport.send(LEDGER_CLA, instruction, p1, p2, buf, acceptStatusList);
|
|
148
|
+
this.throwOnFailure(reply);
|
|
149
|
+
return reply.subarray(0, reply.length - 2);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
pathToBuffer(originalPath) {
|
|
153
|
+
const path = originalPath
|
|
154
|
+
.split("/")
|
|
155
|
+
.filter(value => value !== "m")
|
|
156
|
+
.map(value => (value.endsWith("'") || value.endsWith("h") ? value : value + "'"))
|
|
157
|
+
.join("/");
|
|
158
|
+
const pathNums = BIPPath.fromString(path).toPathArray();
|
|
159
|
+
return this.serializePath(pathNums);
|
|
160
|
+
}
|
|
161
|
+
serializePath(path) {
|
|
162
|
+
const buf = Buffer.alloc(1 + path.length * 4);
|
|
163
|
+
buf.writeUInt8(path.length, 0);
|
|
164
|
+
for (const [i, num] of path.entries()) {
|
|
165
|
+
buf.writeUInt32BE(num, 1 + i * 4);
|
|
166
|
+
}
|
|
167
|
+
return buf;
|
|
168
|
+
}
|
|
169
|
+
publicKeyToAddress(pubKey) {
|
|
170
|
+
const hash = sha3Hash.create();
|
|
171
|
+
hash.update(pubKey);
|
|
172
|
+
hash.update("\x00");
|
|
173
|
+
return Buffer.from(hash.digest());
|
|
174
|
+
}
|
|
175
|
+
throwOnFailure(reply) {
|
|
176
|
+
// transport makes sure reply has a valid length
|
|
177
|
+
const status = reply.readUInt16BE(reply.length - 2);
|
|
178
|
+
if (status !== StatusCodes.OK) {
|
|
179
|
+
throw new Error(`Failure with status code: 0x${status.toString(16)}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=Aptos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Aptos.js","sourceRoot":"","sources":["../src/Aptos.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ledgerhq/hw-app-aptos",
|
|
3
|
+
"version": "6.30.0-next.0",
|
|
4
|
+
"description": "Ledger Hardware Wallet Aptos Application API",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Ledger",
|
|
7
|
+
"LedgerWallet",
|
|
8
|
+
"Aptos",
|
|
9
|
+
"apt",
|
|
10
|
+
"NanoS",
|
|
11
|
+
"Blue",
|
|
12
|
+
"Hardware Wallet"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/LedgerHQ/ledger-live.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/LedgerHQ/ledger-live/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/LedgerHQ/ledger-live/tree/develop/libs/ledgerjs/packages/hw-app-aptos",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"main": "lib/Aptos.js",
|
|
26
|
+
"module": "lib-es/Aptos.js",
|
|
27
|
+
"types": "lib/Aptos.d.ts",
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@noble/hashes": "1.6.1",
|
|
31
|
+
"bip32-path": "^0.4.2",
|
|
32
|
+
"@ledgerhq/errors": "^6.19.1",
|
|
33
|
+
"@ledgerhq/hw-transport": "^6.31.4"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/jest": "^29.5.10",
|
|
37
|
+
"@types/node": "^20.8.10",
|
|
38
|
+
"documentation": "14.0.2",
|
|
39
|
+
"jest": "^29.7.0",
|
|
40
|
+
"rimraf": "^4.4.1",
|
|
41
|
+
"source-map-support": "^0.5.21",
|
|
42
|
+
"ts-jest": "^29.1.1",
|
|
43
|
+
"ts-node": "^10.4.0",
|
|
44
|
+
"@ledgerhq/hw-transport-mocker": "^6.29.4"
|
|
45
|
+
},
|
|
46
|
+
"gitHead": "dd0dea64b58e5a9125c8a422dcffd29e5ef6abec",
|
|
47
|
+
"scripts": {
|
|
48
|
+
"clean": "rimraf lib lib-es",
|
|
49
|
+
"build": "tsc && tsc -m ES6 --outDir lib-es",
|
|
50
|
+
"prewatch": "pnpm build",
|
|
51
|
+
"watch": "tsc --watch",
|
|
52
|
+
"watch:es": "tsc --watch -m ES6 --outDir lib-es",
|
|
53
|
+
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
|
|
54
|
+
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
|
|
55
|
+
"lint:fix": "pnpm lint --fix",
|
|
56
|
+
"test": "jest",
|
|
57
|
+
"unimported": "unimported"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/Aptos.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Ledger Node JS API
|
|
3
|
+
* (c) 2017-2018 Ledger
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
|
|
18
|
+
import BIPPath from "bip32-path";
|
|
19
|
+
import { sha3_256 as sha3Hash } from "@noble/hashes/sha3";
|
|
20
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
21
|
+
import { StatusCodes } from "@ledgerhq/errors";
|
|
22
|
+
|
|
23
|
+
const MAX_APDU_LEN = 255;
|
|
24
|
+
const P1_NON_CONFIRM = 0x00;
|
|
25
|
+
const P1_CONFIRM = 0x01;
|
|
26
|
+
const P1_START = 0x00;
|
|
27
|
+
const P2_MORE = 0x80;
|
|
28
|
+
const P2_LAST = 0x00;
|
|
29
|
+
|
|
30
|
+
const LEDGER_CLA = 0x5b;
|
|
31
|
+
const INS = {
|
|
32
|
+
GET_VERSION: 0x03,
|
|
33
|
+
GET_PUBLIC_KEY: 0x05,
|
|
34
|
+
SIGN_TX: 0x06,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface AppConfig {
|
|
38
|
+
version: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AddressData {
|
|
42
|
+
publicKey: Buffer;
|
|
43
|
+
chainCode: Buffer;
|
|
44
|
+
address: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Aptos API
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* import Transport from "@ledgerhq/hw-transport";
|
|
52
|
+
* import Aptos from "@ledgerhq/hw-app-aptos";
|
|
53
|
+
*
|
|
54
|
+
* function establishConnection() {
|
|
55
|
+
* return Transport.create()
|
|
56
|
+
* .then(transport => new Aptos(transport));
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* function fetchAddress(aptosClient) {
|
|
60
|
+
* return aptosClient.getAddress("44'/144'/0'/0/0");
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* function signTransaction(aptosClient, deviceData, seqNo, buffer) { *
|
|
64
|
+
* const transactionBlob = encode(buffer);
|
|
65
|
+
*
|
|
66
|
+
* console.log('Sending transaction to device for approval...');
|
|
67
|
+
* return aptosClient.signTransaction("44'/144'/0'/0/0", transactionBlob);
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* function prepareAndSign(aptosClient, seqNo) {
|
|
71
|
+
* return fetchAddress(aptosClient)
|
|
72
|
+
* .then(deviceData => signTransaction(aptosClient, deviceData, seqNo));
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* establishConnection()
|
|
76
|
+
* .then(aptos => prepareAndSign(aptos, 123, payload))
|
|
77
|
+
* .then(signature => console.log(`Signature: ${signature}`))
|
|
78
|
+
* .catch(e => console.log(`An error occurred (${e.message})`));
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
export default class Aptos {
|
|
82
|
+
transport: Transport;
|
|
83
|
+
|
|
84
|
+
constructor(transport: Transport, scrambleKey = "aptos") {
|
|
85
|
+
this.transport = transport;
|
|
86
|
+
transport.decorateAppAPIMethods(this, ["getVersion", "getAddress"], scrambleKey);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async getVersion(): Promise<AppConfig> {
|
|
90
|
+
const [major, minor, patch] = await this.sendToDevice(
|
|
91
|
+
INS.GET_VERSION,
|
|
92
|
+
P1_NON_CONFIRM,
|
|
93
|
+
P2_LAST,
|
|
94
|
+
Buffer.alloc(0),
|
|
95
|
+
);
|
|
96
|
+
return {
|
|
97
|
+
version: `${major}.${minor}.${patch}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* get Aptos address for a given BIP 32 path.
|
|
103
|
+
*
|
|
104
|
+
* @param path a path in BIP 32 format
|
|
105
|
+
* @param display optionally enable or not the display
|
|
106
|
+
* @return an object with a publicKey, address and (optionally) chainCode
|
|
107
|
+
* @example
|
|
108
|
+
* const result = await aptos.getAddress("44'/144'/0'/0/0");
|
|
109
|
+
* const { publicKey, address } = result;
|
|
110
|
+
*/
|
|
111
|
+
async getAddress(path: string, display = false): Promise<AddressData> {
|
|
112
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
113
|
+
const responseBuffer = await this.sendToDevice(
|
|
114
|
+
INS.GET_PUBLIC_KEY,
|
|
115
|
+
display ? P1_CONFIRM : P1_NON_CONFIRM,
|
|
116
|
+
P2_LAST,
|
|
117
|
+
pathBuffer,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
let offset = 1;
|
|
121
|
+
const pubKeyLen = responseBuffer.subarray(0, offset)[0] - 1;
|
|
122
|
+
const pubKeyBuffer = responseBuffer.subarray(++offset, (offset += pubKeyLen));
|
|
123
|
+
const chainCodeLen = responseBuffer.subarray(offset, ++offset)[0];
|
|
124
|
+
const chainCodeBuffer = responseBuffer.subarray(offset, offset + chainCodeLen);
|
|
125
|
+
|
|
126
|
+
const address = "0x" + this.publicKeyToAddress(pubKeyBuffer).toString("hex");
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
publicKey: pubKeyBuffer,
|
|
130
|
+
chainCode: chainCodeBuffer,
|
|
131
|
+
address,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* sign a Aptos transaction with a given BIP 32 path
|
|
137
|
+
*
|
|
138
|
+
*
|
|
139
|
+
* @param path a path in BIP 32 format
|
|
140
|
+
* @param txBuffer the buffer to be signed for transaction
|
|
141
|
+
* @return a signature as hex string
|
|
142
|
+
* @example
|
|
143
|
+
* const signature = await aptos.signTransaction("44'/144'/0'/0/0", "12000022800000002400000002614000000001315D3468400000000000000C73210324E5F600B52BB3D9246D49C4AB1722BA7F32B7A3E4F9F2B8A1A28B9118CC36C48114F31B152151B6F42C1D61FE4139D34B424C8647D183142ECFC1831F6E979C6DA907E88B1CAD602DB59E2F");
|
|
144
|
+
*/
|
|
145
|
+
async signTransaction(path: string, txBuffer: Buffer): Promise<{ signature: Buffer }> {
|
|
146
|
+
const pathBuffer = this.pathToBuffer(path);
|
|
147
|
+
await this.sendToDevice(INS.SIGN_TX, P1_START, P2_MORE, pathBuffer);
|
|
148
|
+
const responseBuffer = await this.sendToDevice(INS.SIGN_TX, 1, P2_LAST, txBuffer);
|
|
149
|
+
|
|
150
|
+
const signatureLen = responseBuffer[0];
|
|
151
|
+
const signatureBuffer = responseBuffer.subarray(1, 1 + signatureLen);
|
|
152
|
+
return { signature: signatureBuffer };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// send chunked if payload size exceeds maximum for a call
|
|
156
|
+
private async sendToDevice(
|
|
157
|
+
instruction: number,
|
|
158
|
+
p1: number,
|
|
159
|
+
p2: number,
|
|
160
|
+
payload: Buffer,
|
|
161
|
+
): Promise<Buffer> {
|
|
162
|
+
const acceptStatusList = [StatusCodes.OK];
|
|
163
|
+
let payloadOffset = 0;
|
|
164
|
+
|
|
165
|
+
if (payload.length > MAX_APDU_LEN) {
|
|
166
|
+
while (payload.length - payloadOffset > MAX_APDU_LEN) {
|
|
167
|
+
const buf = payload.subarray(payloadOffset, (payloadOffset += MAX_APDU_LEN));
|
|
168
|
+
const reply = await this.transport.send(
|
|
169
|
+
LEDGER_CLA,
|
|
170
|
+
instruction,
|
|
171
|
+
p1++,
|
|
172
|
+
P2_MORE,
|
|
173
|
+
buf,
|
|
174
|
+
acceptStatusList,
|
|
175
|
+
);
|
|
176
|
+
this.throwOnFailure(reply);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const buf = payload.subarray(payloadOffset);
|
|
181
|
+
const reply = await this.transport.send(LEDGER_CLA, instruction, p1, p2, buf, acceptStatusList);
|
|
182
|
+
this.throwOnFailure(reply);
|
|
183
|
+
|
|
184
|
+
return reply.subarray(0, reply.length - 2);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private pathToBuffer(originalPath: string): Buffer {
|
|
188
|
+
const path = originalPath
|
|
189
|
+
.split("/")
|
|
190
|
+
.filter(value => value !== "m")
|
|
191
|
+
.map(value => (value.endsWith("'") || value.endsWith("h") ? value : value + "'"))
|
|
192
|
+
.join("/");
|
|
193
|
+
const pathNums: number[] = BIPPath.fromString(path).toPathArray();
|
|
194
|
+
return this.serializePath(pathNums);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private serializePath(path: number[]): Buffer {
|
|
198
|
+
const buf = Buffer.alloc(1 + path.length * 4);
|
|
199
|
+
buf.writeUInt8(path.length, 0);
|
|
200
|
+
for (const [i, num] of path.entries()) {
|
|
201
|
+
buf.writeUInt32BE(num, 1 + i * 4);
|
|
202
|
+
}
|
|
203
|
+
return buf;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private publicKeyToAddress(pubKey: Buffer): Buffer {
|
|
207
|
+
const hash = sha3Hash.create();
|
|
208
|
+
hash.update(pubKey);
|
|
209
|
+
hash.update("\x00");
|
|
210
|
+
return Buffer.from(hash.digest());
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private throwOnFailure(reply: Buffer): void {
|
|
214
|
+
// transport makes sure reply has a valid length
|
|
215
|
+
const status = reply.readUInt16BE(reply.length - 2);
|
|
216
|
+
if (status !== StatusCodes.OK) {
|
|
217
|
+
throw new Error(`Failure with status code: 0x${status.toString(16)}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker";
|
|
2
|
+
import Aptos from "../src/Aptos";
|
|
3
|
+
|
|
4
|
+
test("getVersion", async () => {
|
|
5
|
+
const transport = await openTransportReplayer(
|
|
6
|
+
RecordStore.fromString(`
|
|
7
|
+
=> 5b03000000
|
|
8
|
+
<= 0000019000
|
|
9
|
+
`),
|
|
10
|
+
);
|
|
11
|
+
const aptos = new Aptos(transport);
|
|
12
|
+
const result = await aptos.getVersion();
|
|
13
|
+
expect(result).toEqual({
|
|
14
|
+
version: "0.0.1",
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("getAddress without display", async () => {
|
|
19
|
+
const transport = await openTransportReplayer(
|
|
20
|
+
RecordStore.fromString(`
|
|
21
|
+
=> 5b05000015058000002c8000027d800000018000000080000000
|
|
22
|
+
<= 2104d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a41132070c750d272682024bdab22357e7091dff07583574df88850196c14e2da0209df9000
|
|
23
|
+
`),
|
|
24
|
+
);
|
|
25
|
+
const aptos = new Aptos(transport);
|
|
26
|
+
const { address } = await aptos.getAddress("m/44'/637'/1'/0'/0'", false);
|
|
27
|
+
expect(address).toEqual("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("getAddress with display", async () => {
|
|
31
|
+
const transport = await openTransportReplayer(
|
|
32
|
+
RecordStore.fromString(`
|
|
33
|
+
=> 5b05010015058000002c8000027d800000018000000080000000
|
|
34
|
+
<= 2104d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a41132070c750d272682024bdab22357e7091dff07583574df88850196c14e2da0209df9000
|
|
35
|
+
`),
|
|
36
|
+
);
|
|
37
|
+
const aptos = new Aptos(transport);
|
|
38
|
+
const { address } = await aptos.getAddress("m/44'/637'/1'/0'/0'", true);
|
|
39
|
+
expect(address).toEqual("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("signTransaction", async () => {
|
|
43
|
+
const transport = await openTransportReplayer(
|
|
44
|
+
RecordStore.fromString(`
|
|
45
|
+
=> 5b06008015058000002c8000027d800000018000000080000000
|
|
46
|
+
<= 9000
|
|
47
|
+
=> 5b060100f3b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee000000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde082a00000000000000204e0000000000006400000000000000565c51630000000022
|
|
48
|
+
<= 409f6cc54741dceafa8ef5cd11bb33bf432b3296dc516ffa7d798778dac588e2efe2974a9401a366003fd66e14ae446f1d2a9eb99d863e9cf4bf7665cd19663b079000
|
|
49
|
+
`),
|
|
50
|
+
);
|
|
51
|
+
const aptos = new Aptos(transport);
|
|
52
|
+
const transaction = Buffer.from(
|
|
53
|
+
"b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee000000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde082a00000000000000204e0000000000006400000000000000565c51630000000022",
|
|
54
|
+
"hex",
|
|
55
|
+
);
|
|
56
|
+
const { signature } = await aptos.signTransaction("m/44'/637'/1'/0'/0'", transaction);
|
|
57
|
+
expect(signature.toString("hex")).toEqual(
|
|
58
|
+
"9f6cc54741dceafa8ef5cd11bb33bf432b3296dc516ffa7d798778dac588e2efe2974a9401a366003fd66e14ae446f1d2a9eb99d863e9cf4bf7665cd19663b07",
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("should throw on invalid derivation path", async () => {
|
|
63
|
+
const transport = await openTransportReplayer(new RecordStore());
|
|
64
|
+
const aptos = new Aptos(transport);
|
|
65
|
+
return expect(aptos.getAddress("some invalid derivation path", false)).rejects.toThrow("input");
|
|
66
|
+
});
|