@swapkit/wallet-hardware 5.0.1 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/THIRD_PARTY_LICENSES.md +2344 -0
- package/dist/chunk-09e51wma.js +4 -0
- package/dist/chunk-09e51wma.js.map +167 -0
- package/dist/chunk-0arpsm5f.js +4 -0
- package/dist/chunk-0arpsm5f.js.map +10 -0
- package/dist/chunk-1amf58aq.js +4 -0
- package/dist/{chunk-xkh15edk.js.map → chunk-1amf58aq.js.map} +2 -2
- package/dist/chunk-3t17wpjg.js +17 -0
- package/dist/chunk-3t17wpjg.js.map +29 -0
- package/dist/chunk-5c7b7z2d.js +4 -0
- package/dist/{chunk-20wx60bn.js.map → chunk-5c7b7z2d.js.map} +2 -2
- package/dist/chunk-5p3ttw6y.js +4 -0
- package/dist/{chunk-1jpcb30j.js.map → chunk-5p3ttw6y.js.map} +1 -1
- package/dist/chunk-dptbnkzm.js +64 -0
- package/dist/chunk-dptbnkzm.js.map +260 -0
- package/dist/chunk-ds2m83jp.js +5 -0
- package/dist/chunk-ds2m83jp.js.map +11 -0
- package/dist/chunk-eenx3cv6.js +4 -0
- package/dist/chunk-eenx3cv6.js.map +33 -0
- package/dist/chunk-gr3eswf1.js +4 -0
- package/dist/chunk-gr3eswf1.js.map +73 -0
- package/dist/{chunk-7fsaymh4.js → chunk-hg71hp4m.js} +3 -3
- package/dist/{chunk-7fsaymh4.js.map → chunk-hg71hp4m.js.map} +1 -1
- package/dist/chunk-jewm2ag3.js +5 -0
- package/dist/{chunk-4tbtxdxa.js.map → chunk-jewm2ag3.js.map} +2 -2
- package/dist/chunk-jzrytcrf.js +4 -0
- package/dist/chunk-jzrytcrf.js.map +12 -0
- package/dist/chunk-k3thje6a.js +4 -0
- package/dist/chunk-k3thje6a.js.map +9 -0
- package/dist/chunk-nhva847d.js +5 -0
- package/dist/chunk-nhva847d.js.map +9 -0
- package/dist/chunk-tm47brht.js +4 -0
- package/dist/chunk-tm47brht.js.map +140 -0
- package/dist/chunk-xmvg4cds.js +4 -0
- package/dist/chunk-xmvg4cds.js.map +127 -0
- package/dist/chunk-xx7cwpzq.js +4 -0
- package/dist/chunk-xx7cwpzq.js.map +9 -0
- package/dist/chunk-yrxe491r.js +20 -0
- package/dist/chunk-yrxe491r.js.map +17 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/keepkey/index.cjs +2 -2
- package/dist/keepkey/index.cjs.map +2 -2
- package/dist/keepkey/index.js +2 -2
- package/dist/keepkey/index.js.map +2 -2
- package/dist/ledger/index.cjs +90 -2
- package/dist/ledger/index.cjs.map +830 -24
- package/dist/ledger/index.js +2 -2
- package/dist/ledger/index.js.map +21 -19
- package/dist/trezor/index.cjs +2 -2
- package/dist/trezor/index.cjs.map +5 -5
- package/dist/trezor/index.js +2 -2
- package/dist/trezor/index.js.map +3 -3
- package/dist/types/ledger/clients/bitcoin.d.ts +36 -0
- package/dist/types/ledger/clients/bitcoin.d.ts.map +1 -0
- package/dist/types/ledger/clients/cosmos.d.ts +38 -10
- package/dist/types/ledger/clients/cosmos.d.ts.map +1 -1
- package/dist/types/ledger/clients/evm.d.ts +37 -46
- package/dist/types/ledger/clients/evm.d.ts.map +1 -1
- package/dist/types/ledger/clients/near.d.ts +5 -2
- package/dist/types/ledger/clients/near.d.ts.map +1 -1
- package/dist/types/ledger/clients/sui.d.ts +8 -12
- package/dist/types/ledger/clients/sui.d.ts.map +1 -1
- package/dist/types/ledger/clients/thorchain/index.d.ts +34 -8
- package/dist/types/ledger/clients/thorchain/index.d.ts.map +1 -1
- package/dist/types/ledger/clients/thorchain/protocol.d.ts +58 -0
- package/dist/types/ledger/clients/thorchain/protocol.d.ts.map +1 -0
- package/dist/types/ledger/clients/tron.d.ts +11 -8
- package/dist/types/ledger/clients/tron.d.ts.map +1 -1
- package/dist/types/ledger/clients/utxo-legacy-adapter.d.ts +3 -2
- package/dist/types/ledger/clients/utxo-legacy-adapter.d.ts.map +1 -1
- package/dist/types/ledger/clients/utxo-legacy-outputs.d.ts +18 -0
- package/dist/types/ledger/clients/utxo-legacy-outputs.d.ts.map +1 -0
- package/dist/types/ledger/clients/utxo.d.ts +10 -13
- package/dist/types/ledger/clients/utxo.d.ts.map +1 -1
- package/dist/types/ledger/clients/xrp.d.ts +4 -1
- package/dist/types/ledger/clients/xrp.d.ts.map +1 -1
- package/dist/types/ledger/clients/zcash.d.ts +53 -0
- package/dist/types/ledger/clients/zcash.d.ts.map +1 -0
- package/dist/types/ledger/helpers/dmk.d.ts +24 -0
- package/dist/types/ledger/helpers/dmk.d.ts.map +1 -0
- package/dist/types/ledger/helpers/executeDeviceAction.d.ts +34 -0
- package/dist/types/ledger/helpers/executeDeviceAction.d.ts.map +1 -0
- package/dist/types/ledger/helpers/getLedgerAddress.d.ts +1 -1
- package/dist/types/ledger/helpers/getLedgerAddress.d.ts.map +1 -1
- package/dist/types/ledger/helpers/getLedgerClient.d.ts +10 -3
- package/dist/types/ledger/helpers/getLedgerClient.d.ts.map +1 -1
- package/dist/types/ledger/helpers/index.d.ts +2 -1
- package/dist/types/ledger/helpers/index.d.ts.map +1 -1
- package/dist/types/ledger/helpers/ledgerJsDmkBridge.d.ts +26 -0
- package/dist/types/ledger/helpers/ledgerJsDmkBridge.d.ts.map +1 -0
- package/dist/types/ledger/helpers/ledgerJsDmkTransport.d.ts +13 -0
- package/dist/types/ledger/helpers/ledgerJsDmkTransport.d.ts.map +1 -0
- package/dist/types/ledger/helpers/lowS.d.ts +6 -0
- package/dist/types/ledger/helpers/lowS.d.ts.map +1 -0
- package/dist/types/ledger/helpers/rawTx.d.ts +2 -0
- package/dist/types/ledger/helpers/rawTx.d.ts.map +1 -0
- package/dist/types/ledger/index.d.ts +19 -7
- package/dist/types/ledger/index.d.ts.map +1 -1
- package/dist/types/ledger/types.d.ts +6 -10
- package/dist/types/ledger/types.d.ts.map +1 -1
- package/dist/types/trezor/index.d.ts +2 -2
- package/dist/types/trezor/index.d.ts.map +1 -1
- package/package.json +36 -36
- package/src/ledger/clients/bitcoin.ts +575 -0
- package/src/ledger/clients/cosmos.ts +180 -51
- package/src/ledger/clients/evm.ts +238 -195
- package/src/ledger/clients/near.ts +43 -13
- package/src/ledger/clients/sui.ts +55 -68
- package/src/ledger/clients/thorchain/index.ts +210 -86
- package/src/ledger/clients/thorchain/protocol.ts +166 -0
- package/src/ledger/clients/tron.ts +49 -37
- package/src/ledger/clients/utxo-legacy-adapter.ts +35 -32
- package/src/ledger/clients/utxo-legacy-outputs.ts +122 -0
- package/src/ledger/clients/utxo.ts +108 -206
- package/src/ledger/clients/xrp.ts +39 -23
- package/src/ledger/clients/zcash.ts +440 -0
- package/src/ledger/helpers/dmk.ts +231 -0
- package/src/ledger/helpers/executeDeviceAction.ts +201 -0
- package/src/ledger/helpers/getLedgerClient.ts +101 -72
- package/src/ledger/helpers/index.ts +6 -1
- package/src/ledger/helpers/ledgerJsDmkBridge.ts +109 -0
- package/src/ledger/helpers/ledgerJsDmkTransport.ts +88 -0
- package/src/ledger/helpers/lowS.ts +30 -0
- package/src/ledger/helpers/rawTx.ts +12 -0
- package/src/ledger/index.ts +564 -369
- package/src/ledger/types.ts +15 -10
- package/src/trezor/index.ts +183 -68
- package/dist/chunk-1jpcb30j.js +0 -5
- package/dist/chunk-20wx60bn.js +0 -4
- package/dist/chunk-4tbtxdxa.js +0 -5
- package/dist/chunk-7v7cgm69.js +0 -4
- package/dist/chunk-7v7cgm69.js.map +0 -10
- package/dist/chunk-83g13ck6.js +0 -5
- package/dist/chunk-83g13ck6.js.map +0 -10
- package/dist/chunk-h2r568rz.js +0 -4
- package/dist/chunk-h2r568rz.js.map +0 -10
- package/dist/chunk-xkh15edk.js +0 -4
- package/dist/types/ledger/clients/thorchain/common.d.ts +0 -28
- package/dist/types/ledger/clients/thorchain/common.d.ts.map +0 -1
- package/dist/types/ledger/clients/thorchain/helpers.d.ts +0 -7
- package/dist/types/ledger/clients/thorchain/helpers.d.ts.map +0 -1
- package/dist/types/ledger/clients/thorchain/lib.d.ts +0 -22
- package/dist/types/ledger/clients/thorchain/lib.d.ts.map +0 -1
- package/dist/types/ledger/clients/thorchain/utils.d.ts +0 -2
- package/dist/types/ledger/clients/thorchain/utils.d.ts.map +0 -1
- package/dist/types/ledger/clients/utxo-psbt.d.ts +0 -18
- package/dist/types/ledger/clients/utxo-psbt.d.ts.map +0 -1
- package/dist/types/ledger/cosmosTypes.d.ts +0 -40
- package/dist/types/ledger/cosmosTypes.d.ts.map +0 -1
- package/dist/types/ledger/helpers/getLedgerTransport.d.ts +0 -3
- package/dist/types/ledger/helpers/getLedgerTransport.d.ts.map +0 -1
- package/dist/types/ledger/helpers/ledgerNetworkInfo.d.ts +0 -14
- package/dist/types/ledger/helpers/ledgerNetworkInfo.d.ts.map +0 -1
- package/dist/types/ledger/interfaces/CosmosLedgerInterface.d.ts +0 -14
- package/dist/types/ledger/interfaces/CosmosLedgerInterface.d.ts.map +0 -1
- package/src/ledger/clients/thorchain/common.ts +0 -93
- package/src/ledger/clients/thorchain/helpers.ts +0 -120
- package/src/ledger/clients/thorchain/lib.ts +0 -258
- package/src/ledger/clients/thorchain/utils.ts +0 -69
- package/src/ledger/clients/utxo-psbt.ts +0 -204
- package/src/ledger/cosmosTypes.ts +0 -98
- package/src/ledger/helpers/getLedgerTransport.ts +0 -102
- package/src/ledger/helpers/ledgerNetworkInfo.ts +0 -127
- package/src/ledger/interfaces/CosmosLedgerInterface.ts +0 -67
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import type Transport from "@ledgerhq/hw-transport";
|
|
2
|
-
import { SwapKitError } from "@swapkit/helpers";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
CHUNK_SIZE,
|
|
6
|
-
CLA,
|
|
7
|
-
ERROR_CODE,
|
|
8
|
-
errorCodeToString,
|
|
9
|
-
getVersion,
|
|
10
|
-
INS,
|
|
11
|
-
P1_VALUES,
|
|
12
|
-
P2_VALUES,
|
|
13
|
-
processErrorResponse,
|
|
14
|
-
} from "./common";
|
|
15
|
-
import {
|
|
16
|
-
publicKeyv1,
|
|
17
|
-
publicKeyv2,
|
|
18
|
-
serializePathv1,
|
|
19
|
-
serializePathv2,
|
|
20
|
-
signSendChunkv1,
|
|
21
|
-
signSendChunkv2,
|
|
22
|
-
} from "./helpers";
|
|
23
|
-
|
|
24
|
-
export class THORChainApp {
|
|
25
|
-
transport: Transport;
|
|
26
|
-
versionResponse: any;
|
|
27
|
-
|
|
28
|
-
constructor(transport: any) {
|
|
29
|
-
if (!transport) {
|
|
30
|
-
throw new SwapKitError("wallet_ledger_transport_not_defined");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
this.transport = transport;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
static serializeHRP(hrp: string) {
|
|
37
|
-
if (hrp == null || hrp.length < 3 || hrp.length > 83) {
|
|
38
|
-
throw new SwapKitError("wallet_ledger_invalid_params", { reason: "Invalid HRP" });
|
|
39
|
-
}
|
|
40
|
-
const buf = Buffer.alloc(1 + hrp.length);
|
|
41
|
-
buf.writeUInt8(hrp.length, 0);
|
|
42
|
-
buf.write(hrp, 1);
|
|
43
|
-
return buf;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async serializePath(path: number[]) {
|
|
47
|
-
this.versionResponse = await getVersion(this.transport);
|
|
48
|
-
|
|
49
|
-
if (this.versionResponse.return_code !== ERROR_CODE.NoError) {
|
|
50
|
-
throw this.versionResponse;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
switch (this.versionResponse.major) {
|
|
54
|
-
case 1:
|
|
55
|
-
return serializePathv1(path);
|
|
56
|
-
case 2:
|
|
57
|
-
return serializePathv2(path);
|
|
58
|
-
default:
|
|
59
|
-
return Buffer.alloc(0);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async signGetChunks(path: number[], buffer: Buffer) {
|
|
64
|
-
const serializedPath = await this.serializePath(path);
|
|
65
|
-
|
|
66
|
-
const chunks = [];
|
|
67
|
-
chunks.push(serializedPath);
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < buffer.length; i += CHUNK_SIZE) {
|
|
70
|
-
let end = i + CHUNK_SIZE;
|
|
71
|
-
if (i > buffer.length) {
|
|
72
|
-
end = buffer.length;
|
|
73
|
-
}
|
|
74
|
-
chunks.push(buffer.slice(i, end));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return chunks;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async getVersion() {
|
|
81
|
-
try {
|
|
82
|
-
this.versionResponse = await getVersion(this.transport);
|
|
83
|
-
return this.versionResponse;
|
|
84
|
-
} catch (e) {
|
|
85
|
-
return processErrorResponse(e);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
appInfo() {
|
|
90
|
-
return this.transport.send(0xb0, 0x01, 0, 0).then((response: any) => {
|
|
91
|
-
const returnCode = response.readUInt16BE(response.length - 2);
|
|
92
|
-
|
|
93
|
-
let appName = "";
|
|
94
|
-
let appVersion = "";
|
|
95
|
-
let flagLen = 0;
|
|
96
|
-
let flagsValue = 0;
|
|
97
|
-
|
|
98
|
-
if (response[0] !== 1) {
|
|
99
|
-
// Ledger responds with format ID 1. There is no spec for any format != 1
|
|
100
|
-
return { error_message: "response format ID not recognized", return_code: 0x9001 };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const appNameLen = response[1];
|
|
104
|
-
appName = response.slice(2, 2 + appNameLen).toString("ascii");
|
|
105
|
-
let idx = 2 + appNameLen;
|
|
106
|
-
const appVersionLen = response[idx];
|
|
107
|
-
idx += 1;
|
|
108
|
-
appVersion = response.slice(idx, idx + appVersionLen).toString("ascii");
|
|
109
|
-
idx += appVersionLen;
|
|
110
|
-
const appFlagsLen = response[idx];
|
|
111
|
-
idx += 1;
|
|
112
|
-
flagLen = appFlagsLen;
|
|
113
|
-
flagsValue = response[idx];
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
appName,
|
|
117
|
-
appVersion,
|
|
118
|
-
error_message: errorCodeToString(returnCode),
|
|
119
|
-
flag_onboarded: (flagsValue & 4) !== 0,
|
|
120
|
-
flag_pin_validated: (flagsValue & 128) !== 0,
|
|
121
|
-
flag_recovery: (flagsValue & 1) !== 0,
|
|
122
|
-
flag_signed_mcu_code: (flagsValue & 2) !== 0,
|
|
123
|
-
flagLen,
|
|
124
|
-
flagsValue,
|
|
125
|
-
return_code: returnCode,
|
|
126
|
-
};
|
|
127
|
-
}, processErrorResponse);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
deviceInfo() {
|
|
131
|
-
return this.transport
|
|
132
|
-
.send(0xe0, 0x01, 0, 0, Buffer.from([]), [ERROR_CODE.NoError, 0x6e00])
|
|
133
|
-
.then((response: any) => {
|
|
134
|
-
const errorCodeData = response.slice(-2);
|
|
135
|
-
const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
|
|
136
|
-
|
|
137
|
-
if (returnCode === 0x6e00) {
|
|
138
|
-
return { error_message: "This command is only available in the Dashboard", return_code: returnCode };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const targetId = response.slice(0, 4).toString("hex");
|
|
142
|
-
|
|
143
|
-
let pos = 4;
|
|
144
|
-
const secureElementVersionLen = response[pos];
|
|
145
|
-
pos += 1;
|
|
146
|
-
const seVersion = response.slice(pos, pos + secureElementVersionLen).toString();
|
|
147
|
-
pos += secureElementVersionLen;
|
|
148
|
-
|
|
149
|
-
const flagsLen = response[pos];
|
|
150
|
-
pos += 1;
|
|
151
|
-
const flag = response.slice(pos, pos + flagsLen).toString("hex");
|
|
152
|
-
pos += flagsLen;
|
|
153
|
-
|
|
154
|
-
const mcuVersionLen = response[pos];
|
|
155
|
-
pos += 1;
|
|
156
|
-
// Patch issue in mcu version
|
|
157
|
-
let tmp = response.slice(pos, pos + mcuVersionLen);
|
|
158
|
-
if (tmp[mcuVersionLen - 1] === 0) {
|
|
159
|
-
tmp = response.slice(pos, pos + mcuVersionLen - 1);
|
|
160
|
-
}
|
|
161
|
-
const mcuVersion = tmp.toString();
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
error_message: errorCodeToString(returnCode),
|
|
165
|
-
flag,
|
|
166
|
-
mcuVersion,
|
|
167
|
-
return_code: returnCode,
|
|
168
|
-
seVersion,
|
|
169
|
-
// //
|
|
170
|
-
targetId,
|
|
171
|
-
};
|
|
172
|
-
}, processErrorResponse);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async publicKey(path: number[]) {
|
|
176
|
-
try {
|
|
177
|
-
const serializedPath = await this.serializePath(path);
|
|
178
|
-
|
|
179
|
-
switch (this.versionResponse.major) {
|
|
180
|
-
case 1:
|
|
181
|
-
return publicKeyv1(this, serializedPath);
|
|
182
|
-
case 2: {
|
|
183
|
-
const data = Buffer.concat([THORChainApp.serializeHRP("thor"), serializedPath]);
|
|
184
|
-
return publicKeyv2(this, data);
|
|
185
|
-
}
|
|
186
|
-
default:
|
|
187
|
-
return { error_message: "App Version is not supported", return_code: 0x6400 };
|
|
188
|
-
}
|
|
189
|
-
} catch (e) {
|
|
190
|
-
return processErrorResponse(e);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async getAddressAndPubKey(path: number[], hrp: string, showInDevice = false) {
|
|
195
|
-
try {
|
|
196
|
-
const serializedPath = await this.serializePath(path);
|
|
197
|
-
const data = Buffer.concat([THORChainApp.serializeHRP(hrp), serializedPath]);
|
|
198
|
-
const response = await this.transport.send(
|
|
199
|
-
CLA,
|
|
200
|
-
INS.GET_ADDR_SECP256K1,
|
|
201
|
-
showInDevice ? P1_VALUES.SHOW_ADDRESS_IN_DEVICE : P1_VALUES.ONLY_RETRIEVE,
|
|
202
|
-
0,
|
|
203
|
-
data,
|
|
204
|
-
[ERROR_CODE.NoError],
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
const compressedPk = Buffer.from(response.slice(0, 33));
|
|
208
|
-
const bech32Address = Buffer.from(response.slice(33, -2)).toString();
|
|
209
|
-
const returnCode = response.readUInt16BE(response.length - 2);
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
bech32_address: bech32Address,
|
|
213
|
-
compressed_pk: compressedPk,
|
|
214
|
-
error_message: errorCodeToString(returnCode),
|
|
215
|
-
return_code: returnCode,
|
|
216
|
-
};
|
|
217
|
-
} catch (err) {
|
|
218
|
-
return processErrorResponse(err);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
showAddressAndPubKey(path: number[], hrp: string) {
|
|
223
|
-
return this.getAddressAndPubKey(path, hrp, true);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
signSendChunk(chunkIdx: number, chunkNum: number, chunk: Buffer, txType = P2_VALUES.JSON) {
|
|
227
|
-
switch (this.versionResponse.major) {
|
|
228
|
-
case 1:
|
|
229
|
-
return signSendChunkv1(this, chunkIdx, chunkNum, chunk, txType);
|
|
230
|
-
case 2:
|
|
231
|
-
return signSendChunkv2(this, chunkIdx, chunkNum, chunk, txType);
|
|
232
|
-
default:
|
|
233
|
-
return { error_message: "App Version is not supported", return_code: 0x6400 };
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async sign(path: number[], message: string, txType = P2_VALUES.JSON) {
|
|
238
|
-
const buffer = Buffer.from(message);
|
|
239
|
-
let chunks: Buffer[] = [];
|
|
240
|
-
let response: any;
|
|
241
|
-
try {
|
|
242
|
-
chunks = await this.signGetChunks(path, buffer);
|
|
243
|
-
response = await this.signSendChunk(1, chunks.length, chunks[0] as Buffer, txType);
|
|
244
|
-
} catch (error) {
|
|
245
|
-
processErrorResponse(error);
|
|
246
|
-
}
|
|
247
|
-
let result = { error_message: response.error_message, return_code: response.return_code, signature: null };
|
|
248
|
-
|
|
249
|
-
for (let i = 1; i < chunks.length; i += 1) {
|
|
250
|
-
result = await this.signSendChunk(1 + i, chunks.length, chunks[i] as Buffer, txType);
|
|
251
|
-
if (result.return_code !== ERROR_CODE.NoError) {
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return { error_message: result.error_message, return_code: result.return_code, signature: result.signature };
|
|
257
|
-
}
|
|
258
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { base64 } from "@scure/base";
|
|
2
|
-
import { SwapKitError } from "@swapkit/helpers";
|
|
3
|
-
|
|
4
|
-
export const getSignature = (signatureArray: any) => {
|
|
5
|
-
// Check Type Length Value encoding
|
|
6
|
-
if (signatureArray.length < 64) {
|
|
7
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "Too short" });
|
|
8
|
-
}
|
|
9
|
-
if (signatureArray[0] !== 0x30) {
|
|
10
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "TLV encoding: expected first byte 0x30" });
|
|
11
|
-
}
|
|
12
|
-
if (signatureArray[1] + 2 !== signatureArray.length) {
|
|
13
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "signature length does not match TLV" });
|
|
14
|
-
}
|
|
15
|
-
if (signatureArray[2] !== 0x02) {
|
|
16
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "TLV encoding: expected length type 0x02" });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// r signature
|
|
20
|
-
const rLength = signatureArray[3];
|
|
21
|
-
let rSignature = signatureArray.slice(4, rLength + 4);
|
|
22
|
-
|
|
23
|
-
// Drop leading zero on some 'r' signatures that are 33 bytes.
|
|
24
|
-
if (rSignature.length === 33 && rSignature[0] === 0) {
|
|
25
|
-
rSignature = rSignature.slice(1, 33);
|
|
26
|
-
} else if (rSignature.length === 33) {
|
|
27
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "r too long" });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// add leading zero's to pad to 32 bytes
|
|
31
|
-
while (rSignature.length < 32) {
|
|
32
|
-
rSignature.unshift(0);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// s signature
|
|
36
|
-
if (signatureArray[rLength + 4] !== 0x02) {
|
|
37
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", {
|
|
38
|
-
reason: "TLV encoding: expected length type 0x02 for s",
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const sLength = signatureArray[rLength + 5];
|
|
43
|
-
|
|
44
|
-
if (4 + rLength + 2 + sLength !== signatureArray.length) {
|
|
45
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", {
|
|
46
|
-
reason: "TLV byte lengths do not match message length",
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let sSignature = signatureArray.slice(rLength + 6, signatureArray.length);
|
|
51
|
-
|
|
52
|
-
// Drop leading zero on 's' signatures that are 33 bytes. This shouldn't occur since ledger signs using "Small s" math. But just to be sure...
|
|
53
|
-
if (sSignature.length === 33 && sSignature[0] === 0) {
|
|
54
|
-
sSignature = sSignature.slice(1, 33);
|
|
55
|
-
} else if (sSignature.length === 33) {
|
|
56
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "s too long" });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// add leading zero's to pad to 32 bytes
|
|
60
|
-
while (sSignature.length < 32) {
|
|
61
|
-
sSignature.unshift(0);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (rSignature.length !== 32 || sSignature.length !== 32) {
|
|
65
|
-
throw new SwapKitError("wallet_ledger_invalid_signature", { reason: "must be 32 bytes each" });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return base64.encode(Buffer.concat([rSignature, sSignature]));
|
|
69
|
-
};
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import type Transport from "@ledgerhq/hw-transport";
|
|
2
|
-
import { base64 } from "@scure/base";
|
|
3
|
-
import { HDKey } from "@scure/bip32";
|
|
4
|
-
import {
|
|
5
|
-
Chain,
|
|
6
|
-
type DerivationPathArray,
|
|
7
|
-
derivationPathToString,
|
|
8
|
-
getWalletFormatFor,
|
|
9
|
-
SwapKitError,
|
|
10
|
-
type UTXOChain,
|
|
11
|
-
} from "@swapkit/helpers";
|
|
12
|
-
import type { Transaction } from "@swapkit/utxo-signer";
|
|
13
|
-
|
|
14
|
-
import { applyMissingSpendingMetadata } from "../../helpers/psbt";
|
|
15
|
-
import { getLedgerTransport } from "../helpers/getLedgerTransport";
|
|
16
|
-
|
|
17
|
-
type SupportedCoin = "bitcoin" | "litecoin";
|
|
18
|
-
|
|
19
|
-
const UTXO_CHAIN_FOR_COIN: Record<SupportedCoin, UTXOChain> = { bitcoin: Chain.Bitcoin, litecoin: Chain.Litecoin };
|
|
20
|
-
|
|
21
|
-
type DefaultDescriptorTemplate = "wpkh(@0/**)" | "tr(@0/**)" | "sh(wpkh(@0/**))" | "pkh(@0/**)";
|
|
22
|
-
|
|
23
|
-
function templateForFormat(format: ReturnType<typeof getWalletFormatFor>): DefaultDescriptorTemplate {
|
|
24
|
-
switch (format) {
|
|
25
|
-
case "bech32":
|
|
26
|
-
return "wpkh(@0/**)";
|
|
27
|
-
case "p2sh":
|
|
28
|
-
return "sh(wpkh(@0/**))";
|
|
29
|
-
case "legacy":
|
|
30
|
-
return "pkh(@0/**)";
|
|
31
|
-
default:
|
|
32
|
-
return "wpkh(@0/**)";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function pathToString(path: DerivationPathArray | string): string {
|
|
37
|
-
return typeof path === "string" ? path : derivationPathToString(path);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function normalizeLedgerPath(path: string): string {
|
|
41
|
-
return path.replace(/^m\//, "").replace(/^\/+/, "");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function pathToNumberArray(path: string): number[] {
|
|
45
|
-
return path
|
|
46
|
-
.replace(/^m\//, "")
|
|
47
|
-
.split("/")
|
|
48
|
-
.filter(Boolean)
|
|
49
|
-
.map((p) => {
|
|
50
|
-
const hardened = p.endsWith("'");
|
|
51
|
-
const num = Number.parseInt(hardened ? p.slice(0, -1) : p, 10);
|
|
52
|
-
return hardened ? (num | 0x80000000) >>> 0 : num;
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function dropWitnessUtxoFromLegacyInputs(tx: Transaction) {
|
|
57
|
-
for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {
|
|
58
|
-
const input = tx.getInput(inputIndex);
|
|
59
|
-
|
|
60
|
-
if (input.nonWitnessUtxo && input.witnessUtxo) {
|
|
61
|
-
tx.updateInput(inputIndex, { witnessUtxo: undefined });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function hasBip32Derivation(tx: Transaction, inputIndex: number) {
|
|
67
|
-
const input = tx.getInput(inputIndex) as { bip32Derivation?: Array<unknown> };
|
|
68
|
-
|
|
69
|
-
return Boolean(input.bip32Derivation?.length);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const BaseLedgerPsbtUTXO = ({ chain }: { chain: SupportedCoin }) => {
|
|
73
|
-
return (derivationPathArray?: DerivationPathArray | string, injectedTransport?: Transport) => {
|
|
74
|
-
// Per-call state — each BitcoinPsbtLedger/LitecoinPsbtLedger invocation has its own
|
|
75
|
-
// AppClient and master fingerprint so different consumers (e.g. concurrent MCP
|
|
76
|
-
// sessions on different devices) cannot inherit each other's ledger bindings or xpub.
|
|
77
|
-
let appClient: import("ledger-bitcoin").AppClient | undefined;
|
|
78
|
-
let masterFingerprint: string | undefined;
|
|
79
|
-
|
|
80
|
-
async function getAppClient() {
|
|
81
|
-
if (!appClient) {
|
|
82
|
-
const transport = injectedTransport ?? (await getLedgerTransport());
|
|
83
|
-
const { AppClient } = await import("ledger-bitcoin");
|
|
84
|
-
appClient = new AppClient(transport);
|
|
85
|
-
}
|
|
86
|
-
return appClient;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async function getFingerprint() {
|
|
90
|
-
if (!masterFingerprint) {
|
|
91
|
-
const app = await getAppClient();
|
|
92
|
-
masterFingerprint = await app.getMasterFingerprint();
|
|
93
|
-
}
|
|
94
|
-
return masterFingerprint;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Single-address account: change == index == 0 by default.
|
|
98
|
-
const derivationPath = normalizeLedgerPath(
|
|
99
|
-
derivationPathArray ? pathToString(derivationPathArray) : "84'/0'/0'/0/0",
|
|
100
|
-
);
|
|
101
|
-
const pathSegments = derivationPath.split("/").filter(Boolean);
|
|
102
|
-
const accountPath = pathSegments.slice(0, 3).join("/");
|
|
103
|
-
const leafSegments = pathSegments.slice(3);
|
|
104
|
-
const change = Number(leafSegments[0] ?? 0);
|
|
105
|
-
const addressIndex = Number(leafSegments[1] ?? 0);
|
|
106
|
-
// getWalletFormatFor expects an m/-prefixed path (it reads the purpose
|
|
107
|
-
// from the second segment); derivationPath is normalized without it.
|
|
108
|
-
const format = getWalletFormatFor(`m/${derivationPath}`);
|
|
109
|
-
const template = templateForFormat(format);
|
|
110
|
-
|
|
111
|
-
let cachedAccountXpub: string | undefined;
|
|
112
|
-
let cachedLeafPubkey: Uint8Array | undefined;
|
|
113
|
-
|
|
114
|
-
async function buildPolicy() {
|
|
115
|
-
const app = await getAppClient();
|
|
116
|
-
const fpr = await getFingerprint();
|
|
117
|
-
if (!cachedAccountXpub) {
|
|
118
|
-
cachedAccountXpub = await app.getExtendedPubkey(`m/${accountPath}`);
|
|
119
|
-
}
|
|
120
|
-
const { DefaultWalletPolicy } = await import("ledger-bitcoin");
|
|
121
|
-
const policy = new DefaultWalletPolicy(template, `[${fpr}/${accountPath}]${cachedAccountXpub}`);
|
|
122
|
-
return { app, fpr, policy, xpub: cachedAccountXpub };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async function getLeafPubkey() {
|
|
126
|
-
if (!cachedLeafPubkey) {
|
|
127
|
-
const { xpub } = await buildPolicy();
|
|
128
|
-
const accountKey = HDKey.fromExtendedKey(xpub);
|
|
129
|
-
const leaf = accountKey.derive(`m/${change}/${addressIndex}`);
|
|
130
|
-
if (!leaf.publicKey) {
|
|
131
|
-
throw new SwapKitError("wallet_ledger_get_address_error", {
|
|
132
|
-
message: `Cannot derive leaf pubkey for ${chain}`,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
cachedLeafPubkey = leaf.publicKey;
|
|
136
|
-
}
|
|
137
|
-
return cachedLeafPubkey;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return {
|
|
141
|
-
connect: async () => {
|
|
142
|
-
await getAppClient();
|
|
143
|
-
},
|
|
144
|
-
getAddress: async () => {
|
|
145
|
-
const { app, policy } = await buildPolicy();
|
|
146
|
-
const address = await app.getWalletAddress(policy, null, change, addressIndex, false);
|
|
147
|
-
if (!address) {
|
|
148
|
-
throw new SwapKitError("wallet_ledger_get_address_error", {
|
|
149
|
-
message: `Cannot get ${chain} address from ledger derivation path: ${derivationPath}`,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return address;
|
|
153
|
-
},
|
|
154
|
-
getExtendedPublicKey: async (path = `m/${accountPath}`) => {
|
|
155
|
-
const app = await getAppClient();
|
|
156
|
-
return app.getExtendedPubkey(`m/${normalizeLedgerPath(path)}`);
|
|
157
|
-
},
|
|
158
|
-
getPublicKey: getLeafPubkey,
|
|
159
|
-
signTransaction: async (tx: Transaction): Promise<Transaction> => {
|
|
160
|
-
const { app, policy, fpr } = await buildPolicy();
|
|
161
|
-
const fingerprintBE = Number.parseInt(fpr, 16) >>> 0;
|
|
162
|
-
const pathNumbers = pathToNumberArray(derivationPath);
|
|
163
|
-
const missingDerivationIndexes = Array.from({ length: tx.inputsLength }, (_, inputIndex) => inputIndex).filter(
|
|
164
|
-
(inputIndex) => !hasBip32Derivation(tx, inputIndex),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
if (missingDerivationIndexes.length > 0) {
|
|
168
|
-
const leafPubkey = await getLeafPubkey();
|
|
169
|
-
|
|
170
|
-
// Fallback for PSBTs that do not include per-input HD key origins.
|
|
171
|
-
for (const inputIndex of missingDerivationIndexes) {
|
|
172
|
-
tx.updateInput(inputIndex, {
|
|
173
|
-
bip32Derivation: [[leafPubkey, { fingerprint: fingerprintBE, path: pathNumbers }]],
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (format === "legacy") {
|
|
179
|
-
dropWitnessUtxoFromLegacyInputs(tx);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (format === "p2sh") {
|
|
183
|
-
await applyMissingSpendingMetadata({
|
|
184
|
-
chain: UTXO_CHAIN_FOR_COIN[chain],
|
|
185
|
-
publicKey: await getLeafPubkey(),
|
|
186
|
-
tx,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const psbtB64 = base64.encode(tx.toPSBT(0));
|
|
191
|
-
const sigs = await app.signPsbt(psbtB64, policy, null);
|
|
192
|
-
|
|
193
|
-
for (const [idx, partial] of sigs) {
|
|
194
|
-
tx.updateInput(idx, { partialSig: [[new Uint8Array(partial.pubkey), new Uint8Array(partial.signature)]] });
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return tx;
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
};
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
export const BitcoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: "bitcoin" });
|
|
204
|
-
export const LitecoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: "litecoin" });
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { SwapKitError } from "@swapkit/helpers";
|
|
2
|
-
|
|
3
|
-
export interface Coin {
|
|
4
|
-
readonly denom: string;
|
|
5
|
-
readonly amount: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface EncodeObject {
|
|
9
|
-
readonly typeUrl: string;
|
|
10
|
-
readonly value: any;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface AminoMsg {
|
|
14
|
-
readonly type: string;
|
|
15
|
-
readonly value: any;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface AminoMsgSend extends AminoMsg {
|
|
19
|
-
readonly type: "cosmos-sdk/MsgSend";
|
|
20
|
-
readonly value: {
|
|
21
|
-
/** Bech32 account address */
|
|
22
|
-
readonly from_address: string;
|
|
23
|
-
/** Bech32 account address */
|
|
24
|
-
readonly to_address: string;
|
|
25
|
-
readonly amount: readonly Coin[];
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AminoConverter {
|
|
30
|
-
readonly aminoType: string;
|
|
31
|
-
readonly toAmino: (value: any) => any;
|
|
32
|
-
readonly fromAmino: (value: any) => any;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** A map from protobuf type URL to the AminoConverter implementation if supported on chain */
|
|
36
|
-
export type AminoConverters = Record<string, AminoConverter | "not_supported_by_chain">;
|
|
37
|
-
|
|
38
|
-
function isAminoConverter(
|
|
39
|
-
converter: [string, AminoConverter | "not_supported_by_chain"],
|
|
40
|
-
): converter is [string, AminoConverter] {
|
|
41
|
-
return typeof converter[1] !== "string";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* A map from Stargate message types as used in the messages's `Any` type
|
|
46
|
-
* to Amino types.
|
|
47
|
-
*/
|
|
48
|
-
export class AminoTypes {
|
|
49
|
-
// The map type here ensures uniqueness of the protobuf type URL in the key.
|
|
50
|
-
// There is no uniqueness guarantee of the Amino type identifier in the type
|
|
51
|
-
// system or constructor. Instead it's the user's responsibility to ensure
|
|
52
|
-
// there is no overlap when fromAmino is called.
|
|
53
|
-
private readonly register: Record<string, AminoConverter | "not_supported_by_chain">;
|
|
54
|
-
|
|
55
|
-
constructor(types: AminoConverters) {
|
|
56
|
-
this.register = types;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
toAmino({ typeUrl, value }: EncodeObject): AminoMsg {
|
|
60
|
-
const converter = this.register[typeUrl];
|
|
61
|
-
if (converter === "not_supported_by_chain") {
|
|
62
|
-
throw new SwapKitError("wallet_ledger_chain_not_supported", {
|
|
63
|
-
reason: `The message type '${typeUrl}' cannot be signed using the Amino JSON sign mode because this is not supported by chain.`,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
if (!converter) {
|
|
67
|
-
throw new SwapKitError("wallet_ledger_invalid_params", {
|
|
68
|
-
reason: `Type URL '${typeUrl}' does not exist in the Amino message type register.`,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return { type: converter.aminoType, value: converter.toAmino(value) };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
fromAmino({ type, value }: AminoMsg): EncodeObject {
|
|
75
|
-
const matches = Object.entries(this.register)
|
|
76
|
-
.filter(isAminoConverter)
|
|
77
|
-
.filter(([_typeUrl, { aminoType }]) => aminoType === type);
|
|
78
|
-
|
|
79
|
-
if (matches.length === 0) {
|
|
80
|
-
throw new SwapKitError("wallet_ledger_invalid_params", {
|
|
81
|
-
reason: `Amino type identifier '${type}' does not exist in the Amino message type register.`,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (matches.length > 1) {
|
|
86
|
-
throw new SwapKitError("wallet_ledger_invalid_params", {
|
|
87
|
-
reason: `Multiple types are registered with Amino type identifier '${type}': '${matches
|
|
88
|
-
.map(([key, _value]) => key)
|
|
89
|
-
.sort()
|
|
90
|
-
.join("', '")}'. Thus fromAmino cannot be performed.`,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const [typeUrl, converter] = matches[0] as [string, AminoConverter];
|
|
95
|
-
|
|
96
|
-
return { typeUrl, value: converter.fromAmino(value) };
|
|
97
|
-
}
|
|
98
|
-
}
|