Skip to content

Commit de2aa94

Browse files
authored
Feature/add support for nexa (#259)
1 parent 3c2dd01 commit de2aa94

File tree

11 files changed

+413
-0
lines changed

11 files changed

+413
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ libDev
3131
dist
3232
coverage
3333
build
34+
.history

packages/core/src/api/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,6 @@ export { default as polkadotSignTransaction } from './polkadot/PolkadotSignTrans
103103

104104
export { default as kaspaGetAddress } from './kaspa/KaspaGetAddress';
105105
export { default as kaspaSignTransaction } from './kaspa/KaspaSignTransaction';
106+
107+
export { default as nexaGetAddress } from './nexa/NexaGetAddress';
108+
export { default as nexaSignTransaction } from './nexa/NexaSignTransaction';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { NexaGetAddress as HardwareNexaGetAddress } from '@onekeyfe/hd-transport';
2+
import { UI_REQUEST } from '../../constants/ui-request';
3+
import { serializedPath, validatePath } from '../helpers/pathUtils';
4+
import { BaseMethod } from '../BaseMethod';
5+
import { validateParams } from '../helpers/paramsValidator';
6+
import { NexaGetAddressParams } from '../../types';
7+
8+
export default class NexaGetAddress extends BaseMethod<HardwareNexaGetAddress[]> {
9+
hasBundle = false;
10+
11+
init() {
12+
this.checkDeviceId = true;
13+
this.notAllowDeviceMode = [...this.notAllowDeviceMode, UI_REQUEST.INITIALIZE];
14+
15+
this.hasBundle = !!this.payload?.bundle;
16+
const payload = this.hasBundle ? this.payload : { bundle: [this.payload] };
17+
18+
// check payload
19+
validateParams(payload, [{ name: 'bundle', type: 'array' }]);
20+
21+
// init params
22+
this.params = [];
23+
payload.bundle.forEach((batch: NexaGetAddressParams) => {
24+
const addressN = validatePath(batch.path, 3);
25+
26+
validateParams(batch, [
27+
{ name: 'path', required: true },
28+
{ name: 'showOnOneKey', type: 'boolean' },
29+
{ name: 'prefix', type: 'string' },
30+
{ name: 'scheme', type: 'string' },
31+
]);
32+
33+
const showOnOneKey = batch.showOnOneKey ?? true;
34+
35+
this.params.push({
36+
address_n: addressN,
37+
show_display: showOnOneKey,
38+
prefix: batch.prefix,
39+
scheme: batch.scheme,
40+
});
41+
});
42+
}
43+
44+
getVersionRange() {
45+
return {
46+
model_mini: {
47+
min: '3.2.0',
48+
},
49+
model_touch: {
50+
min: '4.4.0',
51+
},
52+
};
53+
}
54+
55+
async run() {
56+
const responses: {
57+
path: string;
58+
pub: string;
59+
address: string;
60+
}[] = [];
61+
62+
for (let i = 0; i < this.params.length; i++) {
63+
const param = this.params[i];
64+
65+
const res = await this.device.commands.typedCall('NexaGetAddress', 'NexaAddress', {
66+
...param,
67+
});
68+
69+
const { address } = res.message;
70+
71+
const result = {
72+
path: serializedPath(param.address_n),
73+
pub: res.message.public_key,
74+
address,
75+
};
76+
responses.push(result);
77+
this.postPreviousAddressMessage(result);
78+
}
79+
80+
return Promise.resolve(this.hasBundle ? responses : responses[0]);
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { TypedCall } from '@onekeyfe/hd-transport';
2+
import { TypedResponseMessage } from '../../device/DeviceCommands';
3+
import { validatePath } from '../helpers/pathUtils';
4+
import { BaseMethod } from '../BaseMethod';
5+
import { validateParams } from '../helpers/paramsValidator';
6+
import { NexaSignTransactionParams, NexaSignature } from '../../types';
7+
8+
export default class NexaSignTransaction extends BaseMethod<NexaSignTransactionParams> {
9+
hasBundle = false;
10+
11+
init() {
12+
const payload = this.payload as NexaSignTransactionParams;
13+
14+
payload.inputs.forEach(input => {
15+
validateParams(input, [
16+
{ name: 'path', type: 'string', required: true },
17+
{ name: 'message', type: 'string', required: true },
18+
{ name: 'prefix', type: 'string', required: true },
19+
]);
20+
return input;
21+
});
22+
this.params = payload;
23+
}
24+
25+
getVersionRange() {
26+
return {
27+
model_mini: {
28+
min: '3.2.0',
29+
},
30+
model_touch: {
31+
min: '4.4.0',
32+
},
33+
};
34+
}
35+
36+
async processTxRequest(
37+
typedCall: TypedCall,
38+
res: TypedResponseMessage<'NexaTxInputRequest'> | TypedResponseMessage<'NexaSignedTx'>,
39+
index: number,
40+
signature: NexaSignature[]
41+
): Promise<NexaSignature[]> {
42+
if (res.type === 'NexaSignedTx') {
43+
signature.push({
44+
index,
45+
signature: res.message.signature,
46+
});
47+
48+
return signature;
49+
}
50+
51+
if (res.type === 'NexaTxInputRequest') {
52+
signature.push({
53+
index,
54+
signature: res.message.signature ?? '',
55+
});
56+
57+
const nextIndex = res.message.request_index;
58+
const input = this.params.inputs[nextIndex];
59+
const response = await typedCall(
60+
'NexaTxInputAck',
61+
// @ts-expect-error
62+
['NexaTxInputRequest', 'NexaSignedTx'],
63+
{
64+
address_n: input.path,
65+
raw_message: input.message,
66+
}
67+
);
68+
69+
return this.processTxRequest(typedCall, response, nextIndex, signature);
70+
}
71+
72+
return signature;
73+
}
74+
75+
async run() {
76+
const { device, params } = this;
77+
const input = params.inputs[0];
78+
79+
const response = await device.commands.typedCall(
80+
'NexaSignTx',
81+
['NexaTxInputRequest', 'NexaSignedTx'],
82+
{
83+
address_n: validatePath(input.path, 3),
84+
raw_message: input.message,
85+
prefix: input.prefix,
86+
input_count: params.inputs.length,
87+
}
88+
);
89+
return this.processTxRequest(
90+
device.commands.typedCall.bind(device.commands),
91+
response as any,
92+
0,
93+
[]
94+
);
95+
}
96+
}

packages/core/src/data/messages/messages.json

+113
Original file line numberDiff line numberDiff line change
@@ -7369,6 +7369,113 @@
73697369
}
73707370
}
73717371
},
7372+
"NexaGetAddress": {
7373+
"fields": {
7374+
"address_n": {
7375+
"rule": "repeated",
7376+
"type": "uint32",
7377+
"id": 1,
7378+
"options": {
7379+
"packed": false
7380+
}
7381+
},
7382+
"show_display": {
7383+
"type": "bool",
7384+
"id": 2
7385+
},
7386+
"prefix": {
7387+
"type": "string",
7388+
"id": 3,
7389+
"options": {
7390+
"default": "nexa"
7391+
}
7392+
}
7393+
}
7394+
},
7395+
"NexaAddress": {
7396+
"fields": {
7397+
"address": {
7398+
"rule": "required",
7399+
"type": "string",
7400+
"id": 1
7401+
},
7402+
"public_key": {
7403+
"rule": "required",
7404+
"type": "bytes",
7405+
"id": 2
7406+
}
7407+
}
7408+
},
7409+
"NexaSignTx": {
7410+
"fields": {
7411+
"address_n": {
7412+
"rule": "repeated",
7413+
"type": "uint32",
7414+
"id": 1,
7415+
"options": {
7416+
"packed": false
7417+
}
7418+
},
7419+
"raw_message": {
7420+
"rule": "required",
7421+
"type": "bytes",
7422+
"id": 2
7423+
},
7424+
"prefix": {
7425+
"type": "string",
7426+
"id": 3,
7427+
"options": {
7428+
"default": "nexa"
7429+
}
7430+
},
7431+
"input_count": {
7432+
"type": "uint32",
7433+
"id": 4,
7434+
"options": {
7435+
"default": 1
7436+
}
7437+
}
7438+
}
7439+
},
7440+
"NexaTxInputRequest": {
7441+
"fields": {
7442+
"request_index": {
7443+
"rule": "required",
7444+
"type": "uint32",
7445+
"id": 1
7446+
},
7447+
"signature": {
7448+
"type": "bytes",
7449+
"id": 2
7450+
}
7451+
}
7452+
},
7453+
"NexaTxInputAck": {
7454+
"fields": {
7455+
"address_n": {
7456+
"rule": "repeated",
7457+
"type": "uint32",
7458+
"id": 1,
7459+
"options": {
7460+
"packed": false
7461+
}
7462+
},
7463+
"raw_message": {
7464+
"rule": "required",
7465+
"type": "bytes",
7466+
"id": 2
7467+
}
7468+
}
7469+
},
7470+
"NexaSignedTx": {
7471+
"fields": {
7472+
"signature": {
7473+
"rule": "required",
7474+
"type": "bytes",
7475+
"id": 1
7476+
}
7477+
}
7478+
},
73727479
"PolkadotGetAddress": {
73737480
"fields": {
73747481
"address_n": {
@@ -9446,6 +9553,12 @@
94469553
"MessageType_KaspaSignedTx": 11303,
94479554
"MessageType_KaspaTxInputRequest": 11304,
94489555
"MessageType_KaspaTxInputAck": 11305,
9556+
"MessageType_NexaGetAddress": 11400,
9557+
"MessageType_NexaAddress": 11401,
9558+
"MessageType_NexaSignTx": 11402,
9559+
"MessageType_NexaSignedTx": 11403,
9560+
"MessageType_NexaTxInputRequest": 11404,
9561+
"MessageType_NexaTxInputAck": 11405,
94499562
"MessageType_DeviceBackToBoot": 903,
94509563
"MessageType_DeviceInfoSettings": 10001,
94519564
"MessageType_GetDeviceInfo": 10002,

packages/core/src/inject.ts

+4
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,8 @@ export const createCoreApi = (
261261
call({ ...params, connectId, deviceId, method: 'kaspaGetAddress' }),
262262
kaspaSignTransaction: (connectId, deviceId, params) =>
263263
call({ ...params, connectId, deviceId, method: 'kaspaSignTransaction' }),
264+
nexaGetAddress: (connectId, deviceId, params) =>
265+
call({ ...params, connectId, deviceId, method: 'nexaGetAddress' }),
266+
nexaSignTransaction: (connectId, deviceId, params) =>
267+
call({ ...params, connectId, deviceId, method: 'nexaSignTransaction' }),
264268
});

packages/core/src/types/api/export.ts

+8
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,11 @@ export type {
137137
KaspaSignInputParams,
138138
KaspaSignOutputParams,
139139
} from './kaspaSignTransaction';
140+
141+
export type { NexaAddress, NexaGetAddressParams } from './nexaGetAddress';
142+
export type {
143+
NexaSignature,
144+
NexaSignTransactionParams,
145+
NexaSignInputParams,
146+
NexaSignOutputParams,
147+
} from './nexaSignTransaction';

packages/core/src/types/api/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ import { polkadotSignTransaction } from './polkadotSignTransaction';
108108
import { kaspaGetAddress } from './kaspaGetAddress';
109109
import { kaspaSignTransaction } from './kaspaSignTransaction';
110110

111+
import { nexaGetAddress } from './nexaGetAddress';
112+
import { nexaSignTransaction } from './nexaSignTransaction';
113+
111114
export * from './export';
112115

113116
export type CoreApi = {
@@ -290,4 +293,10 @@ export type CoreApi = {
290293
*/
291294
kaspaGetAddress: typeof kaspaGetAddress;
292295
kaspaSignTransaction: typeof kaspaSignTransaction;
296+
297+
/**
298+
* nexa function
299+
*/
300+
nexaGetAddress: typeof nexaGetAddress;
301+
nexaSignTransaction: typeof nexaSignTransaction;
293302
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { NexaAddress as HardwareNexaAddress } from '@onekeyfe/hd-transport';
2+
import type { CommonParams, Response } from '../params';
3+
4+
export type NexaAddress = {
5+
path: string;
6+
} & HardwareNexaAddress;
7+
8+
export type NexaGetAddressParams = {
9+
path: string | number[];
10+
prefix?: string;
11+
scheme?: string;
12+
showOnOneKey?: boolean;
13+
};
14+
15+
export declare function nexaGetAddress(
16+
connectId: string,
17+
deviceId: string,
18+
params: CommonParams & NexaGetAddressParams
19+
): Response<NexaAddress>;
20+
21+
export declare function nexaGetAddress(
22+
connectId: string,
23+
deviceId: string,
24+
params: CommonParams & { bundle?: NexaGetAddressParams[] }
25+
): Response<Array<NexaAddress>>;

0 commit comments

Comments
 (0)