@shapeshiftoss/hdwallet-keepkey 1.62.41 → 1.62.42
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/LICENSE.md +6 -6
- package/dist/cjs/.tsbuildinfo +1 -0
- package/dist/{adapter.d.ts → cjs/adapter.d.ts} +6 -7
- package/dist/cjs/adapter.js +146 -0
- package/dist/{binance.d.ts → cjs/binance.d.ts} +3 -4
- package/dist/cjs/binance.js +144 -0
- package/dist/{bitcoin.d.ts → cjs/bitcoin.d.ts} +3 -4
- package/dist/cjs/bitcoin.js +549 -0
- package/dist/{bnbencoding.d.ts → cjs/bnbencoding.d.ts} +3 -2
- package/dist/{bnbencoding.js → cjs/bnbencoding.js} +22 -22
- package/dist/{cosmos.d.ts → cjs/cosmos.d.ts} +4 -5
- package/dist/cjs/cosmos.js +219 -0
- package/dist/{eos.d.ts → cjs/eos.d.ts} +3 -4
- package/dist/cjs/eos.js +244 -0
- package/dist/{ethereum.d.ts → cjs/ethereum.d.ts} +4 -5
- package/dist/cjs/ethereum.js +264 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/{index.js → cjs/index.js} +0 -1
- package/dist/{keepkey.d.ts → cjs/keepkey.d.ts} +45 -20
- package/dist/cjs/keepkey.js +959 -0
- package/dist/{mayachain.d.ts → cjs/mayachain.d.ts} +4 -5
- package/dist/cjs/mayachain.js +169 -0
- package/dist/{osmosis.d.ts → cjs/osmosis.d.ts} +4 -5
- package/dist/cjs/osmosis.js +270 -0
- package/dist/cjs/package.json +1 -0
- package/dist/{ripple.d.ts → cjs/ripple.d.ts} +3 -4
- package/dist/cjs/ripple.js +104 -0
- package/dist/{thorchain.d.ts → cjs/thorchain.d.ts} +4 -5
- package/dist/cjs/thorchain.js +169 -0
- package/dist/{transport.d.ts → cjs/transport.d.ts} +2 -3
- package/dist/cjs/transport.js +333 -0
- package/dist/{typeRegistry.d.ts → cjs/typeRegistry.d.ts} +2 -3
- package/dist/{typeRegistry.js → cjs/typeRegistry.js} +26 -13
- package/dist/{utils.d.ts → cjs/utils.d.ts} +1 -2
- package/dist/{utils.js → cjs/utils.js} +7 -8
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/adapter.d.ts +35 -0
- package/dist/esm/adapter.js +119 -0
- package/dist/esm/binance.d.ts +5 -0
- package/dist/esm/binance.js +112 -0
- package/dist/esm/bitcoin.d.ts +11 -0
- package/dist/esm/bitcoin.js +511 -0
- package/dist/esm/bnbencoding.d.ts +11 -0
- package/dist/esm/bnbencoding.js +103 -0
- package/dist/esm/cosmos.d.ts +6 -0
- package/dist/esm/cosmos.js +187 -0
- package/dist/esm/eos.d.ts +5 -0
- package/dist/esm/eos.js +212 -0
- package/dist/esm/ethereum.d.ts +17 -0
- package/dist/esm/ethereum.js +229 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/keepkey.d.ts +177 -0
- package/dist/esm/keepkey.js +925 -0
- package/dist/esm/mayachain.d.ts +6 -0
- package/dist/esm/mayachain.js +137 -0
- package/dist/esm/osmosis.d.ts +6 -0
- package/dist/esm/osmosis.js +238 -0
- package/dist/esm/ripple.d.ts +5 -0
- package/dist/esm/ripple.js +72 -0
- package/dist/esm/thorchain.d.ts +6 -0
- package/dist/esm/thorchain.js +137 -0
- package/dist/esm/transport.d.ts +55 -0
- package/dist/esm/transport.js +306 -0
- package/dist/esm/typeRegistry.d.ts +4 -0
- package/dist/esm/typeRegistry.js +47 -0
- package/dist/esm/utils.d.ts +6 -0
- package/dist/esm/utils.js +56 -0
- package/package.json +33 -19
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js +0 -175
- package/dist/adapter.js.map +0 -1
- package/dist/binance.d.ts.map +0 -1
- package/dist/binance.js +0 -146
- package/dist/binance.js.map +0 -1
- package/dist/bitcoin.d.ts.map +0 -1
- package/dist/bitcoin.js +0 -568
- package/dist/bitcoin.js.map +0 -1
- package/dist/bnbencoding.d.ts.map +0 -1
- package/dist/bnbencoding.js.map +0 -1
- package/dist/cosmos.d.ts.map +0 -1
- package/dist/cosmos.js +0 -230
- package/dist/cosmos.js.map +0 -1
- package/dist/eos.d.ts.map +0 -1
- package/dist/eos.js +0 -247
- package/dist/eos.js.map +0 -1
- package/dist/ethereum.d.ts.map +0 -1
- package/dist/ethereum.js +0 -281
- package/dist/ethereum.js.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/keepkey.d.ts.map +0 -1
- package/dist/keepkey.js +0 -1027
- package/dist/keepkey.js.map +0 -1
- package/dist/mayachain.d.ts.map +0 -1
- package/dist/mayachain.js +0 -180
- package/dist/mayachain.js.map +0 -1
- package/dist/osmosis.d.ts.map +0 -1
- package/dist/osmosis.js +0 -278
- package/dist/osmosis.js.map +0 -1
- package/dist/ripple.d.ts.map +0 -1
- package/dist/ripple.js +0 -111
- package/dist/ripple.js.map +0 -1
- package/dist/thorchain.d.ts.map +0 -1
- package/dist/thorchain.js +0 -180
- package/dist/thorchain.js.map +0 -1
- package/dist/transport.d.ts.map +0 -1
- package/dist/transport.js +0 -365
- package/dist/transport.js.map +0 -1
- package/dist/typeRegistry.d.ts.map +0 -1
- package/dist/typeRegistry.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as Messages from '@keepkey/device-protocol/lib/messages_pb';
|
|
2
|
+
import * as CosmosMessages from '@keepkey/device-protocol/lib/messages-cosmos_pb';
|
|
3
|
+
import * as core from '@shapeshiftoss/hdwallet-core';
|
|
4
|
+
import bs58check from 'bs58check';
|
|
5
|
+
import PLazy from 'p-lazy';
|
|
6
|
+
const protoTxBuilder = PLazy.from(() => import('@shapeshiftoss/proto-tx-builder'));
|
|
7
|
+
export function cosmosGetAccountPaths(msg) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
addressNList: [
|
|
11
|
+
0x80000000 + 44,
|
|
12
|
+
0x80000000 + core.slip44ByCoin('Atom'),
|
|
13
|
+
0x80000000 + msg.accountIdx,
|
|
14
|
+
0,
|
|
15
|
+
0,
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
export async function cosmosGetAddress(transport, msg) {
|
|
21
|
+
const getAddr = new CosmosMessages.CosmosGetAddress();
|
|
22
|
+
getAddr.setAddressNList(msg.addressNList);
|
|
23
|
+
getAddr.setShowDisplay(msg.showDisplay !== false);
|
|
24
|
+
const response = await transport.call(Messages.MessageType.MESSAGETYPE_COSMOSGETADDRESS, getAddr, {
|
|
25
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
26
|
+
});
|
|
27
|
+
const cosmosAddress = response.proto;
|
|
28
|
+
return core.mustBeDefined(cosmosAddress.getAddress());
|
|
29
|
+
}
|
|
30
|
+
export async function cosmosSignTx(transport, msg) {
|
|
31
|
+
const address = await cosmosGetAddress(transport, { addressNList: msg.addressNList });
|
|
32
|
+
const getPublicKeyMsg = new Messages.GetPublicKey();
|
|
33
|
+
getPublicKeyMsg.setAddressNList(msg.addressNList);
|
|
34
|
+
getPublicKeyMsg.setEcdsaCurveName('secp256k1');
|
|
35
|
+
const pubkeyMsg = (await transport.call(Messages.MessageType.MESSAGETYPE_GETPUBLICKEY, getPublicKeyMsg, {
|
|
36
|
+
msgTimeout: core.DEFAULT_TIMEOUT,
|
|
37
|
+
})).proto;
|
|
38
|
+
const pubkey = bs58check.decode(core.mustBeDefined(pubkeyMsg.getXpub())).slice(45);
|
|
39
|
+
return transport.lockDuring(async () => {
|
|
40
|
+
const signTx = new CosmosMessages.CosmosSignTx();
|
|
41
|
+
signTx.setAddressNList(msg.addressNList);
|
|
42
|
+
signTx.setAccountNumber(msg.account_number);
|
|
43
|
+
signTx.setChainId(msg.chain_id);
|
|
44
|
+
signTx.setFeeAmount(parseInt(msg.tx.fee.amount[0].amount));
|
|
45
|
+
signTx.setGas(parseInt(msg.tx.fee.gas));
|
|
46
|
+
signTx.setSequence(msg.sequence);
|
|
47
|
+
if (msg.tx.memo !== undefined) {
|
|
48
|
+
signTx.setMemo(msg.tx.memo);
|
|
49
|
+
}
|
|
50
|
+
signTx.setMsgCount(1);
|
|
51
|
+
let resp = await transport.call(Messages.MessageType.MESSAGETYPE_COSMOSSIGNTX, signTx, {
|
|
52
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
53
|
+
omitLock: true,
|
|
54
|
+
});
|
|
55
|
+
for (const m of msg.tx.msg) {
|
|
56
|
+
if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_COSMOSMSGREQUEST) {
|
|
57
|
+
throw new Error(`cosmos: unexpected response ${resp.message_type}`);
|
|
58
|
+
}
|
|
59
|
+
let ack;
|
|
60
|
+
if (m.type === 'cosmos-sdk/MsgSend') {
|
|
61
|
+
if (m.value.amount.length !== 1) {
|
|
62
|
+
throw new Error('cosmos: Multiple amounts per msg not supported');
|
|
63
|
+
}
|
|
64
|
+
const denom = m.value.amount[0].denom;
|
|
65
|
+
if (denom !== 'uatom') {
|
|
66
|
+
throw new Error('cosmos: Unsupported denomination: ' + denom);
|
|
67
|
+
}
|
|
68
|
+
const send = new CosmosMessages.CosmosMsgSend();
|
|
69
|
+
send.setFromAddress(m.value.from_address);
|
|
70
|
+
send.setToAddress(m.value.to_address);
|
|
71
|
+
send.setAmount(m.value.amount[0].amount);
|
|
72
|
+
ack = new CosmosMessages.CosmosMsgAck();
|
|
73
|
+
ack.setSend(send);
|
|
74
|
+
}
|
|
75
|
+
else if (m.type === 'cosmos-sdk/MsgDelegate') {
|
|
76
|
+
const denom = m.value.amount.denom;
|
|
77
|
+
if (denom !== 'uatom') {
|
|
78
|
+
throw new Error('cosmos: Unsupported denomination: ' + denom);
|
|
79
|
+
}
|
|
80
|
+
const delegate = new CosmosMessages.CosmosMsgDelegate();
|
|
81
|
+
delegate.setDelegatorAddress(m.value.delegator_address);
|
|
82
|
+
delegate.setValidatorAddress(m.value.validator_address);
|
|
83
|
+
delegate.setAmount(m.value.amount.amount);
|
|
84
|
+
ack = new CosmosMessages.CosmosMsgAck();
|
|
85
|
+
ack.setDelegate(delegate);
|
|
86
|
+
}
|
|
87
|
+
else if (m.type === 'cosmos-sdk/MsgUndelegate') {
|
|
88
|
+
const denom = m.value.amount.denom;
|
|
89
|
+
if (denom !== 'uatom') {
|
|
90
|
+
throw new Error('cosmos: Unsupported denomination: ' + denom);
|
|
91
|
+
}
|
|
92
|
+
const undelegate = new CosmosMessages.CosmosMsgUndelegate();
|
|
93
|
+
undelegate.setDelegatorAddress(m.value.delegator_address);
|
|
94
|
+
undelegate.setValidatorAddress(m.value.validator_address);
|
|
95
|
+
undelegate.setAmount(m.value.amount.amount);
|
|
96
|
+
ack = new CosmosMessages.CosmosMsgAck();
|
|
97
|
+
ack.setUndelegate(undelegate);
|
|
98
|
+
}
|
|
99
|
+
else if (m.type === 'cosmos-sdk/MsgBeginRedelegate') {
|
|
100
|
+
const denom = m.value.amount.denom;
|
|
101
|
+
if (denom !== 'uatom') {
|
|
102
|
+
throw new Error('cosmos: Unsupported denomination: ' + denom);
|
|
103
|
+
}
|
|
104
|
+
const redelegate = new CosmosMessages.CosmosMsgRedelegate();
|
|
105
|
+
redelegate.setDelegatorAddress(m.value.delegator_address);
|
|
106
|
+
redelegate.setValidatorSrcAddress(m.value.validator_src_address);
|
|
107
|
+
redelegate.setValidatorDstAddress(m.value.validator_dst_address);
|
|
108
|
+
redelegate.setAmount(m.value.amount.amount);
|
|
109
|
+
ack = new CosmosMessages.CosmosMsgAck();
|
|
110
|
+
ack.setRedelegate(redelegate);
|
|
111
|
+
}
|
|
112
|
+
else if (m.type === 'cosmos-sdk/MsgWithdrawDelegationReward') {
|
|
113
|
+
const rewards = new CosmosMessages.CosmosMsgRewards();
|
|
114
|
+
rewards.setDelegatorAddress(m.value.delegator_address);
|
|
115
|
+
rewards.setValidatorAddress(m.value.validator_address);
|
|
116
|
+
if (m.value.amount) {
|
|
117
|
+
const denom = m.value.amount.denom;
|
|
118
|
+
if (denom !== 'uatom') {
|
|
119
|
+
throw new Error('cosmos: Unsupported denomination: ' + denom);
|
|
120
|
+
}
|
|
121
|
+
rewards.setAmount(m.value.amount.amount);
|
|
122
|
+
}
|
|
123
|
+
ack = new CosmosMessages.CosmosMsgAck();
|
|
124
|
+
ack.setRewards(rewards);
|
|
125
|
+
}
|
|
126
|
+
else if (m.type === 'cosmos-sdk/MsgTransfer') {
|
|
127
|
+
const denom = m.value.token.denom;
|
|
128
|
+
if (denom !== 'uatom') {
|
|
129
|
+
throw new Error('cosmos: Unsupported denomination: ' + denom);
|
|
130
|
+
}
|
|
131
|
+
const ibcTransfer = new CosmosMessages.CosmosMsgIBCTransfer();
|
|
132
|
+
ibcTransfer.setReceiver(m.value.receiver);
|
|
133
|
+
ibcTransfer.setSender(m.value.sender);
|
|
134
|
+
ibcTransfer.setSourceChannel(m.value.source_channel);
|
|
135
|
+
ibcTransfer.setSourcePort(m.value.source_port);
|
|
136
|
+
ibcTransfer.setRevisionHeight(m.value.timeout_height.revision_height);
|
|
137
|
+
ibcTransfer.setRevisionNumber(m.value.timeout_height.revision_number);
|
|
138
|
+
ibcTransfer.setAmount(m.value.token.amount);
|
|
139
|
+
ibcTransfer.setDenom(m.value.token.denom);
|
|
140
|
+
ack = new CosmosMessages.CosmosMsgAck();
|
|
141
|
+
ack.setIbcTransfer(ibcTransfer);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw new Error(`cosmos: Message ${m.type} is not yet supported`);
|
|
145
|
+
}
|
|
146
|
+
resp = await transport.call(Messages.MessageType.MESSAGETYPE_COSMOSMSGACK, ack, {
|
|
147
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
148
|
+
omitLock: true,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_COSMOSSIGNEDTX) {
|
|
152
|
+
throw new Error(`cosmos: unexpected response ${resp.message_type}`);
|
|
153
|
+
}
|
|
154
|
+
const signedTx = resp.proto;
|
|
155
|
+
const offlineSigner = {
|
|
156
|
+
async getAccounts() {
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
address,
|
|
160
|
+
algo: 'secp256k1',
|
|
161
|
+
pubkey,
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
},
|
|
165
|
+
async signAmino(signerAddress, signDoc) {
|
|
166
|
+
if (signerAddress !== address)
|
|
167
|
+
throw new Error('expected signerAddress to match address');
|
|
168
|
+
return {
|
|
169
|
+
signed: signDoc,
|
|
170
|
+
signature: {
|
|
171
|
+
pub_key: {
|
|
172
|
+
type: 'tendermint/PubKeySecp256k1',
|
|
173
|
+
value: signedTx.getPublicKey_asB64(),
|
|
174
|
+
},
|
|
175
|
+
signature: signedTx.getSignature_asB64(),
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
const signerData = {
|
|
181
|
+
sequence: Number(msg.sequence),
|
|
182
|
+
accountNumber: Number(msg.account_number),
|
|
183
|
+
chainId: msg.chain_id,
|
|
184
|
+
};
|
|
185
|
+
return (await protoTxBuilder).sign(address, msg.tx, offlineSigner, signerData, 'cosmos');
|
|
186
|
+
});
|
|
187
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as core from '@shapeshiftoss/hdwallet-core';
|
|
2
|
+
import type { Transport } from './transport.js';
|
|
3
|
+
export declare function eosGetAccountPaths(msg: core.EosGetAccountPaths): core.EosAccountPath[];
|
|
4
|
+
export declare function eosGetPublicKey(transport: Transport, msg: core.EosGetPublicKey): Promise<string>;
|
|
5
|
+
export declare function eosSignTx(transport: Transport, msg: core.EosToSignTx): Promise<core.EosTxSigned>;
|
package/dist/esm/eos.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as Messages from '@keepkey/device-protocol/lib/messages_pb';
|
|
2
|
+
import * as EosMessages from '@keepkey/device-protocol/lib/messages-eos_pb';
|
|
3
|
+
import * as core from '@shapeshiftoss/hdwallet-core';
|
|
4
|
+
import bs58 from 'bs58';
|
|
5
|
+
import createHash from 'create-hash';
|
|
6
|
+
import Long from 'long';
|
|
7
|
+
function eosSigFormatter(r, s, _v) {
|
|
8
|
+
const recoverId = 0x1f;
|
|
9
|
+
let signature = 'SIG_K1_';
|
|
10
|
+
console.debug('formatter logs');
|
|
11
|
+
const keyBuffer = Buffer.alloc(65);
|
|
12
|
+
const rbuf = Buffer.from(r);
|
|
13
|
+
const sbuf = Buffer.from(s);
|
|
14
|
+
keyBuffer.writeUInt8(recoverId, 0);
|
|
15
|
+
rbuf.copy(keyBuffer, 1);
|
|
16
|
+
sbuf.copy(keyBuffer, 33);
|
|
17
|
+
console.debug(keyBuffer);
|
|
18
|
+
const check = [keyBuffer];
|
|
19
|
+
const keyType = 'K1'; // we only sign using K1 curve
|
|
20
|
+
check.push(Buffer.from(keyType));
|
|
21
|
+
console.debug(check);
|
|
22
|
+
console.debug('hash');
|
|
23
|
+
console.debug(createHash('ripemd160').update(core.compatibleBufferConcat(check)).digest());
|
|
24
|
+
const chksum = createHash('ripemd160')
|
|
25
|
+
.update(core.compatibleBufferConcat(check))
|
|
26
|
+
.digest()
|
|
27
|
+
.slice(0, 4);
|
|
28
|
+
console.debug(chksum);
|
|
29
|
+
signature = signature.concat(bs58.encode(core.compatibleBufferConcat([keyBuffer, chksum])));
|
|
30
|
+
console.debug(signature);
|
|
31
|
+
return signature;
|
|
32
|
+
}
|
|
33
|
+
function charToSymbol(c) {
|
|
34
|
+
if (c.charCodeAt(0) >= 'a'.charCodeAt(0) && c.charCodeAt(0) <= 'z'.charCodeAt(0)) {
|
|
35
|
+
return c.charCodeAt(0) - 'a'.charCodeAt(0) + 6;
|
|
36
|
+
}
|
|
37
|
+
if (c.charCodeAt(0) >= '1'.charCodeAt(0) && c.charCodeAt(0) <= '5'.charCodeAt(0)) {
|
|
38
|
+
return c.charCodeAt(0) - '1'.charCodeAt(0) + 1;
|
|
39
|
+
}
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
function nameToNumber(name) {
|
|
43
|
+
let value = new Long(0, 0, true);
|
|
44
|
+
let c = new Long(0, 0, true);
|
|
45
|
+
for (let i = 0; i < 13; i++) {
|
|
46
|
+
if (i < name.length && i < 13) {
|
|
47
|
+
c = Long.fromNumber(charToSymbol(name[i]), true);
|
|
48
|
+
}
|
|
49
|
+
if (i < 12) {
|
|
50
|
+
c = c.and(Long.fromString('1f', true, 16));
|
|
51
|
+
c = c.shl(64 - 5 * (i + 1));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// last iteration
|
|
55
|
+
c = c.and(Long.fromString('0f', true, 16));
|
|
56
|
+
}
|
|
57
|
+
value = value.or(c);
|
|
58
|
+
}
|
|
59
|
+
return value.toString();
|
|
60
|
+
}
|
|
61
|
+
function symbolFromString(p, name) {
|
|
62
|
+
let result = 0;
|
|
63
|
+
for (let i = 0; i < name.length; i++) {
|
|
64
|
+
result |= name.charCodeAt(i) << (8 * (i + 1));
|
|
65
|
+
}
|
|
66
|
+
result |= p;
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
function assetToNumber(asset) {
|
|
70
|
+
const assetSplit = asset.split(' '); // amount, symbol
|
|
71
|
+
const amountStr = assetSplit[0] ?? '';
|
|
72
|
+
const symbolStr = assetSplit[1] ?? '';
|
|
73
|
+
const dot_pos = amountStr.indexOf('.');
|
|
74
|
+
let fract_part = 0;
|
|
75
|
+
let int_part = 0;
|
|
76
|
+
let precision_digit = 0;
|
|
77
|
+
// parse symbol
|
|
78
|
+
if (dot_pos != -1) {
|
|
79
|
+
precision_digit = amountStr.length - dot_pos - 1;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
precision_digit = 0;
|
|
83
|
+
}
|
|
84
|
+
const sym = symbolFromString(precision_digit, symbolStr);
|
|
85
|
+
//parse amount
|
|
86
|
+
if (dot_pos != -1) {
|
|
87
|
+
int_part = parseInt(amountStr.slice(0, dot_pos));
|
|
88
|
+
fract_part = parseInt(amountStr.slice(dot_pos + 1));
|
|
89
|
+
if (int_part < 0) {
|
|
90
|
+
fract_part *= -1;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
int_part = parseInt(amountStr);
|
|
95
|
+
}
|
|
96
|
+
let amount = int_part;
|
|
97
|
+
amount *= Math.pow(10, sym & 0xff);
|
|
98
|
+
amount += fract_part;
|
|
99
|
+
return [amount, sym];
|
|
100
|
+
}
|
|
101
|
+
export function eosGetAccountPaths(msg) {
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
addressNList: [
|
|
105
|
+
0x80000000 + 44,
|
|
106
|
+
0x80000000 + core.slip44ByCoin('Eos'),
|
|
107
|
+
0x80000000 + msg.accountIdx,
|
|
108
|
+
0,
|
|
109
|
+
0,
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
export async function eosGetPublicKey(transport, msg) {
|
|
115
|
+
const getPubkey = new EosMessages.EosGetPublicKey();
|
|
116
|
+
getPubkey.setAddressNList(msg.addressNList);
|
|
117
|
+
getPubkey.setShowDisplay(msg.showDisplay !== false);
|
|
118
|
+
getPubkey.setKind(msg.kind);
|
|
119
|
+
const response = await transport.call(Messages.MessageType.MESSAGETYPE_EOSGETPUBLICKEY, getPubkey, {
|
|
120
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
121
|
+
});
|
|
122
|
+
const eosPubkey = response.proto;
|
|
123
|
+
return core.mustBeDefined(eosPubkey.getWifPublicKey());
|
|
124
|
+
}
|
|
125
|
+
export async function eosSignTx(transport, msg) {
|
|
126
|
+
return transport.lockDuring(async () => {
|
|
127
|
+
let resp;
|
|
128
|
+
// check some params
|
|
129
|
+
if (msg.tx.actions.length > 1) {
|
|
130
|
+
throw new Error(`Too many actions in Eos transaction: Keepkey only supports one!`);
|
|
131
|
+
}
|
|
132
|
+
const signTx = new EosMessages.EosSignTx();
|
|
133
|
+
signTx.setAddressNList(msg.addressNList);
|
|
134
|
+
signTx.setChainId(core.fromHexString(msg.chain_id));
|
|
135
|
+
const txHeader = new EosMessages.EosTxHeader();
|
|
136
|
+
txHeader.setExpiration((Date.parse(msg.tx.expiration) -
|
|
137
|
+
Date.parse('01 Jan 1970 00:00:00Z') -
|
|
138
|
+
new Date().getTimezoneOffset() * 60 * 1000) /
|
|
139
|
+
1000);
|
|
140
|
+
txHeader.setRefBlockNum(msg.tx.ref_block_num);
|
|
141
|
+
txHeader.setRefBlockPrefix(msg.tx.ref_block_prefix);
|
|
142
|
+
txHeader.setMaxNetUsageWords(msg.tx.max_net_usage_words);
|
|
143
|
+
txHeader.setMaxCpuUsageMs(msg.tx.max_cpu_usage_ms);
|
|
144
|
+
txHeader.setDelaySec(msg.tx.delay_sec);
|
|
145
|
+
signTx.setHeader(txHeader);
|
|
146
|
+
signTx.setNumActions(msg.tx.actions.length);
|
|
147
|
+
console.debug('tx header');
|
|
148
|
+
console.debug(txHeader);
|
|
149
|
+
resp = await transport.call(Messages.MessageType.MESSAGETYPE_EOSSIGNTX, signTx, {
|
|
150
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
151
|
+
omitLock: true,
|
|
152
|
+
});
|
|
153
|
+
if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_EOSTXACTIONREQUEST) {
|
|
154
|
+
throw new Error(`eos: unexpected response ${resp.message_type}`);
|
|
155
|
+
}
|
|
156
|
+
// parse the common block of the action
|
|
157
|
+
const actCommon = new EosMessages.EosActionCommon();
|
|
158
|
+
actCommon.setAccount(nameToNumber(msg.tx.actions[0].account).toString());
|
|
159
|
+
actCommon.setName(nameToNumber(msg.tx.actions[0].name).toString());
|
|
160
|
+
// interate through authorizations and add them
|
|
161
|
+
for (let n = 0; n < msg.tx.actions[0].authorization.length; n++) {
|
|
162
|
+
const actPerm = new EosMessages.EosPermissionLevel();
|
|
163
|
+
actPerm.setActor(nameToNumber(msg.tx.actions[0].authorization[n].actor).toString());
|
|
164
|
+
actPerm.setPermission(nameToNumber(msg.tx.actions[0].authorization[n].permission).toString());
|
|
165
|
+
actCommon.addAuthorization(actPerm);
|
|
166
|
+
}
|
|
167
|
+
const actAck = new EosMessages.EosTxActionAck();
|
|
168
|
+
actAck.setCommon(actCommon);
|
|
169
|
+
// parse the various action types here.
|
|
170
|
+
switch (msg.tx.actions[0].name) {
|
|
171
|
+
case 'transfer': {
|
|
172
|
+
// build the transfer action
|
|
173
|
+
const actType = new EosMessages.EosActionTransfer();
|
|
174
|
+
actType.setSender(nameToNumber(msg.tx.actions[0].data.from).toString());
|
|
175
|
+
actType.setReceiver(nameToNumber(msg.tx.actions[0].data.to).toString());
|
|
176
|
+
const actAsset = new EosMessages.EosAsset();
|
|
177
|
+
const assetParse = assetToNumber(msg.tx.actions[0].data.quantity);
|
|
178
|
+
actAsset.setAmount(assetParse[0].toString());
|
|
179
|
+
actAsset.setSymbol(assetParse[1].toString());
|
|
180
|
+
actType.setQuantity(actAsset);
|
|
181
|
+
actType.setMemo(msg.tx.actions[0].data.memo);
|
|
182
|
+
actAck.setTransfer(actType);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
console.debug('action data');
|
|
187
|
+
console.debug(actAck);
|
|
188
|
+
resp = await transport.call(Messages.MessageType.MESSAGETYPE_EOSTXACTIONACK, actAck, {
|
|
189
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
190
|
+
omitLock: true,
|
|
191
|
+
});
|
|
192
|
+
if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_EOSSIGNEDTX) {
|
|
193
|
+
throw new Error(`eos: unexpected response ${resp.message_type}`);
|
|
194
|
+
}
|
|
195
|
+
const signedTx = resp.proto;
|
|
196
|
+
// format signature for use in the eos system
|
|
197
|
+
// const EosFormatSig = eosSigFormatter(signedTx.getSignatureR() as Uint8Array, signedTx.getSignatureS() as Uint8Array, signedTx.getSignatureV())
|
|
198
|
+
const signatureR = signedTx.getSignatureR_asU8();
|
|
199
|
+
const signatureS = signedTx.getSignatureS_asU8();
|
|
200
|
+
const signatureV = signedTx.getSignatureV();
|
|
201
|
+
if (signatureV === undefined)
|
|
202
|
+
throw new Error('missing signatureV');
|
|
203
|
+
const sig = {
|
|
204
|
+
signatureV,
|
|
205
|
+
signatureR,
|
|
206
|
+
signatureS,
|
|
207
|
+
hash: signedTx.getHash(),
|
|
208
|
+
eosFormSig: eosSigFormatter(signatureR, signatureS, signatureV),
|
|
209
|
+
};
|
|
210
|
+
return sig;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as core from '@shapeshiftoss/hdwallet-core';
|
|
2
|
+
import type { Transport } from './transport.js';
|
|
3
|
+
export declare function ethSupportsNetwork(_chainId: number): Promise<boolean>;
|
|
4
|
+
export declare function ethSupportsSecureTransfer(): Promise<boolean>;
|
|
5
|
+
export declare function ethSupportsNativeShapeShift(): boolean;
|
|
6
|
+
export declare function ethGetAccountPaths(msg: core.ETHGetAccountPath): core.ETHAccountPath[];
|
|
7
|
+
export declare function ethSignTx(transport: Transport, msg: core.ETHSignTx): Promise<core.ETHSignedTx>;
|
|
8
|
+
export declare function ethGetAddress(transport: Transport, msg: core.ETHGetAddress): Promise<core.Address>;
|
|
9
|
+
export declare function ethSignMessage(transport: Transport, msg: core.ETHSignMessage): Promise<core.ETHSignedMessage>;
|
|
10
|
+
/**
|
|
11
|
+
* Supports EIP-712 eth_signTypedData_v4
|
|
12
|
+
* https://docs.metamask.io/wallet/how-to/sign-data/#use-eth_signtypeddata_v4
|
|
13
|
+
* Due to lack of firmware support, a hashed version of the data is
|
|
14
|
+
* displayed to the user on the device when signing
|
|
15
|
+
*/
|
|
16
|
+
export declare function ethSignTypedData(transport: Transport, msg: core.ETHSignTypedData): Promise<core.ETHSignedTypedData>;
|
|
17
|
+
export declare function ethVerifyMessage(transport: Transport, msg: core.ETHVerifyMessage): Promise<boolean>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { Common } from '@ethereumjs/common';
|
|
2
|
+
import { FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx';
|
|
3
|
+
import * as Messages from '@keepkey/device-protocol/lib/messages_pb';
|
|
4
|
+
import * as Ethereum from '@keepkey/device-protocol/lib/messages-ethereum_pb';
|
|
5
|
+
import * as Types from '@keepkey/device-protocol/lib/types_pb';
|
|
6
|
+
import { SignTypedDataVersion, TypedDataUtils } from '@metamask/eth-sig-util';
|
|
7
|
+
import * as core from '@shapeshiftoss/hdwallet-core';
|
|
8
|
+
import * as eip55 from 'eip55';
|
|
9
|
+
import { arrayify, isBytes, isHexString } from 'ethers/lib/utils.js';
|
|
10
|
+
import { toUTF8Array } from './utils.js';
|
|
11
|
+
export async function ethSupportsNetwork(_chainId) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
export async function ethSupportsSecureTransfer() {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
export function ethSupportsNativeShapeShift() {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
export function ethGetAccountPaths(msg) {
|
|
21
|
+
const slip44 = core.slip44ByCoin(msg.coin);
|
|
22
|
+
if (slip44 === undefined)
|
|
23
|
+
return [];
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0],
|
|
27
|
+
hardenedPath: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx],
|
|
28
|
+
relPath: [0, 0],
|
|
29
|
+
description: 'KeepKey',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
function stripLeadingZeroes(buf) {
|
|
34
|
+
const firstZeroIndex = buf.findIndex(x => x !== 0);
|
|
35
|
+
return buf.slice(firstZeroIndex !== -1 ? firstZeroIndex : buf.length);
|
|
36
|
+
}
|
|
37
|
+
export async function ethSignTx(transport, msg) {
|
|
38
|
+
return transport.lockDuring(async () => {
|
|
39
|
+
const est = new Ethereum.EthereumSignTx();
|
|
40
|
+
est.setAddressNList(msg.addressNList);
|
|
41
|
+
est.setNonce(stripLeadingZeroes(core.arrayify(msg.nonce)));
|
|
42
|
+
est.setGasLimit(core.arrayify(msg.gasLimit));
|
|
43
|
+
if (msg.gasPrice) {
|
|
44
|
+
est.setGasPrice(core.arrayify(msg.gasPrice));
|
|
45
|
+
}
|
|
46
|
+
if (msg.maxFeePerGas) {
|
|
47
|
+
est.setMaxFeePerGas(core.arrayify(msg.maxFeePerGas));
|
|
48
|
+
est.setType(core.ETHTransactionType.ETH_TX_TYPE_EIP_1559);
|
|
49
|
+
if (msg.maxPriorityFeePerGas) {
|
|
50
|
+
est.setMaxPriorityFeePerGas(core.arrayify(msg.maxPriorityFeePerGas));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (msg.value.match('^0x0*$') === null) {
|
|
54
|
+
est.setValue(core.arrayify(msg.value));
|
|
55
|
+
}
|
|
56
|
+
if (msg.toAddressNList) {
|
|
57
|
+
est.setAddressType(Types.OutputAddressType.SPEND);
|
|
58
|
+
est.setToAddressNList(msg.toAddressNList);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
est.setAddressType(Types.OutputAddressType.SPEND);
|
|
62
|
+
}
|
|
63
|
+
if (msg.to) {
|
|
64
|
+
est.setTo(core.arrayify(msg.to));
|
|
65
|
+
}
|
|
66
|
+
let dataChunk = null;
|
|
67
|
+
let dataRemaining = undefined;
|
|
68
|
+
if (msg.data) {
|
|
69
|
+
dataRemaining = core.arrayify(msg.data);
|
|
70
|
+
est.setDataLength(dataRemaining.length);
|
|
71
|
+
dataChunk = dataRemaining.slice(0, 1024);
|
|
72
|
+
dataRemaining = dataRemaining.slice(dataChunk.length);
|
|
73
|
+
est.setDataInitialChunk(dataChunk);
|
|
74
|
+
}
|
|
75
|
+
if (msg.chainId !== undefined) {
|
|
76
|
+
est.setChainId(msg.chainId);
|
|
77
|
+
}
|
|
78
|
+
let response;
|
|
79
|
+
let nextResponse = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNTX, est, {
|
|
80
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
81
|
+
omitLock: true,
|
|
82
|
+
});
|
|
83
|
+
response = nextResponse.proto;
|
|
84
|
+
try {
|
|
85
|
+
const esa = new Ethereum.EthereumTxAck();
|
|
86
|
+
while (response.hasDataLength()) {
|
|
87
|
+
const dataLength = response.getDataLength();
|
|
88
|
+
dataRemaining = core.mustBeDefined(dataRemaining);
|
|
89
|
+
dataChunk = dataRemaining.slice(0, dataLength);
|
|
90
|
+
dataRemaining = dataRemaining.slice(dataLength, dataRemaining.length);
|
|
91
|
+
esa.setDataChunk(dataChunk);
|
|
92
|
+
nextResponse = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMTXACK, esa, {
|
|
93
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
94
|
+
omitLock: true,
|
|
95
|
+
});
|
|
96
|
+
response = nextResponse.proto;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error({ error });
|
|
101
|
+
throw new Error('Failed to sign ETH transaction');
|
|
102
|
+
}
|
|
103
|
+
const utxBase = {
|
|
104
|
+
to: msg.to,
|
|
105
|
+
value: msg.value,
|
|
106
|
+
data: msg.data,
|
|
107
|
+
chainId: msg.chainId,
|
|
108
|
+
nonce: msg.nonce,
|
|
109
|
+
gasLimit: msg.gasLimit,
|
|
110
|
+
maxFeePerGas: msg.maxFeePerGas,
|
|
111
|
+
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
|
|
112
|
+
};
|
|
113
|
+
const r = '0x' + core.toHexString(response.getSignatureR_asU8());
|
|
114
|
+
const s = '0x' + core.toHexString(response.getSignatureS_asU8());
|
|
115
|
+
if (!response.hasSignatureV())
|
|
116
|
+
throw new Error('could not get v');
|
|
117
|
+
const v = core.mustBeDefined(response.getSignatureV());
|
|
118
|
+
const v2 = '0x' + v.toString(16);
|
|
119
|
+
const common = Common.custom({ chainId: msg.chainId });
|
|
120
|
+
const tx = msg.maxFeePerGas
|
|
121
|
+
? FeeMarketEIP1559Transaction.fromTxData({
|
|
122
|
+
...utxBase,
|
|
123
|
+
maxFeePerGas: msg.maxFeePerGas,
|
|
124
|
+
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
|
|
125
|
+
r,
|
|
126
|
+
s,
|
|
127
|
+
v: v2,
|
|
128
|
+
})
|
|
129
|
+
: Transaction.fromTxData({ ...utxBase, gasPrice: msg.gasPrice, r, s, v: v2 }, { common });
|
|
130
|
+
return {
|
|
131
|
+
r,
|
|
132
|
+
s,
|
|
133
|
+
v,
|
|
134
|
+
serialized: '0x' + core.toHexString(tx.serialize()),
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
export async function ethGetAddress(transport, msg) {
|
|
139
|
+
const getAddr = new Ethereum.EthereumGetAddress();
|
|
140
|
+
getAddr.setAddressNList(msg.addressNList);
|
|
141
|
+
getAddr.setShowDisplay(msg.showDisplay !== false);
|
|
142
|
+
const response = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMGETADDRESS, getAddr, {
|
|
143
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
144
|
+
});
|
|
145
|
+
const ethAddress = response.proto;
|
|
146
|
+
let address;
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
148
|
+
if (ethAddress.hasAddressStr())
|
|
149
|
+
address = ethAddress.getAddressStr();
|
|
150
|
+
else if (ethAddress.hasAddress())
|
|
151
|
+
address = '0x' + core.toHexString(ethAddress.getAddress_asU8());
|
|
152
|
+
else
|
|
153
|
+
throw new Error('Unable to obtain ETH address from device.');
|
|
154
|
+
return address;
|
|
155
|
+
}
|
|
156
|
+
export async function ethSignMessage(transport, msg) {
|
|
157
|
+
const { addressNList, message } = msg;
|
|
158
|
+
if (!isHexString(message))
|
|
159
|
+
throw new Error('data is not an hex string');
|
|
160
|
+
const m = new Ethereum.EthereumSignMessage();
|
|
161
|
+
m.setAddressNList(addressNList);
|
|
162
|
+
const messageBytes = arrayify(message);
|
|
163
|
+
m.setMessage(messageBytes);
|
|
164
|
+
const response = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNMESSAGE, m, {
|
|
165
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
166
|
+
});
|
|
167
|
+
const sig = response.proto;
|
|
168
|
+
return {
|
|
169
|
+
address: eip55.encode('0x' + core.toHexString(sig.getAddress_asU8())),
|
|
170
|
+
signature: '0x' + core.toHexString(sig.getSignature_asU8()),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Supports EIP-712 eth_signTypedData_v4
|
|
175
|
+
* https://docs.metamask.io/wallet/how-to/sign-data/#use-eth_signtypeddata_v4
|
|
176
|
+
* Due to lack of firmware support, a hashed version of the data is
|
|
177
|
+
* displayed to the user on the device when signing
|
|
178
|
+
*/
|
|
179
|
+
export async function ethSignTypedData(transport, msg) {
|
|
180
|
+
try {
|
|
181
|
+
const version = SignTypedDataVersion.V4;
|
|
182
|
+
const EIP_712_DOMAIN = 'EIP712Domain';
|
|
183
|
+
const { types, primaryType, domain, message } = msg.typedData;
|
|
184
|
+
const domainSeparatorHash = TypedDataUtils.hashStruct(EIP_712_DOMAIN, domain, types, version);
|
|
185
|
+
const ethereumSignTypedHash = new Ethereum.EthereumSignTypedHash();
|
|
186
|
+
ethereumSignTypedHash.setAddressNList(msg.addressNList);
|
|
187
|
+
ethereumSignTypedHash.setDomainSeparatorHash(domainSeparatorHash);
|
|
188
|
+
let messageHash = undefined;
|
|
189
|
+
// If "EIP712Domain" is the primaryType, messageHash is not required - look at T1 connect impl ;)
|
|
190
|
+
// todo: the firmware should define messageHash as an optional Uint8Array field for this case
|
|
191
|
+
if (primaryType !== EIP_712_DOMAIN) {
|
|
192
|
+
messageHash = TypedDataUtils.hashStruct(primaryType, message, types, version);
|
|
193
|
+
ethereumSignTypedHash.setMessageHash(messageHash);
|
|
194
|
+
}
|
|
195
|
+
const response = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNTYPEDHASH, ethereumSignTypedHash, {
|
|
196
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
197
|
+
});
|
|
198
|
+
const result = response.proto;
|
|
199
|
+
const res = {
|
|
200
|
+
address: result.getAddress() || '',
|
|
201
|
+
signature: '0x' + core.toHexString(result.getSignature_asU8()),
|
|
202
|
+
};
|
|
203
|
+
return res;
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error({ error });
|
|
207
|
+
throw new Error('Failed to sign typed ETH message');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export async function ethVerifyMessage(transport, msg) {
|
|
211
|
+
const m = new Ethereum.EthereumVerifyMessage();
|
|
212
|
+
m.setAddress(core.arrayify(msg.address));
|
|
213
|
+
m.setSignature(core.arrayify(msg.signature));
|
|
214
|
+
m.setMessage(isBytes(msg.message) ? arrayify(msg.message) : toUTF8Array(msg.message));
|
|
215
|
+
let event;
|
|
216
|
+
try {
|
|
217
|
+
event = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMVERIFYMESSAGE, m, {
|
|
218
|
+
msgTimeout: core.LONG_TIMEOUT,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
if (core.isIndexable(e) && e.message_enum === Messages.MessageType.MESSAGETYPE_FAILURE) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
throw e;
|
|
226
|
+
}
|
|
227
|
+
const success = event.proto;
|
|
228
|
+
return success.getMessage() === 'Message verified';
|
|
229
|
+
}
|