@onekeyfe/hwk-ledger-adapter 1.2.3-alpha.1 → 1.2.3-alpha.2
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/index.d.mts +14 -12
- package/dist/index.d.ts +14 -12
- package/dist/index.js +684 -559
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +679 -554
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
DeviceJobQueue,
|
|
7
7
|
EConnectorInteraction,
|
|
8
8
|
HardwareErrorCode as HardwareErrorCode3,
|
|
9
|
-
|
|
9
|
+
OperationRegistry,
|
|
10
10
|
SDK,
|
|
11
11
|
TypedEventEmitter,
|
|
12
12
|
UI_REQUEST,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
createHwkError,
|
|
17
17
|
deriveDeviceFingerprint,
|
|
18
18
|
failure as failure2,
|
|
19
|
-
|
|
19
|
+
isHardwareOperationId,
|
|
20
20
|
isHwkRecoveryHint,
|
|
21
21
|
operationMayHaveCompletedParams,
|
|
22
22
|
rehydrateConnectorError,
|
|
@@ -530,7 +530,7 @@ var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "d
|
|
|
530
530
|
function createAllNetworkGetAddress({
|
|
531
531
|
callChain,
|
|
532
532
|
getChainFingerprint,
|
|
533
|
-
|
|
533
|
+
retainOperation,
|
|
534
534
|
errorToFailure
|
|
535
535
|
}) {
|
|
536
536
|
return async function allNetworkGetAddress(connectId, _deviceId, params) {
|
|
@@ -539,19 +539,19 @@ function createAllNetworkGetAddress({
|
|
|
539
539
|
connectId,
|
|
540
540
|
itemCount: params.bundle.length
|
|
541
541
|
});
|
|
542
|
-
const target = resolveHardwareOperationTarget(connectId, params.
|
|
542
|
+
const target = resolveHardwareOperationTarget(connectId, params.operationId, "ledger");
|
|
543
543
|
if (!target.success) return target;
|
|
544
544
|
const effectiveTargetId = target.payload.targetId ?? "";
|
|
545
|
-
let
|
|
545
|
+
let releaseOperationRetention;
|
|
546
546
|
try {
|
|
547
|
-
|
|
547
|
+
releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
|
|
548
548
|
} catch (error) {
|
|
549
549
|
return errorToFailure(error);
|
|
550
550
|
}
|
|
551
551
|
const installContext = {};
|
|
552
552
|
const commonParams = {
|
|
553
553
|
autoInstallApp: params.autoInstallApp,
|
|
554
|
-
|
|
554
|
+
operationId: target.payload.operationId,
|
|
555
555
|
knownConnections: params.knownConnections,
|
|
556
556
|
extra: params.extra,
|
|
557
557
|
allowDeviceSelection: params.allowDeviceSelection
|
|
@@ -602,7 +602,7 @@ function createAllNetworkGetAddress({
|
|
|
602
602
|
});
|
|
603
603
|
return result;
|
|
604
604
|
} finally {
|
|
605
|
-
|
|
605
|
+
releaseOperationRetention?.();
|
|
606
606
|
}
|
|
607
607
|
};
|
|
608
608
|
}
|
|
@@ -611,7 +611,7 @@ function isTopLevelAllNetworkFailure(response) {
|
|
|
611
611
|
return false;
|
|
612
612
|
}
|
|
613
613
|
const code = response.payload?.code;
|
|
614
|
-
return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.DeviceDisconnected || code === HardwareErrorCode2.OperationTimeout || code === HardwareErrorCode2.TransportError || code === HardwareErrorCode2.
|
|
614
|
+
return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.DeviceDisconnected || code === HardwareErrorCode2.OperationTimeout || code === HardwareErrorCode2.TransportError || code === HardwareErrorCode2.OperationEnded || code === HardwareErrorCode2.OperationNotFound || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
|
|
615
615
|
}
|
|
616
616
|
function getItemDeviceId(item) {
|
|
617
617
|
const { deviceId } = item;
|
|
@@ -774,26 +774,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
774
774
|
constructor(connector, options) {
|
|
775
775
|
this.vendor = "ledger";
|
|
776
776
|
this.emitter = new TypedEventEmitter();
|
|
777
|
-
this.
|
|
777
|
+
this._operations = new OperationRegistry({
|
|
778
778
|
vendor: "ledger",
|
|
779
|
-
onEnded: (
|
|
780
|
-
const binding = this.
|
|
779
|
+
onEnded: (operation, reason) => {
|
|
780
|
+
const binding = this._pendingOperationBindings.get(operation.operationId);
|
|
781
781
|
if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
|
|
782
782
|
this._finishBleBinding("cancelled");
|
|
783
783
|
}
|
|
784
|
-
this.
|
|
785
|
-
this.emitter.emit(SDK.
|
|
786
|
-
type: SDK.
|
|
787
|
-
payload: {
|
|
784
|
+
this._pendingOperationBindings.delete(operation.operationId);
|
|
785
|
+
this.emitter.emit(SDK.OPERATION_ENDED, {
|
|
786
|
+
type: SDK.OPERATION_ENDED,
|
|
787
|
+
payload: { operationId: operation.operationId, reason }
|
|
788
788
|
});
|
|
789
789
|
if (reason === "timeout") {
|
|
790
|
-
void this.
|
|
790
|
+
void this._releaseOperationConnection(operation);
|
|
791
791
|
}
|
|
792
792
|
}
|
|
793
793
|
});
|
|
794
794
|
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
795
795
|
this._sessions = /* @__PURE__ */ new Map();
|
|
796
|
-
this.
|
|
796
|
+
this._pendingOperationBindings = /* @__PURE__ */ new Map();
|
|
797
797
|
this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
|
|
798
798
|
this._uiRegistry = new UiRequestRegistry();
|
|
799
799
|
// BTC App rejects account index >= 100 unless display=true. Cached per
|
|
@@ -814,8 +814,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
814
814
|
this._stateGeneration = 0;
|
|
815
815
|
this._connectorTeardownTail = Promise.resolve();
|
|
816
816
|
this._pendingConnectorTeardowns = 0;
|
|
817
|
-
this.
|
|
818
|
-
this.
|
|
817
|
+
this._activeOperationJobs = /* @__PURE__ */ new Set();
|
|
818
|
+
this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
|
|
819
819
|
this._installProgressLastEmittedValue = -Infinity;
|
|
820
820
|
this.allNetworkGetAddress = createAllNetworkGetAddress({
|
|
821
821
|
callChain: this.callChain.bind(this),
|
|
@@ -830,7 +830,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
830
830
|
return this.errorToFailure(error);
|
|
831
831
|
}
|
|
832
832
|
},
|
|
833
|
-
|
|
833
|
+
retainOperation: (operationId) => this._operations.retain(operationId),
|
|
834
834
|
errorToFailure: (error) => this.errorToFailure(error)
|
|
835
835
|
});
|
|
836
836
|
// ---------------------------------------------------------------------------
|
|
@@ -858,11 +858,11 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
858
858
|
});
|
|
859
859
|
};
|
|
860
860
|
this.deviceDisconnectHandler = (data) => {
|
|
861
|
-
const
|
|
862
|
-
if (
|
|
863
|
-
this.
|
|
861
|
+
const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
|
|
862
|
+
if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
|
|
863
|
+
this._pendingOperationDisconnects.add(activeOperation.operationId);
|
|
864
864
|
} else {
|
|
865
|
-
this.
|
|
865
|
+
this._operations.endByConnectionKey(data.connectId, "disconnect");
|
|
866
866
|
}
|
|
867
867
|
this._discoveredDevices.delete(data.connectId);
|
|
868
868
|
this._sessions.delete(data.connectId);
|
|
@@ -965,7 +965,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
965
965
|
const sessionIds = new Set(this._sessions.values());
|
|
966
966
|
this._stateGeneration += 1;
|
|
967
967
|
this._finishBleBinding("cancelled");
|
|
968
|
-
this.
|
|
968
|
+
this._operations.endAll("runtime-reset");
|
|
969
969
|
this._doConnectAbortController?.abort();
|
|
970
970
|
this._discoveredDevices.clear();
|
|
971
971
|
this._sessions.clear();
|
|
@@ -1074,8 +1074,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1074
1074
|
}));
|
|
1075
1075
|
}
|
|
1076
1076
|
// USB single-session invariant: evict all sessions, best-effort (see connectDevice).
|
|
1077
|
-
async _evictAllSessions(
|
|
1078
|
-
this.
|
|
1077
|
+
async _evictAllSessions(preserveOperationId) {
|
|
1078
|
+
this._operations.endAll("explicit", preserveOperationId);
|
|
1079
1079
|
if (this._sessions.size === 0) return;
|
|
1080
1080
|
const stale = [...this._sessions.values()];
|
|
1081
1081
|
this._sessions.clear();
|
|
@@ -1094,11 +1094,129 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1094
1094
|
});
|
|
1095
1095
|
}
|
|
1096
1096
|
async connectDevice(searchTargetId) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1097
|
+
try {
|
|
1098
|
+
return await this._jobQueue.enqueue(
|
|
1099
|
+
searchTargetId || "__ledger_connect__",
|
|
1100
|
+
async () => {
|
|
1101
|
+
const connected = await this._connectTarget(searchTargetId);
|
|
1102
|
+
if (!connected.success) return connected;
|
|
1103
|
+
return this._createOperation(searchTargetId, connected.payload);
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
label: "connectDevice",
|
|
1107
|
+
rejectIfBusy: true,
|
|
1108
|
+
busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
|
|
1109
|
+
}
|
|
1110
|
+
);
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
return this.errorToFailure(error);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async bindBleDevice(params) {
|
|
1116
|
+
if (params.identity.vendor !== "ledger" || !params.identity.value) {
|
|
1117
|
+
return failure2(HardwareErrorCode3.InvalidParams, "Ledger wallet identity is required");
|
|
1118
|
+
}
|
|
1119
|
+
const { chain, value: expectedFingerprint } = params.identity;
|
|
1120
|
+
try {
|
|
1121
|
+
return await this._jobQueue.enqueue(
|
|
1122
|
+
expectedFingerprint,
|
|
1123
|
+
async (signal) => {
|
|
1124
|
+
if (!this.getAvailableTransports().includes("ble")) {
|
|
1125
|
+
throw createHwkError({
|
|
1126
|
+
code: HardwareErrorCode3.TransportNotAvailable,
|
|
1127
|
+
message: "Ledger Bluetooth transport is not available"
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
this._activeConnectionType = "ble";
|
|
1131
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1132
|
+
const attempt = {
|
|
1133
|
+
extra: params.extra,
|
|
1134
|
+
bindingReason: "manual-rebind"
|
|
1135
|
+
};
|
|
1136
|
+
try {
|
|
1137
|
+
for (; ; ) {
|
|
1138
|
+
const connectId = await this._connectFirstOrSelect(
|
|
1139
|
+
[],
|
|
1140
|
+
void 0,
|
|
1141
|
+
true,
|
|
1142
|
+
void 0,
|
|
1143
|
+
attempt,
|
|
1144
|
+
signal
|
|
1145
|
+
);
|
|
1146
|
+
const sessionId = this._sessions.get(connectId);
|
|
1147
|
+
if (!sessionId) {
|
|
1148
|
+
throw createHwkError({
|
|
1149
|
+
code: HardwareErrorCode3.DeviceDisconnected,
|
|
1150
|
+
message: "Selected Ledger connection ended"
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
let saved = false;
|
|
1154
|
+
try {
|
|
1155
|
+
const installContext = {
|
|
1156
|
+
connection: { connectId, sessionId }
|
|
1157
|
+
};
|
|
1158
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1159
|
+
chain,
|
|
1160
|
+
(method, callParams) => this._runConnectorCall(
|
|
1161
|
+
connectId,
|
|
1162
|
+
method,
|
|
1163
|
+
callParams,
|
|
1164
|
+
signal,
|
|
1165
|
+
void 0,
|
|
1166
|
+
void 0,
|
|
1167
|
+
{ autoInstallApp: true },
|
|
1168
|
+
installContext
|
|
1169
|
+
)
|
|
1170
|
+
);
|
|
1171
|
+
if (fingerprint !== expectedFingerprint) {
|
|
1172
|
+
attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
1173
|
+
attempt.rejectedConnectIds.add(connectId);
|
|
1174
|
+
attempt.rejectedConnectId = connectId;
|
|
1175
|
+
} else {
|
|
1176
|
+
const persisted = await this._publishVerifiedBleBinding(
|
|
1177
|
+
connectId,
|
|
1178
|
+
chain,
|
|
1179
|
+
fingerprint,
|
|
1180
|
+
attempt,
|
|
1181
|
+
void 0,
|
|
1182
|
+
signal
|
|
1183
|
+
);
|
|
1184
|
+
if (!persisted) {
|
|
1185
|
+
throw createHwkError({
|
|
1186
|
+
code: HardwareErrorCode3.UnknownError,
|
|
1187
|
+
message: "Bluetooth binding could not be saved",
|
|
1188
|
+
origin: "host"
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
saved = true;
|
|
1192
|
+
return success(connectId);
|
|
1193
|
+
}
|
|
1194
|
+
} finally {
|
|
1195
|
+
if (!saved && this._sessions.get(connectId) === sessionId) {
|
|
1196
|
+
this._sessions.delete(connectId);
|
|
1197
|
+
const teardown = this._runConnectorTeardown(
|
|
1198
|
+
() => this.connector.disconnect(sessionId)
|
|
1199
|
+
).catch(() => void 0);
|
|
1200
|
+
if (!signal.aborted) await teardown;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1206
|
+
throw error;
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
label: "bindBleDevice",
|
|
1211
|
+
rejectIfBusy: true,
|
|
1212
|
+
busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
|
|
1213
|
+
}
|
|
1214
|
+
);
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
return this.errorToFailure(error);
|
|
1217
|
+
}
|
|
1100
1218
|
}
|
|
1101
|
-
async
|
|
1219
|
+
async acquireOperation(connectId, context) {
|
|
1102
1220
|
try {
|
|
1103
1221
|
return await this._jobQueue.enqueue(
|
|
1104
1222
|
connectId || "__ledger_acquire__",
|
|
@@ -1111,8 +1229,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1111
1229
|
await this._ensureDevicePermission(target, void 0, signal);
|
|
1112
1230
|
const attempt = {
|
|
1113
1231
|
...context,
|
|
1114
|
-
extra: context.extra ? { ...context.extra } : void 0
|
|
1115
|
-
previousConnectId: target
|
|
1232
|
+
extra: context.extra ? { ...context.extra } : void 0
|
|
1116
1233
|
};
|
|
1117
1234
|
try {
|
|
1118
1235
|
const resolvedConnectId = await this.ensureConnected(
|
|
@@ -1123,9 +1240,9 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1123
1240
|
attempt
|
|
1124
1241
|
);
|
|
1125
1242
|
_LedgerAdapter._throwIfAborted(signal);
|
|
1126
|
-
const result = this.
|
|
1243
|
+
const result = this._createOperation(connectId, resolvedConnectId);
|
|
1127
1244
|
if (result.success && attempt.selectedConnection) {
|
|
1128
|
-
this.
|
|
1245
|
+
this._pendingOperationBindings.set(result.payload, attempt);
|
|
1129
1246
|
}
|
|
1130
1247
|
return result;
|
|
1131
1248
|
} catch (error) {
|
|
@@ -1134,19 +1251,19 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1134
1251
|
}
|
|
1135
1252
|
},
|
|
1136
1253
|
{
|
|
1137
|
-
label: "
|
|
1254
|
+
label: "acquireOperation",
|
|
1138
1255
|
rejectIfBusy: true,
|
|
1139
|
-
busyError: _LedgerAdapter._createDeviceBusyError("
|
|
1256
|
+
busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
|
|
1140
1257
|
}
|
|
1141
1258
|
);
|
|
1142
1259
|
} catch (error) {
|
|
1143
1260
|
return this.errorToFailure(error);
|
|
1144
1261
|
}
|
|
1145
1262
|
}
|
|
1146
|
-
|
|
1147
|
-
this.
|
|
1263
|
+
_createOperation(searchTargetId, resolvedConnectId) {
|
|
1264
|
+
this._operations.endByConnectionKey(resolvedConnectId, "explicit");
|
|
1148
1265
|
const sessionId = this._sessions.get(resolvedConnectId);
|
|
1149
|
-
if (sessionId) this.
|
|
1266
|
+
if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
|
|
1150
1267
|
const device = this._discoveredDevices.get(resolvedConnectId) ?? {
|
|
1151
1268
|
vendor: "ledger",
|
|
1152
1269
|
model: "unknown",
|
|
@@ -1155,15 +1272,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1155
1272
|
connectId: resolvedConnectId,
|
|
1156
1273
|
connectionType: this.connector.connectionType
|
|
1157
1274
|
};
|
|
1158
|
-
const
|
|
1275
|
+
const operation = this._operations.create({
|
|
1159
1276
|
searchTargetId,
|
|
1160
1277
|
connectId: resolvedConnectId,
|
|
1161
1278
|
device,
|
|
1162
1279
|
connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
|
|
1163
1280
|
});
|
|
1164
|
-
return success(
|
|
1281
|
+
return success(operation.operationId);
|
|
1165
1282
|
}
|
|
1166
|
-
async _connectTarget(connectId,
|
|
1283
|
+
async _connectTarget(connectId, preserveOperationId, signal) {
|
|
1167
1284
|
debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
|
|
1168
1285
|
try {
|
|
1169
1286
|
this._assertConnectorReady("connectDevice");
|
|
@@ -1179,21 +1296,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1179
1296
|
await this._ensureDevicePermission(connectId, void 0, signal);
|
|
1180
1297
|
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1181
1298
|
if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
|
|
1182
|
-
await this._evictAllSessions(
|
|
1299
|
+
await this._evictAllSessions(preserveOperationId);
|
|
1183
1300
|
} else if (this._isBleConnection()) {
|
|
1184
1301
|
const previousSessionId = this._sessions.get(connectId);
|
|
1185
|
-
this.
|
|
1302
|
+
this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
|
|
1186
1303
|
if (previousSessionId) {
|
|
1187
|
-
this.
|
|
1188
|
-
previousSessionId,
|
|
1189
|
-
"explicit",
|
|
1190
|
-
preserveInteractionId
|
|
1191
|
-
);
|
|
1304
|
+
this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
|
|
1192
1305
|
this._sessions.delete(connectId);
|
|
1193
1306
|
await this.connector.disconnect(previousSessionId).catch(() => void 0);
|
|
1194
1307
|
}
|
|
1195
1308
|
} else {
|
|
1196
|
-
await this._evictAllSessions(
|
|
1309
|
+
await this._evictAllSessions(preserveOperationId);
|
|
1197
1310
|
}
|
|
1198
1311
|
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1199
1312
|
const stateGeneration = this._stateGeneration;
|
|
@@ -1230,37 +1343,37 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1230
1343
|
return failureResult;
|
|
1231
1344
|
}
|
|
1232
1345
|
}
|
|
1233
|
-
async
|
|
1234
|
-
const
|
|
1235
|
-
if (!
|
|
1236
|
-
this.
|
|
1346
|
+
async releaseOperation(operationId) {
|
|
1347
|
+
const operation = this._operations.find(operationId);
|
|
1348
|
+
if (!operation) {
|
|
1349
|
+
this._operations.resolve(operationId);
|
|
1237
1350
|
return;
|
|
1238
1351
|
}
|
|
1239
|
-
const
|
|
1240
|
-
if (!
|
|
1241
|
-
const { connectId } =
|
|
1352
|
+
const endedOperation = this._operations.end(operationId, "explicit");
|
|
1353
|
+
if (!endedOperation) return;
|
|
1354
|
+
const { connectId } = operation;
|
|
1242
1355
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1243
|
-
method: "
|
|
1356
|
+
method: "releaseOperation",
|
|
1244
1357
|
connectId,
|
|
1245
1358
|
params: { connectId }
|
|
1246
1359
|
});
|
|
1247
1360
|
try {
|
|
1248
|
-
await this.
|
|
1249
|
-
debugLog("[LedgerAdapter][RES]", { method: "
|
|
1361
|
+
await this._releaseOperationConnection(endedOperation);
|
|
1362
|
+
debugLog("[LedgerAdapter][RES]", { method: "releaseOperation", success: true });
|
|
1250
1363
|
} catch (err) {
|
|
1251
1364
|
const e = err;
|
|
1252
1365
|
debugLog("[LedgerAdapter][RES]", {
|
|
1253
|
-
method: "
|
|
1366
|
+
method: "releaseOperation",
|
|
1254
1367
|
success: false,
|
|
1255
1368
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1256
1369
|
});
|
|
1257
1370
|
throw err;
|
|
1258
1371
|
}
|
|
1259
1372
|
}
|
|
1260
|
-
async
|
|
1373
|
+
async _releaseOperationConnection(operation) {
|
|
1261
1374
|
const sessionIds = /* @__PURE__ */ new Set();
|
|
1262
1375
|
for (const [connectId, sessionId] of this._sessions) {
|
|
1263
|
-
if (connectId ===
|
|
1376
|
+
if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
|
|
1264
1377
|
this._sessions.delete(connectId);
|
|
1265
1378
|
sessionIds.add(sessionId);
|
|
1266
1379
|
}
|
|
@@ -1271,16 +1384,16 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1271
1384
|
);
|
|
1272
1385
|
}
|
|
1273
1386
|
}
|
|
1274
|
-
async
|
|
1275
|
-
const
|
|
1276
|
-
if (!
|
|
1277
|
-
this._discoveredDevices.delete(
|
|
1278
|
-
await this.
|
|
1387
|
+
async _releaseLostOperationConnection(operationId) {
|
|
1388
|
+
const endedOperation = this._operations.end(operationId, "disconnect");
|
|
1389
|
+
if (!endedOperation) return;
|
|
1390
|
+
this._discoveredDevices.delete(endedOperation.connectId);
|
|
1391
|
+
await this._releaseOperationConnection(endedOperation);
|
|
1279
1392
|
}
|
|
1280
|
-
async getDeviceInfo(
|
|
1393
|
+
async getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
1281
1394
|
let connectId;
|
|
1282
1395
|
try {
|
|
1283
|
-
connectId =
|
|
1396
|
+
connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
1284
1397
|
} catch (error) {
|
|
1285
1398
|
return this.errorToFailure(error);
|
|
1286
1399
|
}
|
|
@@ -1321,9 +1434,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1321
1434
|
throw err;
|
|
1322
1435
|
}
|
|
1323
1436
|
}
|
|
1324
|
-
getSupportedChains() {
|
|
1325
|
-
return ["evm", "btc", "sol", "tron", "zcash"];
|
|
1326
|
-
}
|
|
1327
1437
|
// ---------------------------------------------------------------------------
|
|
1328
1438
|
// Chain call helper
|
|
1329
1439
|
// ---------------------------------------------------------------------------
|
|
@@ -1358,7 +1468,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1358
1468
|
if (params && typeof params === "object") {
|
|
1359
1469
|
const {
|
|
1360
1470
|
autoInstallApp,
|
|
1361
|
-
|
|
1471
|
+
operationId,
|
|
1362
1472
|
passphraseState: _passphraseState,
|
|
1363
1473
|
useEmptyPassphrase: _useEmptyPassphrase,
|
|
1364
1474
|
knownConnections,
|
|
@@ -1369,7 +1479,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1369
1479
|
return {
|
|
1370
1480
|
commonParams: {
|
|
1371
1481
|
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
|
|
1372
|
-
|
|
1482
|
+
operationId: typeof operationId === "string" ? operationId : void 0,
|
|
1373
1483
|
knownConnections,
|
|
1374
1484
|
extra,
|
|
1375
1485
|
allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
|
|
@@ -1698,6 +1808,20 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1698
1808
|
this.emitter.off(event, listener);
|
|
1699
1809
|
}
|
|
1700
1810
|
cancel(connectId) {
|
|
1811
|
+
const namedOperationIsLive = (id) => {
|
|
1812
|
+
try {
|
|
1813
|
+
this._operations.resolve(id);
|
|
1814
|
+
return true;
|
|
1815
|
+
} catch {
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
if (isHardwareOperationId(connectId) && !namedOperationIsLive(connectId)) {
|
|
1820
|
+
debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
|
|
1821
|
+
connectId
|
|
1822
|
+
});
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1701
1825
|
const userAbortReason = Object.assign(new Error("User aborted operation"), {
|
|
1702
1826
|
code: HardwareErrorCode3.UserAborted,
|
|
1703
1827
|
_tag: ERROR_TAG.UserAborted
|
|
@@ -1708,29 +1832,29 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1708
1832
|
this._lastCancelReason = void 0;
|
|
1709
1833
|
}
|
|
1710
1834
|
}, 2e3);
|
|
1711
|
-
this._uiRegistry.cancel();
|
|
1712
|
-
this._finishBleBinding("cancelled");
|
|
1713
1835
|
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
1714
|
-
let
|
|
1715
|
-
if (
|
|
1716
|
-
|
|
1717
|
-
} else if (!connectId &&
|
|
1718
|
-
|
|
1836
|
+
let operationId;
|
|
1837
|
+
if (isHardwareOperationId(connectId)) {
|
|
1838
|
+
operationId = connectId;
|
|
1839
|
+
} else if (!connectId && isHardwareOperationId(activeJobId)) {
|
|
1840
|
+
operationId = activeJobId;
|
|
1719
1841
|
}
|
|
1720
1842
|
let resolvedConnectId = connectId;
|
|
1721
|
-
if (
|
|
1843
|
+
if (operationId) {
|
|
1722
1844
|
try {
|
|
1723
|
-
resolvedConnectId = this.
|
|
1845
|
+
resolvedConnectId = this._operations.resolve(operationId).connectId;
|
|
1724
1846
|
} catch {
|
|
1725
1847
|
resolvedConnectId = void 0;
|
|
1726
1848
|
}
|
|
1727
1849
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
const
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1850
|
+
this._uiRegistry.cancel();
|
|
1851
|
+
this._finishBleBinding("cancelled");
|
|
1852
|
+
const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
|
|
1853
|
+
const queueKey = operationId ?? (activeJobId === connectId ? connectId : interactionForPhysicalId?.operationId) ?? connectId;
|
|
1854
|
+
const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
|
|
1855
|
+
if (!connectId) this._pendingOperationBindings.clear();
|
|
1856
|
+
else if (pendingOperationId) {
|
|
1857
|
+
this._pendingOperationBindings.delete(pendingOperationId);
|
|
1734
1858
|
}
|
|
1735
1859
|
if (queueKey) {
|
|
1736
1860
|
this._jobQueue.cancelActiveAndPending(queueKey, userAbortReason);
|
|
@@ -1764,10 +1888,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1764
1888
|
formatDeviceMismatchError(deviceId, fingerprint)
|
|
1765
1889
|
);
|
|
1766
1890
|
}
|
|
1767
|
-
if (
|
|
1768
|
-
const
|
|
1891
|
+
if (isHardwareOperationId(connectId)) {
|
|
1892
|
+
const operation = this._operations.resolve(connectId);
|
|
1769
1893
|
await this._publishVerifiedBleBinding(
|
|
1770
|
-
|
|
1894
|
+
operation.connectId,
|
|
1771
1895
|
chain,
|
|
1772
1896
|
fingerprint,
|
|
1773
1897
|
void 0,
|
|
@@ -1782,39 +1906,53 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1782
1906
|
}
|
|
1783
1907
|
}
|
|
1784
1908
|
/** Discovery may select a BLE target before a later call verifies its wallet. */
|
|
1785
|
-
async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt,
|
|
1786
|
-
const binding =
|
|
1787
|
-
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId)
|
|
1788
|
-
|
|
1789
|
-
this.emitter,
|
|
1790
|
-
this._uiRegistry,
|
|
1791
|
-
{
|
|
1792
|
-
selectionRequestId: binding.selectedConnection.requestId,
|
|
1793
|
-
connection: { transport: "ble", connectId },
|
|
1794
|
-
identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
|
|
1795
|
-
extra: binding.extra
|
|
1796
|
-
},
|
|
1797
|
-
signal
|
|
1798
|
-
);
|
|
1799
|
-
if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
|
|
1800
|
-
this._bindingSelectionRequestId = void 0;
|
|
1801
|
-
}
|
|
1802
|
-
if (interactionId) {
|
|
1803
|
-
this._interactions.resolve(interactionId);
|
|
1804
|
-
this._pendingInteractionBindings.delete(interactionId);
|
|
1909
|
+
async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, operationId, signal) {
|
|
1910
|
+
const binding = operationId ? this._pendingOperationBindings.get(operationId) : attempt;
|
|
1911
|
+
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) {
|
|
1912
|
+
return false;
|
|
1805
1913
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1914
|
+
try {
|
|
1915
|
+
const outcome = await requestSaveDeviceBinding(
|
|
1916
|
+
this.emitter,
|
|
1917
|
+
this._uiRegistry,
|
|
1918
|
+
{
|
|
1919
|
+
selectionRequestId: binding.selectedConnection.requestId,
|
|
1920
|
+
connection: { transport: "ble", connectId },
|
|
1921
|
+
identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
|
|
1922
|
+
extra: binding.extra
|
|
1923
|
+
},
|
|
1924
|
+
signal
|
|
1925
|
+
);
|
|
1926
|
+
if (operationId) this._operations.resolve(operationId);
|
|
1927
|
+
if (!outcome.saved) {
|
|
1928
|
+
debugLog("[LedgerAdapter] BLE binding not persisted by host", {
|
|
1929
|
+
connectId,
|
|
1930
|
+
reason: outcome.reason
|
|
1931
|
+
});
|
|
1816
1932
|
}
|
|
1817
|
-
|
|
1933
|
+
return outcome.saved;
|
|
1934
|
+
} catch (error) {
|
|
1935
|
+
if (operationId) {
|
|
1936
|
+
this._pendingOperationBindings.delete(operationId);
|
|
1937
|
+
this._operations.end(operationId, "explicit");
|
|
1938
|
+
}
|
|
1939
|
+
const sessionId = this._sessions.get(connectId);
|
|
1940
|
+
this._sessions.delete(connectId);
|
|
1941
|
+
if (sessionId) {
|
|
1942
|
+
const teardown = this._runConnectorTeardown(
|
|
1943
|
+
() => this.connector.disconnect(sessionId)
|
|
1944
|
+
).catch(() => void 0);
|
|
1945
|
+
if (!signal?.aborted) await teardown;
|
|
1946
|
+
}
|
|
1947
|
+
throw error;
|
|
1948
|
+
} finally {
|
|
1949
|
+
if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
|
|
1950
|
+
this._bindingSelectionRequestId = void 0;
|
|
1951
|
+
}
|
|
1952
|
+
if (operationId) {
|
|
1953
|
+
this._pendingOperationBindings.delete(operationId);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1818
1956
|
}
|
|
1819
1957
|
/** Verify on the acquired session without re-entering the job queue. */
|
|
1820
1958
|
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
@@ -1986,7 +2124,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1986
2124
|
// Layer 1 entry. Caller signal only races the outer awaiter; the shared
|
|
1987
2125
|
// `_doConnect` runs under its own internal controller so caller A's cancel
|
|
1988
2126
|
// doesn't kill caller B's await.
|
|
1989
|
-
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false,
|
|
2127
|
+
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
|
|
1990
2128
|
if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
|
|
1991
2129
|
this._assertConnectorReady("connectDevice");
|
|
1992
2130
|
if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
|
|
@@ -2016,7 +2154,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2016
2154
|
innerSignal,
|
|
2017
2155
|
connectId,
|
|
2018
2156
|
allowUsbEphemeralFallback,
|
|
2019
|
-
|
|
2157
|
+
preserveOperationId,
|
|
2020
2158
|
context
|
|
2021
2159
|
);
|
|
2022
2160
|
} finally {
|
|
@@ -2032,7 +2170,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2032
2170
|
// Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
|
|
2033
2171
|
// Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
|
|
2034
2172
|
// throw DeviceNotFound so the user is kicked out of the loop.
|
|
2035
|
-
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false,
|
|
2173
|
+
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveOperationId, context) {
|
|
2036
2174
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2037
2175
|
if (this.connector.availableTransports?.includes("usb") && this.connector.availableTransports.includes("ble")) {
|
|
2038
2176
|
this._activeConnectionType = "usb";
|
|
@@ -2047,7 +2185,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2047
2185
|
usbDevices,
|
|
2048
2186
|
usbTarget,
|
|
2049
2187
|
allowUsbEphemeralFallback,
|
|
2050
|
-
|
|
2188
|
+
preserveOperationId,
|
|
2051
2189
|
context,
|
|
2052
2190
|
internalSignal
|
|
2053
2191
|
);
|
|
@@ -2057,24 +2195,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2057
2195
|
(connection) => connection.transport === "ble"
|
|
2058
2196
|
);
|
|
2059
2197
|
if (knownBle?.transport === "ble") {
|
|
2060
|
-
return this._connectDeviceOrThrow(
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2198
|
+
return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
|
|
2199
|
+
}
|
|
2200
|
+
if (targetConnectId && context?.knownConnections === void 0) {
|
|
2201
|
+
throw createHwkError({
|
|
2202
|
+
code: HardwareErrorCode3.DeviceNotFound,
|
|
2203
|
+
message: "Ledger connection metadata is required before starting Bluetooth binding"
|
|
2204
|
+
});
|
|
2065
2205
|
}
|
|
2066
2206
|
const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
|
|
2067
2207
|
return this._connectFirstOrSelect(
|
|
2068
2208
|
bleDevices,
|
|
2069
2209
|
void 0,
|
|
2070
2210
|
allowUsbEphemeralFallback,
|
|
2071
|
-
|
|
2211
|
+
preserveOperationId,
|
|
2072
2212
|
context,
|
|
2073
2213
|
internalSignal
|
|
2074
2214
|
);
|
|
2075
2215
|
}
|
|
2076
2216
|
if (this._isBleConnection() && targetConnectId) {
|
|
2077
|
-
return this._connectDeviceOrThrow(targetConnectId,
|
|
2217
|
+
return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
|
|
2078
2218
|
}
|
|
2079
2219
|
let confirms = 0;
|
|
2080
2220
|
while (!internalSignal.aborted) {
|
|
@@ -2101,7 +2241,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2101
2241
|
devices,
|
|
2102
2242
|
targetConnectId,
|
|
2103
2243
|
allowUsbEphemeralFallback,
|
|
2104
|
-
|
|
2244
|
+
preserveOperationId,
|
|
2105
2245
|
context,
|
|
2106
2246
|
internalSignal
|
|
2107
2247
|
);
|
|
@@ -2132,20 +2272,20 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2132
2272
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2133
2273
|
throw new Error("_doConnect aborted");
|
|
2134
2274
|
}
|
|
2135
|
-
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback
|
|
2136
|
-
|
|
2275
|
+
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
|
|
2276
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
2137
2277
|
if (targetConnectId) {
|
|
2138
2278
|
const target = devices.find(
|
|
2139
2279
|
(d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
|
|
2140
2280
|
);
|
|
2141
2281
|
if (target) {
|
|
2142
|
-
return this._connectDeviceOrThrow(target.connectId,
|
|
2282
|
+
return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
|
|
2143
2283
|
}
|
|
2144
2284
|
if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
|
|
2145
2285
|
debugLog(
|
|
2146
2286
|
`[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
|
|
2147
2287
|
);
|
|
2148
|
-
return this._connectDeviceOrThrow(devices[0].connectId,
|
|
2288
|
+
return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
|
|
2149
2289
|
}
|
|
2150
2290
|
if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
|
|
2151
2291
|
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
@@ -2166,11 +2306,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2166
2306
|
if (requiresBleSelection && context?.allowDeviceSelection !== false) {
|
|
2167
2307
|
const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
|
|
2168
2308
|
if (context) context.bindingSessionId = bindingSessionId;
|
|
2309
|
+
const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
|
|
2310
|
+
const knownUsb = context?.knownConnections?.find(
|
|
2311
|
+
(connection) => connection.transport === "usb"
|
|
2312
|
+
);
|
|
2313
|
+
const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
|
|
2169
2314
|
const { device, requestId } = await requestBleDeviceSelection({
|
|
2170
2315
|
emitter: this.emitter,
|
|
2171
2316
|
registry: this._uiRegistry,
|
|
2172
|
-
signal
|
|
2173
|
-
|
|
2317
|
+
signal,
|
|
2318
|
+
allowUsbFallback,
|
|
2319
|
+
scan: async () => {
|
|
2320
|
+
if (allowUsbFallback) {
|
|
2321
|
+
const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
|
|
2322
|
+
const candidate = usbDevices.find(
|
|
2323
|
+
(device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
|
|
2324
|
+
) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
|
|
2325
|
+
if (candidate) return [candidate];
|
|
2326
|
+
}
|
|
2327
|
+
return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
|
|
2328
|
+
},
|
|
2174
2329
|
request: {
|
|
2175
2330
|
devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
|
|
2176
2331
|
bindingSessionId,
|
|
@@ -2178,14 +2333,25 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2178
2333
|
context: {
|
|
2179
2334
|
kind: "bind-connection",
|
|
2180
2335
|
transport: "ble",
|
|
2181
|
-
reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
|
|
2336
|
+
reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
|
|
2182
2337
|
},
|
|
2183
2338
|
extra: context?.extra
|
|
2184
2339
|
}
|
|
2185
2340
|
});
|
|
2341
|
+
if (device.connectionType === "usb") {
|
|
2342
|
+
this._activeConnectionType = "usb";
|
|
2343
|
+
if (context) context.selectedConnection = void 0;
|
|
2344
|
+
if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
|
|
2345
|
+
this._bindingSelectionRequestId = void 0;
|
|
2346
|
+
this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
2347
|
+
type: UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
2348
|
+
payload: { selectionRequestId: requestId, status: "cancelled" }
|
|
2349
|
+
});
|
|
2350
|
+
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2351
|
+
}
|
|
2186
2352
|
if (context) context.selectedConnection = { connectId: device.connectId, requestId };
|
|
2187
2353
|
this._bindingSelectionRequestId = requestId;
|
|
2188
|
-
return this._connectDeviceOrThrow(device.connectId,
|
|
2354
|
+
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2189
2355
|
}
|
|
2190
2356
|
if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
|
|
2191
2357
|
if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(UI_REQUEST.REQUEST_SELECT_DEVICE)) {
|
|
@@ -2222,17 +2388,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2222
2388
|
}
|
|
2223
2389
|
if (context && requiresBleSelection)
|
|
2224
2390
|
context.selectedConnection = { connectId: selected.connectId, requestId };
|
|
2225
|
-
return this._connectDeviceOrThrow(selected.connectId,
|
|
2391
|
+
return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
|
|
2226
2392
|
}
|
|
2227
2393
|
if (devices.length !== 1) {
|
|
2228
2394
|
throw Object.assign(new Error("Ledger device not found."), {
|
|
2229
2395
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2230
2396
|
});
|
|
2231
2397
|
}
|
|
2232
|
-
return this._connectDeviceOrThrow(devices[0].connectId,
|
|
2398
|
+
return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
|
|
2233
2399
|
}
|
|
2234
|
-
async _connectDeviceOrThrow(chosenConnectId,
|
|
2235
|
-
const result = await this._connectTarget(chosenConnectId,
|
|
2400
|
+
async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
|
|
2401
|
+
const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
|
|
2236
2402
|
if (!result.success) {
|
|
2237
2403
|
const payload = result.payload;
|
|
2238
2404
|
const rethrow = Object.assign(new Error(payload.error), {
|
|
@@ -2274,6 +2440,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2274
2440
|
*/
|
|
2275
2441
|
async _callConnector(sessionId, method, params, signal) {
|
|
2276
2442
|
this._assertConnectorReady(method);
|
|
2443
|
+
if (signal?.aborted) throw this._abortReason(signal);
|
|
2277
2444
|
const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
|
|
2278
2445
|
let promise;
|
|
2279
2446
|
try {
|
|
@@ -2338,32 +2505,32 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2338
2505
|
};
|
|
2339
2506
|
}
|
|
2340
2507
|
async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2341
|
-
const
|
|
2342
|
-
if (
|
|
2508
|
+
const positionalOperationId = isHardwareOperationId(connectId) ? connectId : void 0;
|
|
2509
|
+
if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
|
|
2343
2510
|
throw createHwkError({
|
|
2344
2511
|
code: HardwareErrorCode3.InvalidParams,
|
|
2345
|
-
message: "Conflicting Ledger
|
|
2512
|
+
message: "Conflicting Ledger operation ids",
|
|
2346
2513
|
params: {
|
|
2347
|
-
|
|
2348
|
-
|
|
2514
|
+
positionalOperationId,
|
|
2515
|
+
commonOperationId: commonParams.operationId
|
|
2349
2516
|
}
|
|
2350
2517
|
});
|
|
2351
2518
|
}
|
|
2352
|
-
const
|
|
2353
|
-
const
|
|
2354
|
-
const
|
|
2355
|
-
const effectiveConnectId =
|
|
2519
|
+
const operationId = commonParams?.operationId ?? positionalOperationId;
|
|
2520
|
+
const operation = operationId ? this._operations.resolve(operationId) : void 0;
|
|
2521
|
+
const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
|
|
2522
|
+
const effectiveConnectId = operation?.connectId ?? connectId;
|
|
2356
2523
|
debugLog("[LedgerAdapter][REQ]", {
|
|
2357
2524
|
method,
|
|
2358
2525
|
connectId: effectiveConnectId || "(empty)",
|
|
2359
2526
|
params
|
|
2360
2527
|
});
|
|
2361
|
-
const queueKey = (
|
|
2528
|
+
const queueKey = (operationId ?? effectiveConnectId) || "__ledger_default__";
|
|
2362
2529
|
try {
|
|
2363
2530
|
const result = await this._jobQueue.enqueue(
|
|
2364
2531
|
queueKey,
|
|
2365
2532
|
async (signal) => {
|
|
2366
|
-
if (
|
|
2533
|
+
if (operationId) this._activeOperationJobs.add(operationId);
|
|
2367
2534
|
try {
|
|
2368
2535
|
return await this._runConnectorCall(
|
|
2369
2536
|
effectiveConnectId,
|
|
@@ -2374,16 +2541,16 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2374
2541
|
permissionDeviceId,
|
|
2375
2542
|
commonParams,
|
|
2376
2543
|
installContext ?? {},
|
|
2377
|
-
|
|
2544
|
+
operationId
|
|
2378
2545
|
);
|
|
2379
2546
|
} catch (error) {
|
|
2380
2547
|
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
2381
2548
|
throw error;
|
|
2382
2549
|
} finally {
|
|
2383
|
-
if (
|
|
2384
|
-
this.
|
|
2385
|
-
if (this.
|
|
2386
|
-
await this.
|
|
2550
|
+
if (operationId) {
|
|
2551
|
+
this._activeOperationJobs.delete(operationId);
|
|
2552
|
+
if (this._pendingOperationDisconnects.delete(operationId)) {
|
|
2553
|
+
await this._releaseLostOperationConnection(operationId);
|
|
2387
2554
|
}
|
|
2388
2555
|
}
|
|
2389
2556
|
}
|
|
@@ -2409,15 +2576,19 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2409
2576
|
});
|
|
2410
2577
|
throw err;
|
|
2411
2578
|
} finally {
|
|
2412
|
-
|
|
2579
|
+
releaseOperationRetention?.();
|
|
2413
2580
|
}
|
|
2414
2581
|
}
|
|
2415
2582
|
/**
|
|
2416
2583
|
* Race a promise against an abort signal. On abort, rejects with
|
|
2417
2584
|
* signal.reason → instance _lastCancelReason → generic Error('Aborted').
|
|
2418
2585
|
*/
|
|
2586
|
+
/** Hermes/RN polyfills don't always populate signal.reason; fall back. */
|
|
2587
|
+
_abortReason(signal) {
|
|
2588
|
+
return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2589
|
+
}
|
|
2419
2590
|
_abortable(signal, promise) {
|
|
2420
|
-
const getAbortReason = () =>
|
|
2591
|
+
const getAbortReason = () => this._abortReason(signal);
|
|
2421
2592
|
if (signal.aborted) {
|
|
2422
2593
|
void promise.catch(() => void 0);
|
|
2423
2594
|
return Promise.reject(getAbortReason());
|
|
@@ -2446,9 +2617,9 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2446
2617
|
}
|
|
2447
2618
|
}
|
|
2448
2619
|
/** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
|
|
2449
|
-
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext,
|
|
2620
|
+
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, operationId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
|
|
2450
2621
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2451
|
-
if (!
|
|
2622
|
+
if (!operationId) {
|
|
2452
2623
|
await this._ensureDevicePermission(
|
|
2453
2624
|
connectId,
|
|
2454
2625
|
permissionDeviceId ?? fingerprint?.deviceId,
|
|
@@ -2484,7 +2655,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2484
2655
|
message: "Ledger all-network connection ended"
|
|
2485
2656
|
});
|
|
2486
2657
|
}
|
|
2487
|
-
let resolvedConnectId =
|
|
2658
|
+
let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
|
|
2488
2659
|
preferredConnectId,
|
|
2489
2660
|
signal,
|
|
2490
2661
|
allowUsbEphemeralFallback,
|
|
@@ -2496,12 +2667,12 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2496
2667
|
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2497
2668
|
}
|
|
2498
2669
|
if (!sessionId) {
|
|
2499
|
-
if (
|
|
2500
|
-
this.
|
|
2670
|
+
if (operationId) {
|
|
2671
|
+
this._operations.end(operationId, "disconnect");
|
|
2501
2672
|
throw createHwkError({
|
|
2502
|
-
code: HardwareErrorCode3.
|
|
2503
|
-
message: "Ledger
|
|
2504
|
-
params: {
|
|
2673
|
+
code: HardwareErrorCode3.OperationEnded,
|
|
2674
|
+
message: "Ledger operation connection is no longer active",
|
|
2675
|
+
params: { operationId, reason: "disconnect" }
|
|
2505
2676
|
});
|
|
2506
2677
|
}
|
|
2507
2678
|
throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
|
|
@@ -2520,7 +2691,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2520
2691
|
)
|
|
2521
2692
|
);
|
|
2522
2693
|
if (fp.success) break;
|
|
2523
|
-
const binding =
|
|
2694
|
+
const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
|
|
2524
2695
|
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
|
|
2525
2696
|
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2526
2697
|
code: HardwareErrorCode3.DeviceMismatch
|
|
@@ -2531,13 +2702,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2531
2702
|
binding.rejectedConnectId = resolvedConnectId;
|
|
2532
2703
|
this._sessions.delete(resolvedConnectId);
|
|
2533
2704
|
await this.connector.disconnect(sessionId);
|
|
2534
|
-
if (
|
|
2705
|
+
if (operationId) this._pendingOperationDisconnects.delete(operationId);
|
|
2535
2706
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2536
2707
|
resolvedConnectId = await this._connectFirstOrSelect(
|
|
2537
2708
|
[],
|
|
2538
2709
|
void 0,
|
|
2539
2710
|
true,
|
|
2540
|
-
|
|
2711
|
+
operationId,
|
|
2541
2712
|
binding,
|
|
2542
2713
|
signal
|
|
2543
2714
|
);
|
|
@@ -2549,8 +2720,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2549
2720
|
message: "Selected Ledger connection ended"
|
|
2550
2721
|
});
|
|
2551
2722
|
sessionId = selectedSession;
|
|
2552
|
-
if (
|
|
2553
|
-
this.
|
|
2723
|
+
if (operationId)
|
|
2724
|
+
this._operations.rebind(operationId, {
|
|
2554
2725
|
connectId: resolvedConnectId,
|
|
2555
2726
|
device: selectedDevice,
|
|
2556
2727
|
connectionKeys: [sessionId]
|
|
@@ -2558,22 +2729,21 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2558
2729
|
if (installContext)
|
|
2559
2730
|
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2560
2731
|
}
|
|
2561
|
-
if (!interactionId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
|
|
2562
|
-
this._verifiedBleReconnectTargets.set(connectId, {
|
|
2563
|
-
connectId: resolvedConnectId,
|
|
2564
|
-
chain: fingerprint.chain,
|
|
2565
|
-
fingerprint: fingerprint.deviceId
|
|
2566
|
-
});
|
|
2567
|
-
}
|
|
2568
2732
|
await this._publishVerifiedBleBinding(
|
|
2569
2733
|
resolvedConnectId,
|
|
2570
2734
|
fingerprint.chain,
|
|
2571
2735
|
fingerprint.deviceId,
|
|
2572
2736
|
connectionAttempt,
|
|
2573
|
-
|
|
2574
|
-
connectId,
|
|
2737
|
+
operationId,
|
|
2575
2738
|
signal
|
|
2576
2739
|
);
|
|
2740
|
+
if (!operationId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
|
|
2741
|
+
this._verifiedBleReconnectTargets.set(connectId, {
|
|
2742
|
+
connectId: resolvedConnectId,
|
|
2743
|
+
chain: fingerprint.chain,
|
|
2744
|
+
fingerprint: fingerprint.deviceId
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2577
2747
|
}
|
|
2578
2748
|
businessCallStarted = true;
|
|
2579
2749
|
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
@@ -2581,17 +2751,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2581
2751
|
if (signal.aborted) throw err;
|
|
2582
2752
|
if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
|
|
2583
2753
|
this._discoveredDevices.delete(resolvedConnectId);
|
|
2584
|
-
if (
|
|
2754
|
+
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2585
2755
|
else {
|
|
2586
2756
|
this._sessions.delete(resolvedConnectId);
|
|
2587
2757
|
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2588
2758
|
}
|
|
2589
2759
|
const ambiguous = businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(method);
|
|
2590
|
-
const interactionParams =
|
|
2760
|
+
const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
|
|
2591
2761
|
throw createHwkError({
|
|
2592
|
-
code:
|
|
2762
|
+
code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(err).code,
|
|
2593
2763
|
message: "Ledger operation connection was lost; start a new operation",
|
|
2594
|
-
params: ambiguous ? operationMayHaveCompletedParams(method, {
|
|
2764
|
+
params: ambiguous ? operationMayHaveCompletedParams(method, { operationId }) : interactionParams,
|
|
2595
2765
|
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
2596
2766
|
});
|
|
2597
2767
|
}
|
|
@@ -2610,7 +2780,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2610
2780
|
await this._waitForDeviceConnect(signal);
|
|
2611
2781
|
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2612
2782
|
throw createHwkError({
|
|
2613
|
-
code:
|
|
2783
|
+
code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
|
|
2614
2784
|
message: "Ledger connection ended while waiting for unlock"
|
|
2615
2785
|
});
|
|
2616
2786
|
}
|
|
@@ -2623,7 +2793,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2623
2793
|
permissionDeviceId,
|
|
2624
2794
|
commonParams,
|
|
2625
2795
|
installContext,
|
|
2626
|
-
|
|
2796
|
+
operationId,
|
|
2627
2797
|
lockedRetryBudget - 1
|
|
2628
2798
|
);
|
|
2629
2799
|
}
|
|
@@ -2631,7 +2801,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2631
2801
|
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2632
2802
|
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2633
2803
|
throw createHwkError({
|
|
2634
|
-
code:
|
|
2804
|
+
code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
|
|
2635
2805
|
message: "Ledger connection ended during the app transition"
|
|
2636
2806
|
});
|
|
2637
2807
|
}
|
|
@@ -2640,26 +2810,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2640
2810
|
} catch (retryErr) {
|
|
2641
2811
|
if (isStuckAppStateError(retryErr)) throw err;
|
|
2642
2812
|
if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
|
|
2643
|
-
if (
|
|
2813
|
+
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2644
2814
|
else {
|
|
2645
2815
|
this._sessions.delete(resolvedConnectId);
|
|
2646
2816
|
this._discoveredDevices.delete(resolvedConnectId);
|
|
2647
2817
|
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2648
2818
|
}
|
|
2649
2819
|
throw createHwkError({
|
|
2650
|
-
code:
|
|
2820
|
+
code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(retryErr).code,
|
|
2651
2821
|
message: `Ledger ${method} may have completed before the connection was lost`,
|
|
2652
2822
|
params: !canReplayHardwareMethodAfterTransportFailure(method) ? operationMayHaveCompletedParams(method, {
|
|
2653
|
-
|
|
2823
|
+
operationId,
|
|
2654
2824
|
reason: "disconnect"
|
|
2655
|
-
}) : {
|
|
2825
|
+
}) : { operationId, reason: "disconnect" },
|
|
2656
2826
|
recovery: !canReplayHardwareMethodAfterTransportFailure(method) ? { scope: "unknown" } : void 0
|
|
2657
2827
|
});
|
|
2658
2828
|
}
|
|
2659
2829
|
throw retryErr;
|
|
2660
2830
|
}
|
|
2661
2831
|
}
|
|
2662
|
-
if (!
|
|
2832
|
+
if (!operationId && err?.code === HardwareErrorCode3.DeviceMismatch) {
|
|
2663
2833
|
this._sessions.delete(resolvedConnectId);
|
|
2664
2834
|
this._discoveredDevices.delete(resolvedConnectId);
|
|
2665
2835
|
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
@@ -2721,7 +2891,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2721
2891
|
permissionDeviceId,
|
|
2722
2892
|
commonParams,
|
|
2723
2893
|
installContext,
|
|
2724
|
-
|
|
2894
|
+
operationId
|
|
2725
2895
|
);
|
|
2726
2896
|
}
|
|
2727
2897
|
}
|
|
@@ -2875,8 +3045,8 @@ var LedgerAdapter = _LedgerAdapter;
|
|
|
2875
3045
|
|
|
2876
3046
|
// src/connector/LedgerConnectorBase.ts
|
|
2877
3047
|
import {
|
|
2878
|
-
EConnectorInteraction as
|
|
2879
|
-
HardwareErrorCode as
|
|
3048
|
+
EConnectorInteraction as EConnectorInteraction3,
|
|
3049
|
+
HardwareErrorCode as HardwareErrorCode7,
|
|
2880
3050
|
isKnownNonTargetHardwareVendor,
|
|
2881
3051
|
serializeConnectorError
|
|
2882
3052
|
} from "@onekeyfe/hwk-adapter-core";
|
|
@@ -4109,361 +4279,111 @@ async function _createZcashSigner(ctx, sessionId) {
|
|
|
4109
4279
|
}
|
|
4110
4280
|
|
|
4111
4281
|
// src/connector/chains/tron.ts
|
|
4112
|
-
import { HardwareErrorCode as
|
|
4113
|
-
import Trx from "@ledgerhq/hw-app-trx";
|
|
4114
|
-
|
|
4115
|
-
// src/connector/chains/legacyChainCall.ts
|
|
4116
|
-
import { EConnectorInteraction as EConnectorInteraction3 } from "@onekeyfe/hwk-adapter-core";
|
|
4282
|
+
import { HardwareErrorCode as HardwareErrorCode6, bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@onekeyfe/hwk-adapter-core";
|
|
4117
4283
|
|
|
4118
|
-
// src/
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
isSuccessCommandResult
|
|
4124
|
-
} from "@ledgerhq/device-management-kit";
|
|
4125
|
-
import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
|
|
4126
|
-
var APP_NAME_MAP = {
|
|
4127
|
-
ETH: "Ethereum",
|
|
4128
|
-
BTC: "Bitcoin",
|
|
4129
|
-
SOL: "Solana",
|
|
4130
|
-
TRX: "Tron",
|
|
4131
|
-
ZEC: "Zcash",
|
|
4132
|
-
XRP: "XRP",
|
|
4133
|
-
ADA: "Cardano",
|
|
4134
|
-
DOT: "Polkadot",
|
|
4135
|
-
ATOM: "Cosmos"
|
|
4136
|
-
};
|
|
4137
|
-
var DASHBOARD_APP_NAME = "BOLOS";
|
|
4138
|
-
var AppManager = class {
|
|
4139
|
-
constructor(dmk, options) {
|
|
4140
|
-
this._dmk = dmk;
|
|
4141
|
-
this._waitMs = options?.waitMs ?? 1e3;
|
|
4142
|
-
this._maxRetries = options?.maxRetries ?? 10;
|
|
4284
|
+
// src/signer/SignerTron.ts
|
|
4285
|
+
var SignerTron = class {
|
|
4286
|
+
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4287
|
+
constructor(_sdk) {
|
|
4288
|
+
this._sdk = _sdk;
|
|
4143
4289
|
}
|
|
4144
|
-
/**
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4290
|
+
/** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
|
|
4291
|
+
async getAddress(derivationPath, options) {
|
|
4292
|
+
const action = this._sdk.getAddress(derivationPath, options);
|
|
4293
|
+
const result = await deviceActionToPromise(
|
|
4294
|
+
action,
|
|
4295
|
+
this.onInteraction,
|
|
4296
|
+
void 0,
|
|
4297
|
+
this.onRegisterCanceller
|
|
4298
|
+
);
|
|
4299
|
+
return { address: result.address, publicKey: result.publicKey };
|
|
4150
4300
|
}
|
|
4151
|
-
/**
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
*/
|
|
4161
|
-
/**
|
|
4162
|
-
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
4163
|
-
* device is about to display "Open <app>" on screen and wait for the
|
|
4164
|
-
* user's button press. UI consumers should show their "open app" prompt
|
|
4165
|
-
* in response. NOT called when the target app is already open (no user
|
|
4166
|
-
* interaction needed in that case).
|
|
4167
|
-
*
|
|
4168
|
-
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
4169
|
-
* has physically confirmed on the device, so anything that runs AFTER
|
|
4170
|
-
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
4171
|
-
* Hence the callback must fire BEFORE that await.
|
|
4172
|
-
*/
|
|
4173
|
-
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
4174
|
-
const currentApp = await this._getCurrentApp(sessionId);
|
|
4175
|
-
if (currentApp === targetAppName) {
|
|
4176
|
-
return;
|
|
4177
|
-
}
|
|
4178
|
-
if (!this._isDashboard(currentApp)) {
|
|
4179
|
-
await this._closeCurrentApp(sessionId);
|
|
4180
|
-
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
4181
|
-
}
|
|
4182
|
-
onConfirmOnDevice?.();
|
|
4183
|
-
await this._openApp(sessionId, targetAppName);
|
|
4184
|
-
await this._waitForApp(sessionId, targetAppName);
|
|
4301
|
+
/** Sign a protobuf-encoded raw transaction. */
|
|
4302
|
+
async signTransaction(derivationPath, transaction, options) {
|
|
4303
|
+
const action = this._sdk.signTransaction(derivationPath, transaction, options);
|
|
4304
|
+
return deviceActionToPromise(
|
|
4305
|
+
action,
|
|
4306
|
+
this.onInteraction,
|
|
4307
|
+
void 0,
|
|
4308
|
+
this.onRegisterCanceller
|
|
4309
|
+
);
|
|
4185
4310
|
}
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
if (isSuccessCommandResult(result)) {
|
|
4195
|
-
debugLog("[AppManager] currentApp:", result.data.name);
|
|
4196
|
-
return result.data.name;
|
|
4197
|
-
}
|
|
4198
|
-
const errResult = result;
|
|
4199
|
-
const dmkErr = errResult.error ?? {};
|
|
4200
|
-
const original = dmkErr.originalError;
|
|
4201
|
-
debugLog(
|
|
4202
|
-
"[AppManager] _getCurrentApp failed sessionId=",
|
|
4203
|
-
sessionId,
|
|
4204
|
-
"tag=",
|
|
4205
|
-
dmkErr._tag,
|
|
4206
|
-
"errorCode=",
|
|
4207
|
-
dmkErr.errorCode,
|
|
4208
|
-
"message=",
|
|
4209
|
-
dmkErr.message,
|
|
4210
|
-
"originalErrorMessage=",
|
|
4211
|
-
original?.message ?? String(original ?? "")
|
|
4311
|
+
/** Sign a personal message (TIP-191). */
|
|
4312
|
+
async signPersonalMessage(derivationPath, message, options) {
|
|
4313
|
+
const action = this._sdk.signPersonalMessage(derivationPath, message, options);
|
|
4314
|
+
return deviceActionToPromise(
|
|
4315
|
+
action,
|
|
4316
|
+
this.onInteraction,
|
|
4317
|
+
void 0,
|
|
4318
|
+
this.onRegisterCanceller
|
|
4212
4319
|
);
|
|
4320
|
+
}
|
|
4321
|
+
};
|
|
4322
|
+
|
|
4323
|
+
// src/connector/chains/tron.ts
|
|
4324
|
+
async function tronGetAddress(ctx, sessionId, params) {
|
|
4325
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4326
|
+
const path = normalizePath(params.path);
|
|
4327
|
+
try {
|
|
4328
|
+
const result = await tronSigner.getAddress(path, {
|
|
4329
|
+
checkOnDevice: params.showOnDevice ?? false
|
|
4330
|
+
});
|
|
4331
|
+
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
4332
|
+
} catch (err) {
|
|
4333
|
+
ctx.invalidateSession(sessionId);
|
|
4334
|
+
throw ctx.wrapError(err);
|
|
4335
|
+
} finally {
|
|
4336
|
+
ctx.clearCanceller(sessionId);
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
async function tronSignTransaction(ctx, sessionId, params) {
|
|
4340
|
+
if (!params.rawTxHex) {
|
|
4213
4341
|
throw Object.assign(
|
|
4214
|
-
new Error(
|
|
4215
|
-
|
|
4216
|
-
),
|
|
4217
|
-
{
|
|
4218
|
-
_tag: dmkErr._tag,
|
|
4219
|
-
errorCode: dmkErr.errorCode,
|
|
4220
|
-
originalError: original
|
|
4221
|
-
}
|
|
4342
|
+
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
4343
|
+
{ code: HardwareErrorCode6.InvalidParams }
|
|
4222
4344
|
);
|
|
4223
4345
|
}
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
sessionId,
|
|
4227
|
-
command: new OpenAppCommand({ appName })
|
|
4228
|
-
});
|
|
4229
|
-
if (!isSuccessCommandResult(result)) {
|
|
4230
|
-
const dmkErr = result.error;
|
|
4231
|
-
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
4232
|
-
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
4233
|
-
debugLog(
|
|
4234
|
-
"[AppManager] openApp failed:",
|
|
4235
|
-
appName,
|
|
4236
|
-
"errorCode:",
|
|
4237
|
-
errorCode,
|
|
4238
|
-
"tag:",
|
|
4239
|
-
dmkErr._tag
|
|
4240
|
-
);
|
|
4241
|
-
let code;
|
|
4242
|
-
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
4243
|
-
code = HardwareErrorCode6.AppNotInstalled;
|
|
4244
|
-
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
4245
|
-
code = HardwareErrorCode6.UserRejected;
|
|
4246
|
-
}
|
|
4247
|
-
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
4248
|
-
_tag: ERROR_TAG.OpenAppCommand,
|
|
4249
|
-
code,
|
|
4250
|
-
errorCode,
|
|
4251
|
-
statusCode: errorCode,
|
|
4252
|
-
appName,
|
|
4253
|
-
originalError: dmkErr
|
|
4254
|
-
});
|
|
4255
|
-
}
|
|
4256
|
-
}
|
|
4257
|
-
async _closeCurrentApp(sessionId) {
|
|
4258
|
-
debugLog("[AppManager] closeCurrentApp");
|
|
4259
|
-
await this._dmk.sendCommand({
|
|
4260
|
-
sessionId,
|
|
4261
|
-
command: new CloseAppCommand()
|
|
4262
|
-
});
|
|
4263
|
-
}
|
|
4264
|
-
/**
|
|
4265
|
-
* Poll the device until the expected app is reported as running,
|
|
4266
|
-
* or throw after `_maxRetries` attempts.
|
|
4267
|
-
*/
|
|
4268
|
-
async _waitForApp(sessionId, expectedAppName) {
|
|
4269
|
-
let lastSeen = "";
|
|
4270
|
-
for (let i = 0; i < this._maxRetries; i++) {
|
|
4271
|
-
await this._wait();
|
|
4272
|
-
const current = await this._getCurrentApp(sessionId);
|
|
4273
|
-
lastSeen = current;
|
|
4274
|
-
if (current === expectedAppName) {
|
|
4275
|
-
return;
|
|
4276
|
-
}
|
|
4277
|
-
}
|
|
4278
|
-
debugLog(
|
|
4279
|
-
"[AppManager] waitForApp exhausted: expected=",
|
|
4280
|
-
expectedAppName,
|
|
4281
|
-
"lastSeen=",
|
|
4282
|
-
lastSeen
|
|
4283
|
-
);
|
|
4284
|
-
throw new Error(
|
|
4285
|
-
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
4286
|
-
);
|
|
4287
|
-
}
|
|
4288
|
-
_isDashboard(appName) {
|
|
4289
|
-
return appName === DASHBOARD_APP_NAME;
|
|
4290
|
-
}
|
|
4291
|
-
_wait() {
|
|
4292
|
-
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
4293
|
-
}
|
|
4294
|
-
};
|
|
4295
|
-
|
|
4296
|
-
// src/connector/chains/legacyChainCall.ts
|
|
4297
|
-
function isLegacyWrongAppError(err, _appName) {
|
|
4298
|
-
return isWrongAppError(err);
|
|
4299
|
-
}
|
|
4300
|
-
async function withLegacyChainCall(ctx, sessionId, options, action) {
|
|
4301
|
-
const { appName, needsConfirmation } = options;
|
|
4302
|
-
let openAppPromptShown = false;
|
|
4303
|
-
const onAppOpenPrompt = () => {
|
|
4304
|
-
openAppPromptShown = true;
|
|
4305
|
-
ctx.emit("ui-event", {
|
|
4306
|
-
type: EConnectorInteraction3.ConfirmOpenApp,
|
|
4307
|
-
payload: { sessionId }
|
|
4308
|
-
});
|
|
4309
|
-
};
|
|
4310
|
-
const closeOpenAppUiIfShown = () => {
|
|
4311
|
-
if (openAppPromptShown) {
|
|
4312
|
-
ctx.emit("ui-event", {
|
|
4313
|
-
type: EConnectorInteraction3.InteractionComplete,
|
|
4314
|
-
payload: { sessionId }
|
|
4315
|
-
});
|
|
4316
|
-
openAppPromptShown = false;
|
|
4317
|
-
}
|
|
4318
|
-
};
|
|
4319
|
-
try {
|
|
4320
|
-
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
4321
|
-
} catch (err) {
|
|
4322
|
-
debugLog(
|
|
4323
|
-
"[LegacyChainCall] pre-flight ensureAppOpen failed:",
|
|
4324
|
-
appName,
|
|
4325
|
-
err?.message
|
|
4326
|
-
);
|
|
4327
|
-
closeOpenAppUiIfShown();
|
|
4328
|
-
throw ctx.wrapError(err, { defaultAppName: appName });
|
|
4329
|
-
}
|
|
4330
|
-
const runOnce = async () => {
|
|
4331
|
-
let confirmEmitted = false;
|
|
4332
|
-
if (needsConfirmation) {
|
|
4333
|
-
ctx.emit("ui-event", {
|
|
4334
|
-
type: EConnectorInteraction3.ConfirmOnDevice,
|
|
4335
|
-
payload: { sessionId }
|
|
4336
|
-
});
|
|
4337
|
-
confirmEmitted = true;
|
|
4338
|
-
}
|
|
4339
|
-
try {
|
|
4340
|
-
return await action(sessionId);
|
|
4341
|
-
} finally {
|
|
4342
|
-
if (confirmEmitted || openAppPromptShown) {
|
|
4343
|
-
ctx.emit("ui-event", {
|
|
4344
|
-
type: EConnectorInteraction3.InteractionComplete,
|
|
4345
|
-
payload: { sessionId }
|
|
4346
|
-
});
|
|
4347
|
-
openAppPromptShown = false;
|
|
4348
|
-
}
|
|
4349
|
-
}
|
|
4350
|
-
};
|
|
4346
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4347
|
+
const path = normalizePath(params.path);
|
|
4351
4348
|
try {
|
|
4352
|
-
|
|
4349
|
+
const signature = await tronSigner.signTransaction(path, hexToBytes4(params.rawTxHex));
|
|
4350
|
+
return { signature: bytesToHex3(signature) };
|
|
4353
4351
|
} catch (err) {
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
}
|
|
4359
|
-
debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
|
|
4360
|
-
try {
|
|
4361
|
-
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
4362
|
-
} catch (switchErr) {
|
|
4363
|
-
debugLog(
|
|
4364
|
-
"[LegacyChainCall] retry ensureAppOpen failed:",
|
|
4365
|
-
appName,
|
|
4366
|
-
switchErr?.message
|
|
4367
|
-
);
|
|
4368
|
-
closeOpenAppUiIfShown();
|
|
4369
|
-
throw ctx.wrapError(switchErr, { defaultAppName: appName });
|
|
4370
|
-
}
|
|
4371
|
-
ctx.clearAllSigners();
|
|
4372
|
-
const result = await runOnce();
|
|
4373
|
-
debugLog("[LegacyChainCall] retry succeeded:", appName);
|
|
4374
|
-
return result;
|
|
4375
|
-
}
|
|
4376
|
-
}
|
|
4377
|
-
async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
|
|
4378
|
-
const dmk = await ctx.getOrCreateDmk();
|
|
4379
|
-
const appManager = new AppManager(dmk);
|
|
4380
|
-
await appManager.ensureAppOpen(sessionId, appName, onPrompt);
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
// src/transport/DmkTransport.ts
|
|
4384
|
-
import Transport from "@ledgerhq/hw-transport";
|
|
4385
|
-
var DmkTransport = class extends Transport {
|
|
4386
|
-
constructor(dmk, sessionId) {
|
|
4387
|
-
super();
|
|
4388
|
-
this._dmk = dmk;
|
|
4389
|
-
this._sessionId = sessionId;
|
|
4390
|
-
}
|
|
4391
|
-
async exchange(apdu) {
|
|
4392
|
-
const response = await this._dmk.sendApdu({
|
|
4393
|
-
sessionId: this._sessionId,
|
|
4394
|
-
apdu: new Uint8Array(apdu)
|
|
4395
|
-
});
|
|
4396
|
-
const { data, statusCode } = response;
|
|
4397
|
-
const result = Buffer.alloc(data.length + 2);
|
|
4398
|
-
if (data.length > 0) {
|
|
4399
|
-
result.set(data, 0);
|
|
4400
|
-
}
|
|
4401
|
-
result.set(statusCode, data.length);
|
|
4402
|
-
return result;
|
|
4403
|
-
}
|
|
4404
|
-
async close() {
|
|
4405
|
-
}
|
|
4406
|
-
};
|
|
4407
|
-
|
|
4408
|
-
// src/connector/chains/tron.ts
|
|
4409
|
-
async function tronGetAddress(ctx, sessionId, params) {
|
|
4410
|
-
const path = normalizePath(params.path);
|
|
4411
|
-
const showOnDevice = params.showOnDevice ?? false;
|
|
4412
|
-
return withLegacyChainCall(
|
|
4413
|
-
ctx,
|
|
4414
|
-
sessionId,
|
|
4415
|
-
{
|
|
4416
|
-
appName: "Tron",
|
|
4417
|
-
// Only show "confirm on device" UI when the device is actually going
|
|
4418
|
-
// to display the address for the user to verify.
|
|
4419
|
-
needsConfirmation: showOnDevice
|
|
4420
|
-
},
|
|
4421
|
-
async (sid) => {
|
|
4422
|
-
const trx = await _createTrx(ctx, sid);
|
|
4423
|
-
const result = await trx.getAddress(path, showOnDevice);
|
|
4424
|
-
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
4425
|
-
}
|
|
4426
|
-
);
|
|
4427
|
-
}
|
|
4428
|
-
async function tronSignTransaction(ctx, sessionId, params) {
|
|
4429
|
-
if (!params.rawTxHex) {
|
|
4430
|
-
throw Object.assign(
|
|
4431
|
-
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
4432
|
-
{ code: HardwareErrorCode7.InvalidParams }
|
|
4433
|
-
);
|
|
4352
|
+
ctx.invalidateSession(sessionId);
|
|
4353
|
+
throw ctx.wrapError(err);
|
|
4354
|
+
} finally {
|
|
4355
|
+
ctx.clearCanceller(sessionId);
|
|
4434
4356
|
}
|
|
4435
|
-
const path = normalizePath(params.path);
|
|
4436
|
-
return withLegacyChainCall(
|
|
4437
|
-
ctx,
|
|
4438
|
-
sessionId,
|
|
4439
|
-
{ appName: "Tron", needsConfirmation: true },
|
|
4440
|
-
async (sid) => {
|
|
4441
|
-
const trx = await _createTrx(ctx, sid);
|
|
4442
|
-
const signature = await trx.signTransaction(
|
|
4443
|
-
path,
|
|
4444
|
-
params.rawTxHex,
|
|
4445
|
-
params.tokenSignatures ?? []
|
|
4446
|
-
);
|
|
4447
|
-
return { signature };
|
|
4448
|
-
}
|
|
4449
|
-
);
|
|
4450
4357
|
}
|
|
4451
4358
|
async function tronSignMessage(ctx, sessionId, params) {
|
|
4359
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4452
4360
|
const path = normalizePath(params.path);
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
);
|
|
4361
|
+
try {
|
|
4362
|
+
const signature = await tronSigner.signPersonalMessage(path, hexToBytes4(params.messageHex));
|
|
4363
|
+
return { signature: bytesToHex3(signature) };
|
|
4364
|
+
} catch (err) {
|
|
4365
|
+
ctx.invalidateSession(sessionId);
|
|
4366
|
+
throw ctx.wrapError(err);
|
|
4367
|
+
} finally {
|
|
4368
|
+
ctx.clearCanceller(sessionId);
|
|
4369
|
+
}
|
|
4463
4370
|
}
|
|
4464
|
-
async function
|
|
4371
|
+
async function _createTronSigner(ctx, sessionId) {
|
|
4465
4372
|
const dmk = await ctx.getOrCreateDmk();
|
|
4466
|
-
|
|
4373
|
+
const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
|
|
4374
|
+
const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
|
|
4375
|
+
const signer = new SignerTron(sdkSigner);
|
|
4376
|
+
signer.onInteraction = (interaction) => {
|
|
4377
|
+
debugLog("[LedgerConnector] tron.onInteraction:", interaction);
|
|
4378
|
+
ctx.emit("ui-event", {
|
|
4379
|
+
type: collapseSignerInteraction(interaction),
|
|
4380
|
+
payload: { sessionId }
|
|
4381
|
+
});
|
|
4382
|
+
};
|
|
4383
|
+
signer.onRegisterCanceller = (cancel) => {
|
|
4384
|
+
ctx.registerCanceller(sessionId, cancel);
|
|
4385
|
+
};
|
|
4386
|
+
return signer;
|
|
4467
4387
|
}
|
|
4468
4388
|
|
|
4469
4389
|
// src/device-apps/customActions.ts
|
|
@@ -4642,7 +4562,7 @@ var DeviceApps = class {
|
|
|
4642
4562
|
seTargetId: v.seTargetId,
|
|
4643
4563
|
mcuTargetId: v.mcuTargetId,
|
|
4644
4564
|
seVersion: v.seVersion,
|
|
4645
|
-
seFlagsHex:
|
|
4565
|
+
seFlagsHex: bytesToHex4(v.seFlags),
|
|
4646
4566
|
mcuSephVersion: v.mcuSephVersion,
|
|
4647
4567
|
mcuBootloaderVersion: v.mcuBootloaderVersion,
|
|
4648
4568
|
hwVersion: v.hwVersion
|
|
@@ -4716,7 +4636,7 @@ var DeviceApps = class {
|
|
|
4716
4636
|
}
|
|
4717
4637
|
}
|
|
4718
4638
|
};
|
|
4719
|
-
function
|
|
4639
|
+
function bytesToHex4(bytes) {
|
|
4720
4640
|
if (!bytes) return "";
|
|
4721
4641
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4722
4642
|
}
|
|
@@ -4763,7 +4683,7 @@ var METHOD_PREFIX_TO_APP_NAME = {
|
|
|
4763
4683
|
zcash: "Zcash"
|
|
4764
4684
|
};
|
|
4765
4685
|
var HARDWARE_ERROR_CODE_VALUES = new Set(
|
|
4766
|
-
Object.values(
|
|
4686
|
+
Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
|
|
4767
4687
|
);
|
|
4768
4688
|
var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
4769
4689
|
var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
|
|
@@ -4800,6 +4720,8 @@ async function defaultLedgerKitImporter(pkg) {
|
|
|
4800
4720
|
return import("@ledgerhq/device-signer-kit-bitcoin");
|
|
4801
4721
|
case "@ledgerhq/device-signer-kit-solana":
|
|
4802
4722
|
return import("@ledgerhq/device-signer-kit-solana");
|
|
4723
|
+
case "@ledgerhq/device-signer-kit-tron":
|
|
4724
|
+
return import("@ledgerhq/device-signer-kit-tron");
|
|
4803
4725
|
case "@ledgerhq/device-signer-kit-zcash":
|
|
4804
4726
|
return import("@ledgerhq/device-signer-kit-zcash");
|
|
4805
4727
|
case "@ledgerhq/context-module":
|
|
@@ -4960,7 +4882,7 @@ var LedgerConnectorBase = class {
|
|
|
4960
4882
|
`Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
|
|
4961
4883
|
);
|
|
4962
4884
|
err._tag = ERROR_TAG.BlePairingTimeout;
|
|
4963
|
-
err.code =
|
|
4885
|
+
err.code = HardwareErrorCode7.BlePairingTimeout;
|
|
4964
4886
|
reject(err);
|
|
4965
4887
|
}, HANG_CEILING_MS);
|
|
4966
4888
|
});
|
|
@@ -4993,7 +4915,7 @@ var LedgerConnectorBase = class {
|
|
|
4993
4915
|
"Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
|
|
4994
4916
|
);
|
|
4995
4917
|
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
4996
|
-
err.code =
|
|
4918
|
+
err.code = HardwareErrorCode7.DeviceNotFound;
|
|
4997
4919
|
throw err;
|
|
4998
4920
|
};
|
|
4999
4921
|
const doConnect = async (path) => {
|
|
@@ -5057,7 +4979,7 @@ var LedgerConnectorBase = class {
|
|
|
5057
4979
|
"Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
|
|
5058
4980
|
);
|
|
5059
4981
|
wrapped._tag = ERROR_TAG.BleGattBondingFailed;
|
|
5060
|
-
wrapped.code =
|
|
4982
|
+
wrapped.code = HardwareErrorCode7.BlePairingTimeout;
|
|
5061
4983
|
wrapped.originalError = err;
|
|
5062
4984
|
throw wrapped;
|
|
5063
4985
|
}
|
|
@@ -5135,7 +5057,7 @@ var LedgerConnectorBase = class {
|
|
|
5135
5057
|
this._unwatchSessionState(sessionId);
|
|
5136
5058
|
this._signerManager?.invalidate(sessionId);
|
|
5137
5059
|
this._cancellers.get(sessionId)?.({
|
|
5138
|
-
code:
|
|
5060
|
+
code: HardwareErrorCode7.DeviceDisconnected,
|
|
5139
5061
|
tag: "DeviceDisconnected",
|
|
5140
5062
|
message: "Device disconnected"
|
|
5141
5063
|
});
|
|
@@ -5161,7 +5083,7 @@ var LedgerConnectorBase = class {
|
|
|
5161
5083
|
success: false,
|
|
5162
5084
|
error: serializeConnectorError(
|
|
5163
5085
|
Object.assign(new Error("Ledger app is unresponsive"), {
|
|
5164
|
-
code:
|
|
5086
|
+
code: HardwareErrorCode7.DeviceAppStuck,
|
|
5165
5087
|
_tag: ERROR_TAG.DeviceAppStuck,
|
|
5166
5088
|
originalError: err
|
|
5167
5089
|
})
|
|
@@ -5173,7 +5095,7 @@ var LedgerConnectorBase = class {
|
|
|
5173
5095
|
success: false,
|
|
5174
5096
|
error: serializeConnectorError(
|
|
5175
5097
|
Object.assign(new Error("Device communication interrupted, please retry"), {
|
|
5176
|
-
code:
|
|
5098
|
+
code: HardwareErrorCode7.TransportError,
|
|
5177
5099
|
_tag: ERROR_TAG.DeviceTransportStuck,
|
|
5178
5100
|
originalError: err
|
|
5179
5101
|
})
|
|
@@ -5248,7 +5170,7 @@ var LedgerConnectorBase = class {
|
|
|
5248
5170
|
try {
|
|
5249
5171
|
return await apps.install(p.appName, ({ progress }) => {
|
|
5250
5172
|
ctx.emit("ui-event", {
|
|
5251
|
-
type:
|
|
5173
|
+
type: EConnectorInteraction3.AppInstallProgress,
|
|
5252
5174
|
payload: {
|
|
5253
5175
|
sessionId,
|
|
5254
5176
|
appName: p.appName,
|
|
@@ -5587,6 +5509,209 @@ var LedgerConnectorBase = class {
|
|
|
5587
5509
|
return error;
|
|
5588
5510
|
}
|
|
5589
5511
|
};
|
|
5512
|
+
|
|
5513
|
+
// src/transport/DmkTransport.ts
|
|
5514
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
5515
|
+
var DmkTransport = class extends Transport {
|
|
5516
|
+
constructor(dmk, sessionId) {
|
|
5517
|
+
super();
|
|
5518
|
+
this._dmk = dmk;
|
|
5519
|
+
this._sessionId = sessionId;
|
|
5520
|
+
}
|
|
5521
|
+
async exchange(apdu) {
|
|
5522
|
+
const response = await this._dmk.sendApdu({
|
|
5523
|
+
sessionId: this._sessionId,
|
|
5524
|
+
apdu: new Uint8Array(apdu)
|
|
5525
|
+
});
|
|
5526
|
+
const { data, statusCode } = response;
|
|
5527
|
+
const result = Buffer.alloc(data.length + 2);
|
|
5528
|
+
if (data.length > 0) {
|
|
5529
|
+
result.set(data, 0);
|
|
5530
|
+
}
|
|
5531
|
+
result.set(statusCode, data.length);
|
|
5532
|
+
return result;
|
|
5533
|
+
}
|
|
5534
|
+
async close() {
|
|
5535
|
+
}
|
|
5536
|
+
};
|
|
5537
|
+
|
|
5538
|
+
// src/app/AppManager.ts
|
|
5539
|
+
import {
|
|
5540
|
+
CloseAppCommand,
|
|
5541
|
+
GetAppAndVersionCommand,
|
|
5542
|
+
OpenAppCommand,
|
|
5543
|
+
isSuccessCommandResult
|
|
5544
|
+
} from "@ledgerhq/device-management-kit";
|
|
5545
|
+
import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
|
|
5546
|
+
var APP_NAME_MAP = {
|
|
5547
|
+
ETH: "Ethereum",
|
|
5548
|
+
BTC: "Bitcoin",
|
|
5549
|
+
SOL: "Solana",
|
|
5550
|
+
TRX: "Tron",
|
|
5551
|
+
ZEC: "Zcash",
|
|
5552
|
+
XRP: "XRP",
|
|
5553
|
+
ADA: "Cardano",
|
|
5554
|
+
DOT: "Polkadot",
|
|
5555
|
+
ATOM: "Cosmos"
|
|
5556
|
+
};
|
|
5557
|
+
var DASHBOARD_APP_NAME = "BOLOS";
|
|
5558
|
+
var AppManager = class {
|
|
5559
|
+
constructor(dmk, options) {
|
|
5560
|
+
this._dmk = dmk;
|
|
5561
|
+
this._waitMs = options?.waitMs ?? 1e3;
|
|
5562
|
+
this._maxRetries = options?.maxRetries ?? 10;
|
|
5563
|
+
}
|
|
5564
|
+
/**
|
|
5565
|
+
* Return the Ledger app name for a given chain ticker,
|
|
5566
|
+
* or undefined if the chain is not supported.
|
|
5567
|
+
*/
|
|
5568
|
+
static getAppName(chain) {
|
|
5569
|
+
return APP_NAME_MAP[chain];
|
|
5570
|
+
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Ensure the target app is open on the device identified by `sessionId`.
|
|
5573
|
+
*
|
|
5574
|
+
* Flow:
|
|
5575
|
+
* 1. Check the currently running app.
|
|
5576
|
+
* 2. If it is already the target, return immediately.
|
|
5577
|
+
* 3. If a different app is running (not dashboard), close it first.
|
|
5578
|
+
* 4. Open the target app.
|
|
5579
|
+
* 5. Poll until the device confirms the target app is running.
|
|
5580
|
+
*/
|
|
5581
|
+
/**
|
|
5582
|
+
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
5583
|
+
* device is about to display "Open <app>" on screen and wait for the
|
|
5584
|
+
* user's button press. UI consumers should show their "open app" prompt
|
|
5585
|
+
* in response. NOT called when the target app is already open (no user
|
|
5586
|
+
* interaction needed in that case).
|
|
5587
|
+
*
|
|
5588
|
+
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
5589
|
+
* has physically confirmed on the device, so anything that runs AFTER
|
|
5590
|
+
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
5591
|
+
* Hence the callback must fire BEFORE that await.
|
|
5592
|
+
*/
|
|
5593
|
+
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
5594
|
+
const currentApp = await this._getCurrentApp(sessionId);
|
|
5595
|
+
if (currentApp === targetAppName) {
|
|
5596
|
+
return;
|
|
5597
|
+
}
|
|
5598
|
+
if (!this._isDashboard(currentApp)) {
|
|
5599
|
+
await this._closeCurrentApp(sessionId);
|
|
5600
|
+
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
5601
|
+
}
|
|
5602
|
+
onConfirmOnDevice?.();
|
|
5603
|
+
await this._openApp(sessionId, targetAppName);
|
|
5604
|
+
await this._waitForApp(sessionId, targetAppName);
|
|
5605
|
+
}
|
|
5606
|
+
// ---------------------------------------------------------------------------
|
|
5607
|
+
// Private helpers
|
|
5608
|
+
// ---------------------------------------------------------------------------
|
|
5609
|
+
async _getCurrentApp(sessionId) {
|
|
5610
|
+
const result = await this._dmk.sendCommand({
|
|
5611
|
+
sessionId,
|
|
5612
|
+
command: new GetAppAndVersionCommand()
|
|
5613
|
+
});
|
|
5614
|
+
if (isSuccessCommandResult(result)) {
|
|
5615
|
+
debugLog("[AppManager] currentApp:", result.data.name);
|
|
5616
|
+
return result.data.name;
|
|
5617
|
+
}
|
|
5618
|
+
const errResult = result;
|
|
5619
|
+
const dmkErr = errResult.error ?? {};
|
|
5620
|
+
const original = dmkErr.originalError;
|
|
5621
|
+
debugLog(
|
|
5622
|
+
"[AppManager] _getCurrentApp failed sessionId=",
|
|
5623
|
+
sessionId,
|
|
5624
|
+
"tag=",
|
|
5625
|
+
dmkErr._tag,
|
|
5626
|
+
"errorCode=",
|
|
5627
|
+
dmkErr.errorCode,
|
|
5628
|
+
"message=",
|
|
5629
|
+
dmkErr.message,
|
|
5630
|
+
"originalErrorMessage=",
|
|
5631
|
+
original?.message ?? String(original ?? "")
|
|
5632
|
+
);
|
|
5633
|
+
throw Object.assign(
|
|
5634
|
+
new Error(
|
|
5635
|
+
dmkErr.message ?? "Failed to get current app from device"
|
|
5636
|
+
),
|
|
5637
|
+
{
|
|
5638
|
+
_tag: dmkErr._tag,
|
|
5639
|
+
errorCode: dmkErr.errorCode,
|
|
5640
|
+
originalError: original
|
|
5641
|
+
}
|
|
5642
|
+
);
|
|
5643
|
+
}
|
|
5644
|
+
async _openApp(sessionId, appName) {
|
|
5645
|
+
const result = await this._dmk.sendCommand({
|
|
5646
|
+
sessionId,
|
|
5647
|
+
command: new OpenAppCommand({ appName })
|
|
5648
|
+
});
|
|
5649
|
+
if (!isSuccessCommandResult(result)) {
|
|
5650
|
+
const dmkErr = result.error;
|
|
5651
|
+
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
5652
|
+
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
5653
|
+
debugLog(
|
|
5654
|
+
"[AppManager] openApp failed:",
|
|
5655
|
+
appName,
|
|
5656
|
+
"errorCode:",
|
|
5657
|
+
errorCode,
|
|
5658
|
+
"tag:",
|
|
5659
|
+
dmkErr._tag
|
|
5660
|
+
);
|
|
5661
|
+
let code;
|
|
5662
|
+
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
5663
|
+
code = HardwareErrorCode8.AppNotInstalled;
|
|
5664
|
+
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
5665
|
+
code = HardwareErrorCode8.UserRejected;
|
|
5666
|
+
}
|
|
5667
|
+
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
5668
|
+
_tag: ERROR_TAG.OpenAppCommand,
|
|
5669
|
+
code,
|
|
5670
|
+
errorCode,
|
|
5671
|
+
statusCode: errorCode,
|
|
5672
|
+
appName,
|
|
5673
|
+
originalError: dmkErr
|
|
5674
|
+
});
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
async _closeCurrentApp(sessionId) {
|
|
5678
|
+
debugLog("[AppManager] closeCurrentApp");
|
|
5679
|
+
await this._dmk.sendCommand({
|
|
5680
|
+
sessionId,
|
|
5681
|
+
command: new CloseAppCommand()
|
|
5682
|
+
});
|
|
5683
|
+
}
|
|
5684
|
+
/**
|
|
5685
|
+
* Poll the device until the expected app is reported as running,
|
|
5686
|
+
* or throw after `_maxRetries` attempts.
|
|
5687
|
+
*/
|
|
5688
|
+
async _waitForApp(sessionId, expectedAppName) {
|
|
5689
|
+
let lastSeen = "";
|
|
5690
|
+
for (let i = 0; i < this._maxRetries; i++) {
|
|
5691
|
+
await this._wait();
|
|
5692
|
+
const current = await this._getCurrentApp(sessionId);
|
|
5693
|
+
lastSeen = current;
|
|
5694
|
+
if (current === expectedAppName) {
|
|
5695
|
+
return;
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
debugLog(
|
|
5699
|
+
"[AppManager] waitForApp exhausted: expected=",
|
|
5700
|
+
expectedAppName,
|
|
5701
|
+
"lastSeen=",
|
|
5702
|
+
lastSeen
|
|
5703
|
+
);
|
|
5704
|
+
throw new Error(
|
|
5705
|
+
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
5706
|
+
);
|
|
5707
|
+
}
|
|
5708
|
+
_isDashboard(appName) {
|
|
5709
|
+
return appName === DASHBOARD_APP_NAME;
|
|
5710
|
+
}
|
|
5711
|
+
_wait() {
|
|
5712
|
+
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
5713
|
+
}
|
|
5714
|
+
};
|
|
5590
5715
|
export {
|
|
5591
5716
|
AppManager,
|
|
5592
5717
|
DmkTransport,
|