@onekeyfe/hd-core 1.2.0-alpha.34 → 1.2.0-alpha.36
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/__tests__/AllNetworkGetAddressBase.tracing.test.ts +74 -0
- package/__tests__/DeviceCommands.test.ts +84 -0
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/device-settings.test.ts +3 -0
- package/__tests__/device-wallet-session-store.test.ts +80 -7
- package/__tests__/evmSignTransaction.test.ts +69 -0
- package/__tests__/get-device-state.test.ts +180 -40
- package/__tests__/open-wallet-session.test.ts +314 -55
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +56 -0
- package/__tests__/protocol-v2.test.ts +768 -139
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/evm/protocol.d.ts +3 -0
- package/dist/api/evm/protocol.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/core/deviceEventRegistration.d.ts +2 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +25 -6
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceWalletSessionStore.d.ts +0 -1
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +27 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +63 -12
- package/dist/index.js +1164 -904
- package/dist/protocols/protocol-v2/features.d.ts +17 -6
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +1 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +3 -3
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +5 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -1
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +2 -2
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +2 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +1 -1
- package/src/api/FirmwareUpdateV4.ts +39 -14
- package/src/api/GetPassphraseState.ts +17 -0
- package/src/api/OpenWalletSession.ts +31 -8
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +24 -2
- package/src/api/device/DeviceUnlock.ts +1 -1
- package/src/api/evm/EVMGetAddress.ts +2 -2
- package/src/api/evm/EVMGetPublicKey.ts +2 -2
- package/src/api/evm/EVMSignMessage.ts +2 -2
- package/src/api/evm/EVMSignTransaction.ts +2 -2
- package/src/api/evm/EVMSignTypedData.ts +35 -45
- package/src/api/evm/EVMVerifyMessage.ts +2 -2
- package/src/api/evm/protocol.ts +6 -0
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +2 -2
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +46 -4
- package/src/data/messages/messages-protocol-v2.json +408 -485
- package/src/device/Device.ts +266 -43
- package/src/device/DeviceCommands.ts +12 -2
- package/src/device/DeviceWalletSessionStore.ts +0 -14
- package/src/deviceProfile/buildDeviceFeatures.ts +1 -1
- package/src/events/device.ts +4 -0
- package/src/events/ui-request.ts +32 -2
- package/src/protocols/protocol-v2/features.ts +65 -16
- package/src/protocols/protocol-v2/index.ts +5 -0
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +8 -3
- package/src/protocols/protocol-v2/uiInteraction.ts +31 -5
- package/src/protocols/protocol-v2/unlockRetry.ts +51 -18
- package/src/protocols/protocol-v2/walletSession.ts +172 -64
- package/src/types/api/openWalletSession.ts +1 -1
- package/src/types/api/protocolV2.ts +2 -2
- package/src/types/device.ts +2 -0
- package/src/utils/deviceFeaturesUtils.ts +4 -0
|
@@ -18,6 +18,79 @@ class TestAllNetworkMethod extends AllNetworkGetAddressBase {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
describe('AllNetworkGetAddressBase tracing', () => {
|
|
21
|
+
test('resumes a Protocol V2 hidden wallet before running a nested chain method', async () => {
|
|
22
|
+
const calls: string[] = [];
|
|
23
|
+
const checkPassphraseStateSafety = jest.fn().mockImplementation(() => {
|
|
24
|
+
calls.push('resume-hidden-session');
|
|
25
|
+
return Promise.resolve(true);
|
|
26
|
+
});
|
|
27
|
+
const innerMethod = {
|
|
28
|
+
checkSafetyLevelOnTestNet: jest.fn().mockResolvedValue(false),
|
|
29
|
+
connectId: 'connect-id',
|
|
30
|
+
deviceId: 'device-id',
|
|
31
|
+
getVersionRange: jest.fn().mockReturnValue({}),
|
|
32
|
+
assertProtocolSupported: jest.fn(),
|
|
33
|
+
init: jest.fn(),
|
|
34
|
+
name: 'evmGetAddress',
|
|
35
|
+
responseID: 43,
|
|
36
|
+
run: jest.fn().mockImplementation(() => {
|
|
37
|
+
calls.push('run-chain-method');
|
|
38
|
+
return Promise.resolve([{ address: '0xhidden' }]);
|
|
39
|
+
}),
|
|
40
|
+
setDevice: jest.fn(),
|
|
41
|
+
strictCheckDeviceSupport: false,
|
|
42
|
+
};
|
|
43
|
+
(findMethod as jest.Mock).mockReturnValue(innerMethod);
|
|
44
|
+
const method = new TestAllNetworkMethod({
|
|
45
|
+
id: 1,
|
|
46
|
+
payload: {
|
|
47
|
+
method: 'allNetworkGetAddress',
|
|
48
|
+
connectId: 'connect-id',
|
|
49
|
+
deviceId: 'device-id',
|
|
50
|
+
passphraseState: 'hidden-state',
|
|
51
|
+
bundle: [],
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
method.device = {
|
|
55
|
+
checkPassphraseStateSafety,
|
|
56
|
+
commands: {
|
|
57
|
+
typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }),
|
|
58
|
+
},
|
|
59
|
+
getCurrentFirmwareType: jest.fn(),
|
|
60
|
+
getProtocol: jest.fn().mockReturnValue('V2'),
|
|
61
|
+
getCurrentFirmwareVersionString: jest.fn().mockReturnValue('1.0.0'),
|
|
62
|
+
getCurrentMethodVersionRange: jest
|
|
63
|
+
.fn()
|
|
64
|
+
.mockImplementation((getRange: (type: string) => unknown) => getRange('pro2')),
|
|
65
|
+
instanceId: 'device-instance',
|
|
66
|
+
isProtocolV2: jest.fn().mockReturnValue(true),
|
|
67
|
+
isBootloader: jest.fn().mockReturnValue(false),
|
|
68
|
+
isRomloader: jest.fn().mockReturnValue(false),
|
|
69
|
+
off: jest.fn(),
|
|
70
|
+
on: jest.fn(),
|
|
71
|
+
state: { status: { unlocked: true } },
|
|
72
|
+
updateProtocolV2Status: jest.fn(),
|
|
73
|
+
} as any;
|
|
74
|
+
|
|
75
|
+
await method.callMethod(
|
|
76
|
+
'evmGetAddress',
|
|
77
|
+
{
|
|
78
|
+
bundle: [
|
|
79
|
+
{
|
|
80
|
+
_originRequestParams: {
|
|
81
|
+
network: 'evm',
|
|
82
|
+
path: "m/44'/60'/0'/0/0",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
0
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(checkPassphraseStateSafety).toHaveBeenCalledWith('hidden-state', false, undefined);
|
|
91
|
+
expect(calls).toEqual(['resume-hidden-session', 'run-chain-method']);
|
|
92
|
+
});
|
|
93
|
+
|
|
21
94
|
test('releases the nested request context when an unhandled error escapes', async () => {
|
|
22
95
|
const deviceInstanceId = 'device-instance';
|
|
23
96
|
const innerMethod = {
|
|
@@ -51,6 +124,7 @@ describe('AllNetworkGetAddressBase tracing', () => {
|
|
|
51
124
|
getCurrentMethodVersionRange: jest
|
|
52
125
|
.fn()
|
|
53
126
|
.mockImplementation((getRange: (type: string) => unknown) => getRange('classic')),
|
|
127
|
+
isProtocolV2: jest.fn().mockReturnValue(false),
|
|
54
128
|
off: jest.fn(),
|
|
55
129
|
on: jest.fn(),
|
|
56
130
|
} as any;
|
|
@@ -19,6 +19,90 @@ const createCommands = () => {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
describe('DeviceCommands failure mapping', () => {
|
|
22
|
+
it('logs passphrase call request and response without exposing the passphrase', async () => {
|
|
23
|
+
const commands = createCommands();
|
|
24
|
+
const requestLog = getLogger(LoggerNames.DeviceCommands);
|
|
25
|
+
const responseLog = getLogger(LoggerNames.Core);
|
|
26
|
+
requestLog.messages.length = 0;
|
|
27
|
+
responseLog.messages.length = 0;
|
|
28
|
+
commands.mainId = 'main-id';
|
|
29
|
+
commands.transport = {
|
|
30
|
+
call: jest.fn().mockResolvedValue({
|
|
31
|
+
type: 'Success',
|
|
32
|
+
message: {
|
|
33
|
+
message: 'Passphrase accepted',
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
} as any;
|
|
37
|
+
|
|
38
|
+
await expect(
|
|
39
|
+
commands.call('DeviceSessionAskPassphrase', {
|
|
40
|
+
passphrase: 'hidden-wallet-secret',
|
|
41
|
+
on_device: false,
|
|
42
|
+
})
|
|
43
|
+
).resolves.toMatchObject({ type: 'Success' });
|
|
44
|
+
|
|
45
|
+
expect(requestLog.messages.at(-1)?.message).toEqual([
|
|
46
|
+
'[DeviceCommands] [call] Sending',
|
|
47
|
+
'DeviceSessionAskPassphrase',
|
|
48
|
+
{
|
|
49
|
+
passphrase: '[REDACTED]',
|
|
50
|
+
on_device: false,
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
expect(responseLog.messages.at(-1)?.message).toEqual([
|
|
54
|
+
'[DeviceCommands] [call] Received',
|
|
55
|
+
'Success',
|
|
56
|
+
{
|
|
57
|
+
message: 'Passphrase accepted',
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
expect(JSON.stringify([...requestLog.messages, ...responseLog.messages])).not.toContain(
|
|
61
|
+
'hidden-wallet-secret'
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('logs DeviceStatus response fields without exposing the device ID', async () => {
|
|
66
|
+
const commands = createCommands();
|
|
67
|
+
const log = getLogger(LoggerNames.Core);
|
|
68
|
+
log.messages.length = 0;
|
|
69
|
+
commands.mainId = 'main-id';
|
|
70
|
+
commands.transport = {
|
|
71
|
+
call: jest.fn().mockResolvedValue({
|
|
72
|
+
type: 'DeviceStatus',
|
|
73
|
+
message: {
|
|
74
|
+
device_id: 'sensitive-device-id',
|
|
75
|
+
unlocked: true,
|
|
76
|
+
init_states: true,
|
|
77
|
+
backup_required: false,
|
|
78
|
+
passphrase_enabled: true,
|
|
79
|
+
attach_to_pin_enabled: false,
|
|
80
|
+
unlocked_by_attach_to_pin: false,
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
} as any;
|
|
84
|
+
|
|
85
|
+
await expect(commands.call('DeviceStatusGet', {})).resolves.toMatchObject({
|
|
86
|
+
type: 'DeviceStatus',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const receivedLog = log.messages.at(-1)?.message;
|
|
90
|
+
expect(receivedLog).toEqual([
|
|
91
|
+
'[DeviceCommands] [call] Received',
|
|
92
|
+
'DeviceStatus',
|
|
93
|
+
{
|
|
94
|
+
device_id: '[REDACTED]',
|
|
95
|
+
unlocked: true,
|
|
96
|
+
init_states: true,
|
|
97
|
+
backup_required: false,
|
|
98
|
+
passphrase_enabled: true,
|
|
99
|
+
attach_to_pin_enabled: false,
|
|
100
|
+
unlocked_by_attach_to_pin: false,
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
103
|
+
expect(JSON.stringify(receivedLog)).not.toContain('sensitive-device-id');
|
|
104
|
+
});
|
|
105
|
+
|
|
22
106
|
it('does not log DeviceSessionGet response secrets', async () => {
|
|
23
107
|
const commands = createCommands();
|
|
24
108
|
const log = getLogger(LoggerNames.DeviceCommands);
|
|
@@ -3,6 +3,8 @@ import { registerHardwareUiEventListeners } from '../src/core/deviceEventRegistr
|
|
|
3
3
|
|
|
4
4
|
const handlers = {
|
|
5
5
|
pin: jest.fn(),
|
|
6
|
+
pinOnDevice: jest.fn(),
|
|
7
|
+
pinOnDeviceComplete: jest.fn(),
|
|
6
8
|
button: jest.fn(),
|
|
7
9
|
passphrase: jest.fn(),
|
|
8
10
|
passphraseOnDevice: jest.fn(),
|
|
@@ -21,6 +23,8 @@ describe('hardware UI event registration', () => {
|
|
|
21
23
|
expect(registerHardwareUiEventListeners(device as any, handlers)).toBe(true);
|
|
22
24
|
expect(device.on.mock.calls.map(([type]) => type)).toEqual([
|
|
23
25
|
DEVICE.PIN,
|
|
26
|
+
DEVICE.PIN_ON_DEVICE,
|
|
27
|
+
DEVICE.PIN_ON_DEVICE_COMPLETE,
|
|
24
28
|
DEVICE.BUTTON,
|
|
25
29
|
DEVICE.PASSPHRASE,
|
|
26
30
|
DEVICE.PASSPHRASE_ON_DEVICE,
|
|
@@ -34,6 +38,8 @@ describe('hardware UI event registration', () => {
|
|
|
34
38
|
expect(registerHardwareUiEventListeners(device as any, handlers)).toBe(true);
|
|
35
39
|
expect(device.on.mock.calls.map(([type]) => type)).toEqual([
|
|
36
40
|
DEVICE.PIN,
|
|
41
|
+
DEVICE.PIN_ON_DEVICE,
|
|
42
|
+
DEVICE.PIN_ON_DEVICE_COMPLETE,
|
|
37
43
|
DEVICE.BUTTON,
|
|
38
44
|
DEVICE.PASSPHRASE,
|
|
39
45
|
DEVICE.PASSPHRASE_ON_DEVICE,
|
|
@@ -40,6 +40,9 @@ describe('DeviceSettings protocol routing', () => {
|
|
|
40
40
|
['language', { language: 'ja-JP' }],
|
|
41
41
|
['brightness', { brightness: 80 }],
|
|
42
42
|
['haptic feedback', { hapticFeedback: true }],
|
|
43
|
+
['animation', { animationEnabled: true }],
|
|
44
|
+
['tap to wake', { tapToWake: true }],
|
|
45
|
+
['device name display', { deviceNameDisplayEnabled: true }],
|
|
43
46
|
])('does not unlock Protocol V2 before changing %s', (_name, settings) => {
|
|
44
47
|
const method = new DeviceSettings({
|
|
45
48
|
id: 1,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import ClearSessionCache from '../src/api/ClearSessionCache';
|
|
4
|
+
import GetPassphraseState from '../src/api/GetPassphraseState';
|
|
4
5
|
import DeviceWipe from '../src/api/device/DeviceWipe';
|
|
5
6
|
import {
|
|
6
7
|
DeviceWalletSessionStore,
|
|
@@ -35,15 +36,18 @@ describe('DeviceWalletSessionStore', () => {
|
|
|
35
36
|
expect(store.get('device-2', 'hidden-a')).toBe('session-c');
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
test('
|
|
39
|
+
test('keeps SDK session handles without mirroring hardware eviction policy', () => {
|
|
39
40
|
const store = new DeviceWalletSessionStore();
|
|
40
41
|
store.setStandard('device-1', 'wallet-a', 'session-a');
|
|
41
42
|
store.set('device-1', 'wallet-b', 'session-b');
|
|
42
43
|
store.set('device-1', 'wallet-c', 'session-c');
|
|
43
44
|
store.set('device-1', 'wallet-d', 'session-d');
|
|
44
45
|
|
|
45
|
-
expect(store.get('device-1', 'wallet-a')).
|
|
46
|
-
expect(store.getStandard('device-1')).
|
|
46
|
+
expect(store.get('device-1', 'wallet-a')).toBe('session-a');
|
|
47
|
+
expect(store.getStandard('device-1')).toEqual({
|
|
48
|
+
passphraseState: 'wallet-a',
|
|
49
|
+
sessionId: 'session-a',
|
|
50
|
+
});
|
|
47
51
|
expect(store.get('device-1', 'wallet-b')).toBe('session-b');
|
|
48
52
|
expect(store.get('device-1', 'wallet-c')).toBe('session-c');
|
|
49
53
|
expect(store.get('device-1', 'wallet-d')).toBe('session-d');
|
|
@@ -94,7 +98,7 @@ describe('DeviceWalletSessionStore', () => {
|
|
|
94
98
|
expect(store.getPending('stable-device-id')).toBe('pending-session');
|
|
95
99
|
});
|
|
96
100
|
|
|
97
|
-
test('keeps
|
|
101
|
+
test('keeps every known handle when descriptor sessions merge into a stable device id', () => {
|
|
98
102
|
const store = new DeviceWalletSessionStore();
|
|
99
103
|
store.setStandard('stable-device-id', 'wallet-a', 'session-a');
|
|
100
104
|
store.set('stable-device-id', 'wallet-b', 'session-b');
|
|
@@ -106,8 +110,11 @@ describe('DeviceWalletSessionStore', () => {
|
|
|
106
110
|
nextDeviceId: 'stable-device-id',
|
|
107
111
|
});
|
|
108
112
|
|
|
109
|
-
expect(store.get('stable-device-id', 'wallet-a')).
|
|
110
|
-
expect(store.getStandard('stable-device-id')).
|
|
113
|
+
expect(store.get('stable-device-id', 'wallet-a')).toBe('session-a');
|
|
114
|
+
expect(store.getStandard('stable-device-id')).toEqual({
|
|
115
|
+
passphraseState: 'wallet-a',
|
|
116
|
+
sessionId: 'session-a',
|
|
117
|
+
});
|
|
111
118
|
expect(store.get('stable-device-id', 'wallet-b')).toBe('session-b');
|
|
112
119
|
expect(store.get('stable-device-id', 'wallet-c')).toBe('session-c');
|
|
113
120
|
expect(store.get('stable-device-id', 'wallet-d')).toBe('session-d');
|
|
@@ -402,4 +409,70 @@ describe('Protocol V1 wallet identity initialization', () => {
|
|
|
402
409
|
expect.any(Object)
|
|
403
410
|
);
|
|
404
411
|
});
|
|
412
|
+
|
|
413
|
+
test('selects the standard V1 wallet after a non-destructive live identity read', async () => {
|
|
414
|
+
const device = Device.fromDescriptor({ id: 'connect-a', path: 'connect-a' } as never);
|
|
415
|
+
device.features = {
|
|
416
|
+
protocol: 'V1',
|
|
417
|
+
deviceId: 'device-a',
|
|
418
|
+
unlocked: true,
|
|
419
|
+
passphraseProtection: false,
|
|
420
|
+
} as never;
|
|
421
|
+
const typedCall = jest
|
|
422
|
+
.fn()
|
|
423
|
+
.mockResolvedValueOnce({ type: 'Features', message: { device_id: 'device-a' } })
|
|
424
|
+
.mockResolvedValueOnce({ type: 'Features', message: { device_id: 'device-a' } });
|
|
425
|
+
device.commands = { typedCall } as never;
|
|
426
|
+
jest.spyOn(TransportManager, 'reconfigure').mockResolvedValue(undefined);
|
|
427
|
+
|
|
428
|
+
await device.initialize({ deviceId: 'device-a' });
|
|
429
|
+
|
|
430
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
431
|
+
expect(typedCall).toHaveBeenNthCalledWith(1, 'GetFeatures', 'Features', {});
|
|
432
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
433
|
+
2,
|
|
434
|
+
'Initialize',
|
|
435
|
+
'Features',
|
|
436
|
+
{
|
|
437
|
+
passphrase_state: undefined,
|
|
438
|
+
is_contains_attach: true,
|
|
439
|
+
},
|
|
440
|
+
expect.any(Object)
|
|
441
|
+
);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
describe('Protocol V1 passphrase state classification', () => {
|
|
446
|
+
test('does not expose a standard-wallet address as passphrase state', async () => {
|
|
447
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
448
|
+
type: 'Address',
|
|
449
|
+
message: { address: 'standard-wallet-address' },
|
|
450
|
+
});
|
|
451
|
+
const method = new GetPassphraseState({
|
|
452
|
+
payload: {
|
|
453
|
+
method: 'getPassphraseState',
|
|
454
|
+
connectId: 'connect-a',
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
method.device = {
|
|
458
|
+
features: {},
|
|
459
|
+
commands: { typedCall },
|
|
460
|
+
isProtocolV2: () => false,
|
|
461
|
+
getCurrentDeviceType: () => EDeviceType.Classic,
|
|
462
|
+
getCurrentFirmwareVersionString: () => '1.0.0',
|
|
463
|
+
getCurrentPassphraseProtection: () => false,
|
|
464
|
+
getCurrentDeviceId: () => 'device-a',
|
|
465
|
+
getFeatures: jest.fn(),
|
|
466
|
+
getInternalState: () => undefined,
|
|
467
|
+
updateInternalState: jest.fn(),
|
|
468
|
+
} as never;
|
|
469
|
+
|
|
470
|
+
await expect(method.run()).resolves.toBeUndefined();
|
|
471
|
+
expect(typedCall).toHaveBeenCalledWith('GetAddress', 'Address', {
|
|
472
|
+
address_n: expect.any(Array),
|
|
473
|
+
coin_name: 'Testnet',
|
|
474
|
+
script_type: 'SPENDADDRESS',
|
|
475
|
+
show_display: false,
|
|
476
|
+
});
|
|
477
|
+
});
|
|
405
478
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import EVMSignTransaction from '../src/api/evm/EVMSignTransaction';
|
|
2
|
+
import TransportManager from '../src/data-manager/TransportManager';
|
|
2
3
|
|
|
3
4
|
import type { EVMTransactionEIP7702 } from '../src/types';
|
|
4
5
|
|
|
@@ -17,13 +18,17 @@ jest.mock('../src/device/Device', () => ({
|
|
|
17
18
|
Device: jest.fn(),
|
|
18
19
|
}));
|
|
19
20
|
|
|
21
|
+
const mockTransportManager = jest.mocked(TransportManager);
|
|
22
|
+
|
|
20
23
|
describe('EVMSignTransaction EIP-7702', () => {
|
|
21
24
|
let mockDevice: any;
|
|
22
25
|
let mockTypedCall: jest.Mock;
|
|
23
26
|
|
|
24
27
|
beforeEach(() => {
|
|
28
|
+
mockTransportManager.getProtocolV1MessageSchema.mockReturnValue('v1CurrentSchema');
|
|
25
29
|
mockTypedCall = jest.fn();
|
|
26
30
|
mockDevice = {
|
|
31
|
+
isProtocolV2: jest.fn(() => false),
|
|
27
32
|
commands: {
|
|
28
33
|
typedCall: {
|
|
29
34
|
bind: jest.fn(() => mockTypedCall),
|
|
@@ -32,6 +37,70 @@ describe('EVMSignTransaction EIP-7702', () => {
|
|
|
32
37
|
};
|
|
33
38
|
});
|
|
34
39
|
|
|
40
|
+
describe('Protocol-specific schema routing', () => {
|
|
41
|
+
const transaction: EVMTransactionEIP7702 = {
|
|
42
|
+
to: '0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9',
|
|
43
|
+
value: '0x0',
|
|
44
|
+
gasLimit: '0x5208',
|
|
45
|
+
nonce: '0x0',
|
|
46
|
+
chainId: 1,
|
|
47
|
+
maxFeePerGas: '0xbebc200',
|
|
48
|
+
maxPriorityFeePerGas: '0x9502f900',
|
|
49
|
+
authorizationList: [
|
|
50
|
+
{
|
|
51
|
+
chainId: 1,
|
|
52
|
+
address: '0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9',
|
|
53
|
+
nonce: '0x1',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const createTransactionMethod = () => {
|
|
59
|
+
const method = new EVMSignTransaction({
|
|
60
|
+
id: 1,
|
|
61
|
+
payload: {
|
|
62
|
+
method: 'evmSignTransaction',
|
|
63
|
+
path: "m/44'/60'/0'/0/0",
|
|
64
|
+
transaction,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
method.device = mockDevice;
|
|
68
|
+
method.init();
|
|
69
|
+
return method;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
it('keeps Protocol V2 on current OneKey messages after a legacy V1 device', async () => {
|
|
73
|
+
mockTransportManager.getProtocolV1MessageSchema.mockReturnValue('v1LegacySchema');
|
|
74
|
+
mockDevice.isProtocolV2.mockReturnValue(true);
|
|
75
|
+
mockTypedCall.mockResolvedValue({
|
|
76
|
+
message: {
|
|
77
|
+
signature_v: 27,
|
|
78
|
+
signature_r: '01',
|
|
79
|
+
signature_s: '02',
|
|
80
|
+
authorization_signatures: [],
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await expect(createTransactionMethod().run()).resolves.toMatchObject({
|
|
85
|
+
v: '0x1b',
|
|
86
|
+
});
|
|
87
|
+
expect(mockTypedCall).toHaveBeenCalledWith(
|
|
88
|
+
'EthereumSignTxEIP7702OneKey',
|
|
89
|
+
'EthereumTxRequestOneKey',
|
|
90
|
+
expect.any(Object)
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('continues to use legacy messages for a Protocol V1 legacy device', async () => {
|
|
95
|
+
mockTransportManager.getProtocolV1MessageSchema.mockReturnValue('v1LegacySchema');
|
|
96
|
+
|
|
97
|
+
await expect(createTransactionMethod().run()).rejects.toThrow(
|
|
98
|
+
'EIP7702 not supported by Trezor'
|
|
99
|
+
);
|
|
100
|
+
expect(mockTypedCall).not.toHaveBeenCalled();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
35
104
|
describe('EIP-7702 Transaction Detection', () => {
|
|
36
105
|
it('should detect EIP-7702 transaction with authorization list', () => {
|
|
37
106
|
const transaction: EVMTransactionEIP7702 = {
|