Skip to content
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
4daefc5
feat(hardware): add complete OneKey Pro 2 integration (#11814)
wabicai Aug 5, 2026
1c84b57
feat(hardware): support Neo Protocol V2 devices (#12766)
wabicai Aug 6, 2026
d689378
chore: update hardware SDK to alpha.69 (#12780)
wabicai Aug 6, 2026
c5b41eb
feat: harden firmware upgrade pipeline (#12656)
originalix Aug 8, 2026
bfbb41b
fix: load Protocol V2 resource archives (#12788)
wabicai Aug 10, 2026
c634641
fix: pro2 bug fix (#12804)
wabicai Aug 10, 2026
fbf9d67
fix: upgrade hardware SDK to 1.2.0-alpha.101 (#12808)
originalix Aug 10, 2026
0844092
Feat/hw fixes on hotfix652 OK-57549 OK-58650 OK-58579 (#12795)
ByteZhang1024 Aug 11, 2026
25d41f4
fix: handle onboarding device scan errors (#12830)
wabicai Aug 11, 2026
18d2c33
fix: avoid legacy inputs in prepared firmware V3 (#12828)
originalix Aug 11, 2026
2fc9dea
fix: harden Pro2/Neo recovery, BLE, and resource uploads (#12809)
wabicai Aug 11, 2026
f92b1b5
fix: NFT collect fixes (OK-59979 OK-59980) (#12836)
originalix Aug 11, 2026
c1a8134
chore: upgrade sdk (#12838)
originalix Aug 11, 2026
33dc57b
fix: align firmware update device details (#12829)
originalix Aug 12, 2026
b98acf1
fix: close device setting select after change (#12841)
originalix Aug 12, 2026
580cb7d
fix: align prerelease firmware integrity validation (#12843)
originalix Aug 12, 2026
972d385
fix: restore Extension firmware update flow (#12851)
originalix Aug 13, 2026
dfae70f
fix: avoid desktop sync IPC for time cache (#12853)
huhuanming Aug 13, 2026
0ddaf46
fix: remove Pro 2 from USB connect device label (#12855)
originalix Aug 13, 2026
475f6d7
chore: upgrade hardware sdk to alpha.118 (#12856)
wabicai Aug 13, 2026
2d46939
chore: upgrade hardware sdk to alpha.119 (#12865)
originalix Aug 13, 2026
ca383fd
fix: skip ble pairing dialog for live ble session OK-60091 (#12861)
originalix Aug 13, 2026
31b876d
fix: correct Pro2 settings and label sync(OK-60188, OK-59745, OK-6025…
wabicai Aug 13, 2026
0e8a936
fix: restore firmware-switch device management behavior (OK-60205) (#…
originalix Aug 14, 2026
65c2c86
fix: sync portfolio on idle desktop BLE links (#12845)
wabicai Aug 14, 2026
415bd25
fix: sync V1 device-side settings changes after device operations (OK…
originalix Aug 14, 2026
e97fb2c
fix: support Pro2 and Neo BLE flows (#12871)
wabicai Aug 14, 2026
4f3240e
fix: sync hardware connection status after reset(OK-60117) (#12892)
originalix Aug 15, 2026
8c27282
fix: use SDK dimensions for Pro2 images (#12894)
wabicai Aug 15, 2026
8636eec
chore: upgrade hardware SDK to alpha.129 (#12896)
wabicai Aug 16, 2026
8434ce4
fix: sync Pro2 settings and send BLE cancel immediately (#12899)
wabicai Aug 17, 2026
e5486af
Fix/trezor ble row heal on hotfix652 OK-60092 (#12900)
ByteZhang1024 Aug 17, 2026
532ca6d
fix: align hardware errors, wallet state, and Pro2 workflows (#12905)
wabicai Aug 18, 2026
1068b9d
fix: resolve third-party hardware wallet avatars per device model OK-…
ByteZhang1024 Aug 18, 2026
43bae11
fix: seed canonical device state before hidden wallet passphrase sess…
originalix Aug 18, 2026
c5afee1
test: cover manifest-free Pro2 RESC archives (#12924)
wabicai Aug 18, 2026
5eae8a6
fix: correct Ledger Nano Gen5/Flex asset swap and legacy Trezor One c…
ByteZhang1024 Aug 18, 2026
b9f8dcc
fix: let SDK own hardware Cancel and bump SDK 148 (#12917)
wabicai Aug 19, 2026
525d64a
fix: restore desktop usb routing OK-60693 (#12940)
originalix Aug 19, 2026
43a0b6c
fix: cache the Earn banner list and keep rich text inline when clampe…
ezailWang Aug 19, 2026
d7b20ff
fix: detect hardware disconnect on desktop USB and BLE (OK-60486) (#1…
originalix Aug 19, 2026
125dc9d
fix: validate Electron update package before install (OK-59014, OK-58…
huhuanming Aug 20, 2026
4291387
fix: align Pro2/Neo firmware verify, homescreen and BLE name compatib…
wabicai Aug 20, 2026
01ab553
fix: support iOS Fabric WebView navigation (#12947)
originalix Aug 20, 2026
d3e6a4d
fix: prevent iOS address list liquid glass blur (#12948)
originalix Aug 20, 2026
fb7ea90
fix: align Pro2 firmware and portfolio behavior (#12950)
wabicai Aug 20, 2026
c56ce2b
fix: keep genuine-check verify dialog interactive on iOS retry (#12951)
originalix Aug 20, 2026
9313aea
fix: keep Android browser content above toolbar(OK-60781) (#12955)
limichange Aug 21, 2026
0103e4e
chore: upgrade hardware SDK to 1.2.0-alpha.176 (#12954)
wabicai Aug 21, 2026
6de2050
fix: show Protocol V2 custom wallpapers (#12956)
wabicai Aug 21, 2026
6e7ad6b
fix: reconcile hotfix/v6.5.2 sync residuals with x
originalix Aug 21, 2026
3e8d66e
fix: resolve sync PR lint and unit test failures
originalix Aug 21, 2026
951144e
ci: raise startup budgets for 6.5.2 hardware SDK weight
originalix Aug 21, 2026
043bf6b
ci: allow eager HardwarePortfolioSync simpleDb entity in bundle archi…
originalix Aug 21, 2026
3254707
fix: make hardwarePortfolioSync simpleDb getter lazy and add missing …
originalix Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,9 @@ Cases are appended by AI after each bug fix. Do NOT reorder or delete entries
**Root Cause**: `swapTypeSwitchAction` remapped From/To when the Pro target equaled the restored FromToken, but `SwapHeaderContainer` synced the account from the pre-switch `fromToken.networkId` captured in the React closure.
**Fix**: Return the settled FromToken from `swapTypeSwitchAction` and sync the account network from that value after the type switch leaves the Pro owner.
**Catchable by**: Section 4: Logic moved between files carries its surrounding guard/condition and scope; Section 4: Data flow end-to-end after a state remapping
## Case: BLE pairing dialog shown while device already paired and communicating
**Date**: 2026-08-13 | **Platforms**: Desktop (macOS/Windows desktop BLE)
**Symptom**: Creating a wallet over Bluetooth showed the "Pairing with your device" dialog mid-flow (OK-60091) even though the device was OS-paired and actively communicating on a live Noble session; the dialog's repair then re-scanned and "discovered" the very connectId the caller passed in.
**Root Cause**: `getCompatibleConnectId` triggered the USB→BLE pairing repair purely from DB bookkeeping (device record missing `bleConnectId` — recreated that way by a USB wallet creation after wallet removal deleted the record), never recognizing the caller's incoming connectId as the live BLE endpoint. DB binding state is neither necessary nor sufficient evidence of OS pairing state.
**Fix**: Before the dialog fallback, silently verify and persist the caller-held connectId, gated by runtime evidence: id differs from the record's USB identifiers, carried real device traffic within 60s (stamped by DEVICE.STATE/DEVICE.CONNECT, invalidated on DEVICE.DISCONNECT), probed with silentMode (no global error dialog from error constructors), a bounded 10s timeout, and the session's remembered protocol pinned (forced re-detection sends a V2 Ping into an active V1 session, which the device may not answer — SDK error 713); the probed deviceId must match before persisting.
**Catchable by**: NEW — not covered (interactive dialog triggered from persistence bookkeeping instead of live transport evidence)
6 changes: 3 additions & 3 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
},
"dependencies": {
"@napi-rs/keyring": "^1.3.0",
"@onekeyfe/hd-common-connect-sdk": "1.1.34-alpha.0",
"@onekeyfe/hd-core": "1.1.34-alpha.0",
"@onekeyfe/hd-transport-usb": "1.1.34-alpha.0",
"@onekeyfe/hd-common-connect-sdk": "1.2.0-alpha.176",
"@onekeyfe/hd-core": "1.2.0-alpha.176",
"@onekeyfe/hd-transport-usb": "1.2.0-alpha.176",
"proper-lockfile": "^4.1.2"
}
}
10 changes: 6 additions & 4 deletions apps/cli/src/__tests__/btc-signer-hardware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,16 @@ function makeDeps(overrides: { sdk?: Partial<CoreApi> } = {}): {
} {
const device = makeDevice();
const sdk = {
getFeatures: jest.fn(async () => makeSuccess({ unlocked: true })),
getDeviceState: jest.fn(async () =>
makeSuccess({ status: { unlocked: true } }),
),
deviceUnlock: jest.fn(async () => makeSuccess({})),
searchDevices: jest.fn(async () =>
makeSuccess([
{
connectId: device.connectId,
deviceId: device.deviceId,
features: { device_id: device.deviceId, session_id: 'session-123' },
sessionId: 'session-123',
},
]),
),
Expand Down Expand Up @@ -101,8 +103,8 @@ function makeDeps(overrides: { sdk?: Partial<CoreApi> } = {}): {
async () => sdk,
) as unknown as ISignerHardwareDeps['ensureSDKReady'],
installPassphraseProvider,
resolvePassphraseStateByMode:
jest.fn() as unknown as ISignerHardwareDeps['resolvePassphraseStateByMode'],
resolvePassphraseSessionByMode:
jest.fn() as unknown as ISignerHardwareDeps['resolvePassphraseSessionByMode'],
keychainFactory: () => ({
get: jest.fn(async () => null),
set: jest.fn(async () => undefined),
Expand Down
36 changes: 36 additions & 0 deletions apps/cli/src/__tests__/device-search-command.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { formatSearchedDevice } from '../commands/device/device-search';

describe('device search formatting', () => {
it('prefers the canonical display name over the transport name', () => {
expect(
formatSearchedDevice({
connectId: 'PRO2_USB',
name: 'Pro2 6136',
displayName: 'My Pro 2',
deviceType: 'pro2',
}),
).toMatchObject({
name: 'My Pro 2',
model: 'pro2',
});
});

it('prefers serialNo while keeping the legacy uuid fallback', () => {
expect(
formatSearchedDevice({
serialNo: 'SERIAL-NO',
uuid: 'LEGACY-UUID',
}),
).toMatchObject({
serial: 'SERIAL-NO',
});

expect(
formatSearchedDevice({
uuid: 'LEGACY-UUID',
}),
).toMatchObject({
serial: 'LEGACY-UUID',
});
});
});
57 changes: 27 additions & 30 deletions apps/cli/src/__tests__/evm-signer-hardware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* - passphrase mode 'none' → useEmptyPassphrase branch, no keychain reads
* - passphrase mode 'on_host' → keychain preload hits session cache
* - passphrase mode 'on_host' with deviceWasLocked → keychain skipped,
* resolvePassphraseStateByMode fallback, result re-persisted
* resolvePassphraseSessionByMode fallback, result re-persisted
* - buildHardwareEvmTransaction + buildSignedTxFromSignatureEvm wired in
* for both EIP-1559 and legacy shapes
* - signMessage throws when path missing
Expand Down Expand Up @@ -45,9 +45,8 @@ const MOCK_SID_FROM_KEYCHAIN = 'sess_N1fKj3BvP4kRZ';
const MOCK_STALE_PS = 'stalePsXyz7HkLm9';
const MOCK_STALE_SID = 'staleSidAbc4JkRp';
const MOCK_RESOLVED_PS = 'freshResolveWxYz23';
// Returned by sdk.searchDevices() after a fresh getPassphraseState — this
// is the session_id that persistPassphraseState must capture and write to
// the keychain to replace the now-invalid stale one.
// Returned by getPassphraseState after a fresh hidden-wallet resolve. The
// session_id must be written with the matching passphraseState.
const MOCK_FRESH_SID_AFTER_RESOLVE = 'sess_freshAfterUnlockK7';

const DEVICE: DeviceInfo = {
Expand All @@ -71,7 +70,7 @@ function makeDeps(
keychainGet: jest.Mock;
keychainSet: jest.Mock;
installPassphraseProvider: jest.Mock;
resolvePassphraseStateByMode: jest.Mock;
resolvePassphraseSessionByMode: jest.Mock;
preloadSessionCache: jest.Mock;
stderrWrite: jest.Mock;
};
Expand All @@ -90,21 +89,16 @@ function makeDeps(
});

const sdk = {
getFeatures: jest.fn(async () =>
makeSuccess({ unlocked: overrides.unlocked ?? true }),
getDeviceState: jest.fn(async () =>
makeSuccess({ status: { unlocked: overrides.unlocked ?? true } }),
),
deviceUnlock: jest.fn(async () => makeSuccess({})),
// searchDevices is invoked by persistPassphraseState to discover the
// session_id the device just minted for the freshly-resolved passphrase.
searchDevices: jest.fn(async () =>
makeSuccess([
{
connectId: DEVICE.connectId,
deviceId: DEVICE.deviceId,
features: {
device_id: DEVICE.deviceId,
session_id: MOCK_FRESH_SID_AFTER_RESOLVE,
},
sessionId: MOCK_FRESH_SID_AFTER_RESOLVE,
},
]),
),
Expand All @@ -125,17 +119,21 @@ function makeDeps(
// passphraseState from the SDK is an opaque ASCII token (base58-ish,
// not hex). The default here matches the real-world shape so the
// keychain utf-8 round-trip stays honest under test.
const resolvePassphraseStateByMode =
overrides.resolveByMode ?? jest.fn(async () => MOCK_RESOLVED_PS);
const resolvePassphraseSessionByMode =
overrides.resolveByMode ??
jest.fn(async () => ({
passphraseState: MOCK_RESOLVED_PS,
sessionId: MOCK_FRESH_SID_AFTER_RESOLVE,
}));
const preloadSessionCache = overrides.preloadSessionCache ?? jest.fn();
const stderrWrite = jest.fn(() => true);

const deps: ISignerHardwareDeps = {
ensureSDKReady:
ensureSDKReady as unknown as ISignerHardwareDeps['ensureSDKReady'],
installPassphraseProvider,
resolvePassphraseStateByMode:
resolvePassphraseStateByMode as unknown as ISignerHardwareDeps['resolvePassphraseStateByMode'],
resolvePassphraseSessionByMode:
resolvePassphraseSessionByMode as unknown as ISignerHardwareDeps['resolvePassphraseSessionByMode'],
keychainFactory: () => ({
get: keychainGet,
set: keychainSet,
Expand All @@ -152,7 +150,7 @@ function makeDeps(
keychainGet,
keychainSet,
installPassphraseProvider,
resolvePassphraseStateByMode,
resolvePassphraseSessionByMode,
preloadSessionCache,
stderrWrite,
},
Expand Down Expand Up @@ -229,7 +227,7 @@ describe('SignerHardware', () => {

expect(addr).toEqual({ address: '0xabc', path: "m/44'/60'/0'/0/0" });
expect(mocks.keychainGet).not.toHaveBeenCalled();
expect(mocks.resolvePassphraseStateByMode).not.toHaveBeenCalled();
expect(mocks.resolvePassphraseSessionByMode).not.toHaveBeenCalled();
expect(mocks.preloadSessionCache).not.toHaveBeenCalled();

const callArgs = mocks.sdk.evmGetAddress.mock.calls[0];
Expand Down Expand Up @@ -280,7 +278,7 @@ describe('SignerHardware', () => {
MOCK_PS_FROM_KEYCHAIN,
MOCK_SID_FROM_KEYCHAIN,
);
expect(mocks.resolvePassphraseStateByMode).not.toHaveBeenCalled();
expect(mocks.resolvePassphraseSessionByMode).not.toHaveBeenCalled();
const params = mocks.sdk.evmGetAddress.mock.calls[0][2];
expect(params).toMatchObject({
passphraseState: MOCK_PS_FROM_KEYCHAIN,
Expand Down Expand Up @@ -309,8 +307,9 @@ describe('SignerHardware', () => {
await signer.getAddress('evm--1');

expect(mocks.sdk.deviceUnlock).toHaveBeenCalledWith(DEVICE.connectId, {});
expect(mocks.resolvePassphraseStateByMode).toHaveBeenCalledWith(
expect(mocks.resolvePassphraseSessionByMode).toHaveBeenCalledWith(
DEVICE.connectId,
DEVICE.deviceId,
'on_host',
);

Expand All @@ -328,8 +327,9 @@ describe('SignerHardware', () => {
Buffer.from(MOCK_FRESH_SID_AFTER_RESOLVE, 'utf-8'),
);

// searchDevices is the source of the fresh session_id post-resolve.
expect(mocks.sdk.searchDevices).toHaveBeenCalled();
// session_id now comes from the getPassphraseState payload directly.
// searchDevices may still run during signer init for connectId refresh,
// but it is no longer the source of the freshly resolved session_id.

// After persisting, warm the SDK in-process cache with the new
// (deviceId, freshPassphraseState, freshSessionId) triple so any
Expand Down Expand Up @@ -358,8 +358,9 @@ describe('SignerHardware', () => {

await signer.getAddress('evm--1');

expect(mocks.resolvePassphraseStateByMode).toHaveBeenCalledWith(
expect(mocks.resolvePassphraseSessionByMode).toHaveBeenCalledWith(
DEVICE.connectId,
DEVICE.deviceId,
'on_host',
);
// Both keys must be written so the next process can preload a valid
Expand All @@ -376,7 +377,7 @@ describe('SignerHardware', () => {

it('throws when hidden wallet resolve returns undefined instead of silently using standard wallet', async () => {
const { deps } = makeDeps({
resolveByMode: jest.fn(async () => undefined),
resolveByMode: jest.fn(async () => ({})),
});
const signer = new SignerHardware({
device: DEVICE,
Expand Down Expand Up @@ -563,10 +564,7 @@ describe('SignerHardware', () => {
{
connectId: FRESH_CONNECT_ID,
deviceId: DEVICE.deviceId,
features: {
device_id: DEVICE.deviceId,
session_id: MOCK_FRESH_SID_AFTER_RESOLVE,
},
sessionId: MOCK_FRESH_SID_AFTER_RESOLVE,
},
]),
),
Expand Down Expand Up @@ -620,7 +618,6 @@ describe('SignerHardware', () => {
{
connectId: 'other-device-connect',
deviceId: 'other-device-id',
features: { device_id: 'other-device-id' },
},
]),
),
Expand Down
Loading
Loading