@tcswap/wallet-hardware 4.2.16
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/chunk-1w9rn6fj.js +5 -0
- package/dist/chunk-1w9rn6fj.js.map +10 -0
- package/dist/chunk-1z3vq8v6.js +4 -0
- package/dist/chunk-1z3vq8v6.js.map +10 -0
- package/dist/chunk-3jd7804n.js +4 -0
- package/dist/chunk-3jd7804n.js.map +10 -0
- package/dist/chunk-4pybhxzx.js +4 -0
- package/dist/chunk-4pybhxzx.js.map +10 -0
- package/dist/chunk-9kbkdt6e.js +4 -0
- package/dist/chunk-9kbkdt6e.js.map +10 -0
- package/dist/chunk-eng8tyvd.js +4 -0
- package/dist/chunk-eng8tyvd.js.map +10 -0
- package/dist/chunk-fazw0jvt.js +4 -0
- package/dist/chunk-fazw0jvt.js.map +9 -0
- package/dist/chunk-zzfbcc7e.js +5 -0
- package/dist/chunk-zzfbcc7e.js.map +9 -0
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +9 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +9 -0
- package/dist/keepkey/index.cjs +4 -0
- package/dist/keepkey/index.cjs.map +15 -0
- package/dist/keepkey/index.js +4 -0
- package/dist/keepkey/index.js.map +15 -0
- package/dist/ledger/index.cjs +5 -0
- package/dist/ledger/index.cjs.map +25 -0
- package/dist/ledger/index.js +5 -0
- package/dist/ledger/index.js.map +25 -0
- package/dist/trezor/index.cjs +4 -0
- package/dist/trezor/index.cjs.map +10 -0
- package/dist/trezor/index.js +4 -0
- package/dist/trezor/index.js.map +10 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/keepkey/chains/cosmos.d.ts +7 -0
- package/dist/types/keepkey/chains/cosmos.d.ts.map +1 -0
- package/dist/types/keepkey/chains/evm.d.ts +29 -0
- package/dist/types/keepkey/chains/evm.d.ts.map +1 -0
- package/dist/types/keepkey/chains/mayachain.d.ts +10 -0
- package/dist/types/keepkey/chains/mayachain.d.ts.map +1 -0
- package/dist/types/keepkey/chains/ripple.d.ts +37 -0
- package/dist/types/keepkey/chains/ripple.d.ts.map +1 -0
- package/dist/types/keepkey/chains/thorchain.d.ts +10 -0
- package/dist/types/keepkey/chains/thorchain.d.ts.map +1 -0
- package/dist/types/keepkey/chains/utxo.d.ts +25 -0
- package/dist/types/keepkey/chains/utxo.d.ts.map +1 -0
- package/dist/types/keepkey/coins.d.ts +15 -0
- package/dist/types/keepkey/coins.d.ts.map +1 -0
- package/dist/types/keepkey/index.d.ts +15 -0
- package/dist/types/keepkey/index.d.ts.map +1 -0
- package/dist/types/ledger/clients/cosmos.d.ts +23 -0
- package/dist/types/ledger/clients/cosmos.d.ts.map +1 -0
- package/dist/types/ledger/clients/evm.d.ts +52 -0
- package/dist/types/ledger/clients/evm.d.ts.map +1 -0
- package/dist/types/ledger/clients/near.d.ts +7 -0
- package/dist/types/ledger/clients/near.d.ts.map +1 -0
- package/dist/types/ledger/clients/thorchain/common.d.ts +28 -0
- package/dist/types/ledger/clients/thorchain/common.d.ts.map +1 -0
- package/dist/types/ledger/clients/thorchain/helpers.d.ts +10 -0
- package/dist/types/ledger/clients/thorchain/helpers.d.ts.map +1 -0
- package/dist/types/ledger/clients/thorchain/index.d.ts +25 -0
- package/dist/types/ledger/clients/thorchain/index.d.ts.map +1 -0
- package/dist/types/ledger/clients/thorchain/lib.d.ts +25 -0
- package/dist/types/ledger/clients/thorchain/lib.d.ts.map +1 -0
- package/dist/types/ledger/clients/thorchain/utils.d.ts +5 -0
- package/dist/types/ledger/clients/thorchain/utils.d.ts.map +1 -0
- package/dist/types/ledger/clients/tron.d.ts +26 -0
- package/dist/types/ledger/clients/tron.d.ts.map +1 -0
- package/dist/types/ledger/clients/utxo.d.ts +43 -0
- package/dist/types/ledger/clients/utxo.d.ts.map +1 -0
- package/dist/types/ledger/clients/xrp.d.ts +11 -0
- package/dist/types/ledger/clients/xrp.d.ts.map +1 -0
- package/dist/types/ledger/cosmosTypes.d.ts +43 -0
- package/dist/types/ledger/cosmosTypes.d.ts.map +1 -0
- package/dist/types/ledger/helpers/getLedgerAddress.d.ts +10 -0
- package/dist/types/ledger/helpers/getLedgerAddress.d.ts.map +1 -0
- package/dist/types/ledger/helpers/getLedgerClient.d.ts +42 -0
- package/dist/types/ledger/helpers/getLedgerClient.d.ts.map +1 -0
- package/dist/types/ledger/helpers/getLedgerTransport.d.ts +6 -0
- package/dist/types/ledger/helpers/getLedgerTransport.d.ts.map +1 -0
- package/dist/types/ledger/helpers/index.d.ts +4 -0
- package/dist/types/ledger/helpers/index.d.ts.map +1 -0
- package/dist/types/ledger/index.d.ts +14 -0
- package/dist/types/ledger/index.d.ts.map +1 -0
- package/dist/types/ledger/interfaces/CosmosLedgerInterface.d.ts +14 -0
- package/dist/types/ledger/interfaces/CosmosLedgerInterface.d.ts.map +1 -0
- package/dist/types/ledger/types.d.ts +16 -0
- package/dist/types/ledger/types.d.ts.map +1 -0
- package/dist/types/trezor/evmSigner.d.ts +32 -0
- package/dist/types/trezor/evmSigner.d.ts.map +1 -0
- package/dist/types/trezor/index.d.ts +14 -0
- package/dist/types/trezor/index.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/index.ts +1 -0
- package/src/keepkey/chains/cosmos.ts +69 -0
- package/src/keepkey/chains/evm.ts +117 -0
- package/src/keepkey/chains/mayachain.ts +102 -0
- package/src/keepkey/chains/ripple.ts +88 -0
- package/src/keepkey/chains/thorchain.ts +97 -0
- package/src/keepkey/chains/utxo.ts +141 -0
- package/src/keepkey/coins.ts +70 -0
- package/src/keepkey/index.ts +159 -0
- package/src/ledger/clients/cosmos.ts +83 -0
- package/src/ledger/clients/evm.ts +145 -0
- package/src/ledger/clients/near.ts +67 -0
- package/src/ledger/clients/thorchain/common.ts +93 -0
- package/src/ledger/clients/thorchain/helpers.ts +124 -0
- package/src/ledger/clients/thorchain/index.ts +91 -0
- package/src/ledger/clients/thorchain/lib.ts +282 -0
- package/src/ledger/clients/thorchain/utils.ts +71 -0
- package/src/ledger/clients/tron.ts +84 -0
- package/src/ledger/clients/utxo.ts +158 -0
- package/src/ledger/clients/xrp.ts +50 -0
- package/src/ledger/cosmosTypes.ts +102 -0
- package/src/ledger/helpers/getLedgerAddress.ts +73 -0
- package/src/ledger/helpers/getLedgerClient.ts +121 -0
- package/src/ledger/helpers/getLedgerTransport.ts +106 -0
- package/src/ledger/helpers/index.ts +3 -0
- package/src/ledger/index.ts +303 -0
- package/src/ledger/interfaces/CosmosLedgerInterface.ts +58 -0
- package/src/ledger/types.ts +40 -0
- package/src/trezor/evmSigner.ts +181 -0
- package/src/trezor/index.ts +350 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { KeepKeySdk } from "@keepkey/keepkey-sdk";
|
|
6
|
+
import {
|
|
7
|
+
Chain,
|
|
8
|
+
DerivationPath,
|
|
9
|
+
type DerivationPathArray,
|
|
10
|
+
derivationPathToString,
|
|
11
|
+
FeeOption,
|
|
12
|
+
type GenericTransferParams,
|
|
13
|
+
USwapError,
|
|
14
|
+
type UTXOChain,
|
|
15
|
+
} from "@tcswap/helpers";
|
|
16
|
+
import type { UTXOToolboxes } from "@tcswap/toolboxes/utxo";
|
|
17
|
+
import type { Psbt } from "bitcoinjs-lib";
|
|
18
|
+
import { bip32ToAddressNList, ChainToKeepKeyName } from "../coins";
|
|
19
|
+
|
|
20
|
+
interface KeepKeyInputObject {
|
|
21
|
+
addressNList: number[];
|
|
22
|
+
scriptType: string;
|
|
23
|
+
amount: string;
|
|
24
|
+
vout: number;
|
|
25
|
+
txid: string;
|
|
26
|
+
hex: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function utxoWalletMethods({
|
|
30
|
+
sdk,
|
|
31
|
+
chain,
|
|
32
|
+
derivationPath,
|
|
33
|
+
}: {
|
|
34
|
+
sdk: KeepKeySdk;
|
|
35
|
+
chain: Exclude<UTXOChain, typeof Chain.Zcash>;
|
|
36
|
+
derivationPath?: DerivationPathArray;
|
|
37
|
+
}): Promise<
|
|
38
|
+
UTXOToolboxes[UTXOChain] & {
|
|
39
|
+
address: string;
|
|
40
|
+
signTransaction: (psbt: Psbt, inputs: KeepKeyInputObject[], memo?: string) => Promise<string>;
|
|
41
|
+
}
|
|
42
|
+
> {
|
|
43
|
+
const { getUtxoToolbox } = await import("@tcswap/toolboxes/utxo");
|
|
44
|
+
// This might not work for BCH
|
|
45
|
+
const toolbox = await getUtxoToolbox(chain);
|
|
46
|
+
const scriptType = [Chain.Bitcoin, Chain.Litecoin].includes(chain as typeof Chain.Bitcoin)
|
|
47
|
+
? ("p2wpkh" as const)
|
|
48
|
+
: ("p2pkh" as const);
|
|
49
|
+
|
|
50
|
+
const derivationPathString = derivationPath ? derivationPathToString(derivationPath) : `${DerivationPath[chain]}/0`;
|
|
51
|
+
|
|
52
|
+
const addressInfo = {
|
|
53
|
+
address_n: bip32ToAddressNList(derivationPathString),
|
|
54
|
+
coin: ChainToKeepKeyName[chain],
|
|
55
|
+
script_type: scriptType,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const walletAddress: string = (await sdk.address.utxoGetAddress(addressInfo)).address;
|
|
59
|
+
|
|
60
|
+
const signTransaction = async (psbt: Psbt, inputs: KeepKeyInputObject[], memo = "") => {
|
|
61
|
+
const outputs = psbt.txOutputs
|
|
62
|
+
.map((output) => {
|
|
63
|
+
const { value, address, change } = output as {
|
|
64
|
+
address: string;
|
|
65
|
+
script: Buffer;
|
|
66
|
+
value: number;
|
|
67
|
+
change?: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const outputAddress =
|
|
71
|
+
// @ts-expect-error - stripToCashAddress is not defined in the UTXO toolbox just only on BCH
|
|
72
|
+
chain === Chain.BitcoinCash ? toolbox.stripToCashAddress(address) : address;
|
|
73
|
+
|
|
74
|
+
if (change || address === walletAddress) {
|
|
75
|
+
return {
|
|
76
|
+
addressNList: addressInfo.address_n,
|
|
77
|
+
addressType: "change",
|
|
78
|
+
amount: value,
|
|
79
|
+
isChange: true,
|
|
80
|
+
scriptType,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (outputAddress) {
|
|
85
|
+
return { address: outputAddress, addressType: "spend", amount: value };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null;
|
|
89
|
+
})
|
|
90
|
+
.filter(Boolean);
|
|
91
|
+
|
|
92
|
+
const removeNullAndEmptyObjectsFromArray = (arr: any[]) => {
|
|
93
|
+
return arr.filter((item) => item !== null && typeof item === "object" && Object.keys(item).length > 0);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const responseSign = await sdk.utxo.utxoSignTransaction({
|
|
97
|
+
coin: ChainToKeepKeyName[chain],
|
|
98
|
+
inputs,
|
|
99
|
+
opReturnData: memo,
|
|
100
|
+
outputs: removeNullAndEmptyObjectsFromArray(outputs),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return responseSign.serializedTx?.toString();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const transfer = async ({ recipient, feeOptionKey, feeRate, memo, ...rest }: GenericTransferParams) => {
|
|
107
|
+
if (!walletAddress)
|
|
108
|
+
throw new USwapError("wallet_keepkey_invalid_params", { reason: "From address must be provided" });
|
|
109
|
+
if (!recipient)
|
|
110
|
+
throw new USwapError("wallet_keepkey_invalid_params", { reason: "Recipient address must be provided" });
|
|
111
|
+
|
|
112
|
+
const createTxMethod =
|
|
113
|
+
chain === Chain.BitcoinCash
|
|
114
|
+
? (toolbox as UTXOToolboxes["BCH"]).buildTx
|
|
115
|
+
: (toolbox as UTXOToolboxes["BTC"]).createTransaction;
|
|
116
|
+
|
|
117
|
+
const { psbt, inputs: rawInputs } = await createTxMethod({
|
|
118
|
+
...rest,
|
|
119
|
+
feeRate: feeRate || (await toolbox.getFeeRates())[feeOptionKey || FeeOption.Fast],
|
|
120
|
+
fetchTxHex: true,
|
|
121
|
+
memo,
|
|
122
|
+
recipient,
|
|
123
|
+
sender: walletAddress,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const inputs = rawInputs.map(({ value, index, hash, txHex }) => ({
|
|
127
|
+
//@TODO don't hardcode master, lookup on blockbook what input this is for and what path that address is!
|
|
128
|
+
addressNList: addressInfo.address_n,
|
|
129
|
+
amount: value.toString(),
|
|
130
|
+
hex: txHex || "",
|
|
131
|
+
scriptType,
|
|
132
|
+
txid: hash,
|
|
133
|
+
vout: index,
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
const txHex = await signTransaction(psbt, inputs, memo);
|
|
137
|
+
return toolbox.broadcastTx(txHex);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return { ...toolbox, address: walletAddress, signTransaction, transfer };
|
|
141
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
KeepKey Specific bip32 path conventions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { USwapError } from "@tcswap/helpers";
|
|
10
|
+
|
|
11
|
+
const HARDENED = 0x80000000;
|
|
12
|
+
|
|
13
|
+
export enum ChainToKeepKeyName {
|
|
14
|
+
BTC = "Bitcoin",
|
|
15
|
+
BCH = "BitcoinCash",
|
|
16
|
+
DOGE = "Dogecoin",
|
|
17
|
+
LTC = "Litecoin",
|
|
18
|
+
DASH = "Dash",
|
|
19
|
+
XRP = "Ripple",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function addressNListToBIP32(address: number[]) {
|
|
23
|
+
return `m/${address.map((num) => (num >= HARDENED ? `${num - HARDENED}'` : num)).join("/")}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function bip32Like(path: string) {
|
|
27
|
+
if (path === "m/") return true;
|
|
28
|
+
|
|
29
|
+
return /^m(((\/[0-9]+h)+|(\/[0-9]+H)+|(\/[0-9]+')*)((\/[0-9]+)*))$/.test(path);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function bip32ToAddressNList(initPath: string): number[] {
|
|
33
|
+
let path = initPath;
|
|
34
|
+
|
|
35
|
+
if (!bip32Like(path)) {
|
|
36
|
+
throw new USwapError("wallet_keepkey_invalid_params", { reason: `Not a bip32 path: '${path}'` });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (/^m\//i.test(path)) {
|
|
40
|
+
path = path.slice(2);
|
|
41
|
+
}
|
|
42
|
+
const segments = path.split("/");
|
|
43
|
+
|
|
44
|
+
if (segments.length === 1 && segments[0] === "") return [];
|
|
45
|
+
|
|
46
|
+
const ret = new Array(segments.length);
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < segments.length; i++) {
|
|
49
|
+
// TODO: Check for better way instead of exec
|
|
50
|
+
const segment = segments[i];
|
|
51
|
+
if (segment) {
|
|
52
|
+
const tmp = /(\d+)([hH']?)/.exec(segment);
|
|
53
|
+
if (tmp === null) throw new USwapError("wallet_keepkey_invalid_params", { reason: "Invalid input" });
|
|
54
|
+
|
|
55
|
+
const [, num = "", modifier = ""] = tmp;
|
|
56
|
+
|
|
57
|
+
ret[i] = Number.parseInt(num, 10);
|
|
58
|
+
|
|
59
|
+
if (ret[i] >= HARDENED) throw new USwapError("wallet_keepkey_invalid_params", { reason: "Invalid child index" });
|
|
60
|
+
|
|
61
|
+
if (modifier === "h" || modifier === "H" || modifier === "'") {
|
|
62
|
+
ret[i] += HARDENED;
|
|
63
|
+
} else if (modifier.length > 0) {
|
|
64
|
+
throw new USwapError("wallet_keepkey_invalid_params", { reason: "Invalid modifier" });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return ret;
|
|
70
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { KeepKeySdk } from "@keepkey/keepkey-sdk";
|
|
6
|
+
import {
|
|
7
|
+
Chain,
|
|
8
|
+
type DerivationPathArray,
|
|
9
|
+
filterSupportedChains,
|
|
10
|
+
NetworkDerivationPath,
|
|
11
|
+
USwapConfig,
|
|
12
|
+
USwapError,
|
|
13
|
+
WalletOption,
|
|
14
|
+
} from "@tcswap/helpers";
|
|
15
|
+
|
|
16
|
+
export type { PairingInfo } from "@keepkey/keepkey-sdk";
|
|
17
|
+
|
|
18
|
+
import { createWallet, getWalletSupportedChains } from "@tcswap/wallet-core";
|
|
19
|
+
import { cosmosWalletMethods } from "./chains/cosmos";
|
|
20
|
+
import { KeepKeySigner } from "./chains/evm";
|
|
21
|
+
import { mayachainWalletMethods } from "./chains/mayachain";
|
|
22
|
+
import { thorchainWalletMethods } from "./chains/thorchain";
|
|
23
|
+
import { utxoWalletMethods } from "./chains/utxo";
|
|
24
|
+
|
|
25
|
+
export const keepkeyWallet = createWallet({
|
|
26
|
+
connect: ({ addChain, supportedChains, walletType }) =>
|
|
27
|
+
async function connectKeepkey(chains: Chain[], derivationPathMap?: Record<Chain, DerivationPathArray>) {
|
|
28
|
+
const filteredChains = filterSupportedChains({ chains, supportedChains, walletType });
|
|
29
|
+
const pairingInfo = USwapConfig.get("integrations").keepKey;
|
|
30
|
+
if (!pairingInfo) throw new Error("KeepKey config not found");
|
|
31
|
+
|
|
32
|
+
const initialApiKey = USwapConfig.get("apiKeys").keepKey || "1234";
|
|
33
|
+
|
|
34
|
+
await checkAndLaunch();
|
|
35
|
+
|
|
36
|
+
// Conform to the expected { apiKey, pairingInfo } structure
|
|
37
|
+
const keepkeyConfig = { apiKey: initialApiKey, pairingInfo };
|
|
38
|
+
const keepKeySdk = await KeepKeySdk.create(keepkeyConfig);
|
|
39
|
+
|
|
40
|
+
// Persist the new API key via USwapConfig after pairing
|
|
41
|
+
if (keepkeyConfig.apiKey && keepkeyConfig.apiKey !== initialApiKey) {
|
|
42
|
+
USwapConfig.setApiKey("keepKey", keepkeyConfig.apiKey);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await Promise.all(
|
|
46
|
+
filteredChains.map(async (chain) => {
|
|
47
|
+
const walletMethods = await getWalletMethods({
|
|
48
|
+
chain,
|
|
49
|
+
derivationPath: derivationPathMap?.[chain] || NetworkDerivationPath[chain],
|
|
50
|
+
sdk: keepKeySdk,
|
|
51
|
+
});
|
|
52
|
+
const address = (await walletMethods.getAddress()) || "";
|
|
53
|
+
|
|
54
|
+
addChain({ ...walletMethods, address, chain, walletType: WalletOption.KEEPKEY });
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
name: "connectKeepkey",
|
|
60
|
+
supportedChains: [
|
|
61
|
+
Chain.Arbitrum,
|
|
62
|
+
Chain.Avalanche,
|
|
63
|
+
Chain.Base,
|
|
64
|
+
Chain.BinanceSmartChain,
|
|
65
|
+
Chain.Bitcoin,
|
|
66
|
+
Chain.BitcoinCash,
|
|
67
|
+
Chain.Cosmos,
|
|
68
|
+
Chain.Dogecoin,
|
|
69
|
+
Chain.Dash,
|
|
70
|
+
Chain.Ethereum,
|
|
71
|
+
Chain.Litecoin,
|
|
72
|
+
Chain.Monad,
|
|
73
|
+
Chain.Ripple,
|
|
74
|
+
Chain.Optimism,
|
|
75
|
+
Chain.Polygon,
|
|
76
|
+
Chain.THORChain,
|
|
77
|
+
Chain.Maya,
|
|
78
|
+
Chain.XLayer,
|
|
79
|
+
],
|
|
80
|
+
walletType: WalletOption.KEEPKEY,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export const KEEPKEY_SUPPORTED_CHAINS = getWalletSupportedChains(keepkeyWallet);
|
|
84
|
+
|
|
85
|
+
async function getWalletMethods({
|
|
86
|
+
sdk,
|
|
87
|
+
chain,
|
|
88
|
+
derivationPath,
|
|
89
|
+
}: {
|
|
90
|
+
sdk: KeepKeySdk;
|
|
91
|
+
chain: Chain;
|
|
92
|
+
derivationPath?: DerivationPathArray;
|
|
93
|
+
}) {
|
|
94
|
+
const { getProvider, getEvmToolbox } = await import("@tcswap/toolboxes/evm");
|
|
95
|
+
|
|
96
|
+
switch (chain) {
|
|
97
|
+
case Chain.BinanceSmartChain:
|
|
98
|
+
case Chain.Arbitrum:
|
|
99
|
+
case Chain.Optimism:
|
|
100
|
+
case Chain.Polygon:
|
|
101
|
+
case Chain.Avalanche:
|
|
102
|
+
case Chain.Base:
|
|
103
|
+
case Chain.Ethereum:
|
|
104
|
+
case Chain.Monad:
|
|
105
|
+
case Chain.XLayer: {
|
|
106
|
+
const provider = await getProvider(chain);
|
|
107
|
+
const signer = new KeepKeySigner({ chain, derivationPath, provider, sdk });
|
|
108
|
+
const toolbox = await getEvmToolbox(chain, { provider, signer });
|
|
109
|
+
|
|
110
|
+
return toolbox;
|
|
111
|
+
}
|
|
112
|
+
case Chain.Cosmos: {
|
|
113
|
+
return cosmosWalletMethods({ derivationPath, sdk });
|
|
114
|
+
}
|
|
115
|
+
case Chain.THORChain: {
|
|
116
|
+
return thorchainWalletMethods({ derivationPath, sdk });
|
|
117
|
+
}
|
|
118
|
+
case Chain.Maya: {
|
|
119
|
+
return mayachainWalletMethods({ derivationPath, sdk });
|
|
120
|
+
}
|
|
121
|
+
case Chain.Bitcoin:
|
|
122
|
+
case Chain.BitcoinCash:
|
|
123
|
+
case Chain.Dash:
|
|
124
|
+
case Chain.Dogecoin:
|
|
125
|
+
case Chain.Litecoin: {
|
|
126
|
+
return utxoWalletMethods({ chain, derivationPath, sdk });
|
|
127
|
+
}
|
|
128
|
+
case Chain.Ripple: {
|
|
129
|
+
const { rippleWalletMethods } = await import("./chains/ripple");
|
|
130
|
+
return rippleWalletMethods({ derivationPath, sdk });
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
throw new USwapError("wallet_keepkey_chain_not_supported", { chain });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// kk-sdk docs: https://keepkey.com/blog/building_on_the_keepkey_sdk
|
|
138
|
+
// test spec: if offline, launch keepkey-bridge
|
|
139
|
+
async function checkAndLaunch(attempts = 0) {
|
|
140
|
+
if (attempts >= 3) {
|
|
141
|
+
alert("KeepKey desktop is required for keepkey-sdk, please go to https://keepkey.com/get-started");
|
|
142
|
+
}
|
|
143
|
+
const isAvailable = await checkKeepkeyAvailability();
|
|
144
|
+
|
|
145
|
+
if (!isAvailable) {
|
|
146
|
+
window.location.assign("keepkey://launch");
|
|
147
|
+
await new Promise((resolve) => setTimeout(resolve, 30000));
|
|
148
|
+
await checkAndLaunch(attempts + 1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function checkKeepkeyAvailability(spec = "http://localhost:1646/spec/swagger.json") {
|
|
153
|
+
try {
|
|
154
|
+
const response = await fetch(spec);
|
|
155
|
+
return response.status === 200;
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { type DerivationPathArray, derivationPathToString, NetworkDerivationPath, USwapError } from "@tcswap/helpers";
|
|
6
|
+
import { CosmosLedgerInterface } from "../interfaces/CosmosLedgerInterface";
|
|
7
|
+
|
|
8
|
+
export class CosmosLedger extends CosmosLedgerInterface {
|
|
9
|
+
private pubKey: string | null = null;
|
|
10
|
+
|
|
11
|
+
derivationPath: string;
|
|
12
|
+
|
|
13
|
+
constructor(derivationPath: DerivationPathArray = NetworkDerivationPath.GAIA) {
|
|
14
|
+
super();
|
|
15
|
+
this.chain = "cosmos";
|
|
16
|
+
this.derivationPath = derivationPathToString(derivationPath);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
connect = async () => {
|
|
20
|
+
await this.checkOrCreateTransportAndLedger(true);
|
|
21
|
+
const { publicKey, address } = await this.getAddressAndPubKey();
|
|
22
|
+
|
|
23
|
+
this.pubKey = Buffer.from(publicKey, "hex").toString("base64");
|
|
24
|
+
|
|
25
|
+
return address;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
getAddressAndPubKey = async () => {
|
|
29
|
+
await this.checkOrCreateTransportAndLedger(true);
|
|
30
|
+
|
|
31
|
+
const response = await this.ledgerApp.getAddress(this.derivationPath, this.chain);
|
|
32
|
+
|
|
33
|
+
return response;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
signTransaction = async (rawTx: string, sequence = "0") => {
|
|
37
|
+
await this.checkOrCreateTransportAndLedger(true);
|
|
38
|
+
|
|
39
|
+
const { return_code, error_message, signature } = await this.ledgerApp.sign(this.derivationPath, rawTx);
|
|
40
|
+
|
|
41
|
+
if (!this.pubKey) throw new USwapError("wallet_ledger_pubkey_not_found");
|
|
42
|
+
|
|
43
|
+
this.validateResponse(return_code, error_message);
|
|
44
|
+
|
|
45
|
+
return [{ pub_key: { type: "tendermint/PubKeySecp256k1", value: this.pubKey }, sequence, signature }];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
signAmino = async (signerAddress: string, signDoc: any): Promise<any> => {
|
|
49
|
+
await this.checkOrCreateTransportAndLedger(true);
|
|
50
|
+
|
|
51
|
+
const accounts = await this.getAccounts();
|
|
52
|
+
const accountIndex = accounts.findIndex((account) => account.address === signerAddress);
|
|
53
|
+
|
|
54
|
+
if (accountIndex === -1) {
|
|
55
|
+
throw new USwapError("wallet_ledger_address_not_found", { address: signerAddress });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const importedAmino = await import("@cosmjs/amino");
|
|
59
|
+
const encodeSecp256k1Signature =
|
|
60
|
+
importedAmino.encodeSecp256k1Signature ?? importedAmino.default?.encodeSecp256k1Signature;
|
|
61
|
+
const serializeSignDoc = importedAmino.serializeSignDoc ?? importedAmino.default?.serializeSignDoc;
|
|
62
|
+
const importedCrypto = await import("@cosmjs/crypto");
|
|
63
|
+
const Secp256k1Signature = importedCrypto.Secp256k1Signature ?? importedCrypto.default?.Secp256k1Signature;
|
|
64
|
+
|
|
65
|
+
const message = serializeSignDoc(signDoc);
|
|
66
|
+
const signature = await this.ledgerApp.sign(this.derivationPath, message);
|
|
67
|
+
|
|
68
|
+
this.validateResponse(signature.return_code, signature.error_message);
|
|
69
|
+
|
|
70
|
+
const secpSignature = Secp256k1Signature.fromDer(signature.signature).toFixedLength();
|
|
71
|
+
|
|
72
|
+
return { signature: encodeSecp256k1Signature(accounts[0].pubkey, secpSignature), signed: signDoc };
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
getAccounts = async () => {
|
|
76
|
+
await this.checkOrCreateTransportAndLedger(true);
|
|
77
|
+
|
|
78
|
+
const addressAndPubKey = await this.getAddressAndPubKey();
|
|
79
|
+
return [
|
|
80
|
+
{ address: addressAndPubKey.address, algo: "secp256k1", pubkey: Buffer.from(addressAndPubKey.publicKey, "hex") },
|
|
81
|
+
] as any[];
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type EthereumApp from "@ledgerhq/hw-app-eth";
|
|
6
|
+
import {
|
|
7
|
+
ChainId,
|
|
8
|
+
type DerivationPathArray,
|
|
9
|
+
derivationPathToString,
|
|
10
|
+
NetworkDerivationPath,
|
|
11
|
+
USwapError,
|
|
12
|
+
} from "@tcswap/helpers";
|
|
13
|
+
import { AbstractSigner, type Provider, type TransactionRequest } from "ethers";
|
|
14
|
+
|
|
15
|
+
import { getLedgerTransport } from "../helpers/getLedgerTransport";
|
|
16
|
+
|
|
17
|
+
class EVMLedgerInterface extends AbstractSigner {
|
|
18
|
+
chainId: ChainId = ChainId.Ethereum;
|
|
19
|
+
derivationPath = "";
|
|
20
|
+
ledgerApp: InstanceType<typeof EthereumApp> | null = null;
|
|
21
|
+
ledgerTimeout = 50000;
|
|
22
|
+
|
|
23
|
+
constructor({
|
|
24
|
+
provider,
|
|
25
|
+
derivationPath = NetworkDerivationPath.OP,
|
|
26
|
+
chainId = ChainId.Optimism,
|
|
27
|
+
}: { provider: Provider; derivationPath?: DerivationPathArray | string; chainId?: ChainId }) {
|
|
28
|
+
super(provider);
|
|
29
|
+
|
|
30
|
+
this.chainId = chainId || ChainId.Ethereum;
|
|
31
|
+
this.derivationPath = typeof derivationPath === "string" ? derivationPath : derivationPathToString(derivationPath);
|
|
32
|
+
|
|
33
|
+
Object.defineProperty(this, "provider", { enumerable: true, value: provider || null, writable: false });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
connect = (provider: Provider) =>
|
|
37
|
+
new EVMLedgerInterface({ chainId: this.chainId, derivationPath: this.derivationPath, provider });
|
|
38
|
+
|
|
39
|
+
checkOrCreateTransportAndLedger = async () => {
|
|
40
|
+
if (this.ledgerApp) return;
|
|
41
|
+
await this.createTransportAndLedger();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
createTransportAndLedger = async () => {
|
|
45
|
+
const transport = await getLedgerTransport();
|
|
46
|
+
const EthereumApp = (await import("@ledgerhq/hw-app-eth")).default;
|
|
47
|
+
|
|
48
|
+
this.ledgerApp = new EthereumApp(transport);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
getAddress = async () => {
|
|
52
|
+
const response = await this.getAddressAndPubKey();
|
|
53
|
+
if (!response) throw new USwapError("wallet_ledger_failed_to_get_address");
|
|
54
|
+
return response.address;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
getAddressAndPubKey = async () => {
|
|
58
|
+
await this.createTransportAndLedger();
|
|
59
|
+
return this.ledgerApp?.getAddress(this.derivationPath);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
showAddressAndPubKey = async () => {
|
|
63
|
+
await this.createTransportAndLedger();
|
|
64
|
+
return this.ledgerApp?.getAddress(this.derivationPath, true);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
signMessage = async (messageHex: string) => {
|
|
68
|
+
const { Signature } = await import("ethers");
|
|
69
|
+
await this.createTransportAndLedger();
|
|
70
|
+
|
|
71
|
+
const sig = await this.ledgerApp?.signPersonalMessage(this.derivationPath, messageHex);
|
|
72
|
+
|
|
73
|
+
if (!sig) throw new USwapError("wallet_ledger_signing_error");
|
|
74
|
+
|
|
75
|
+
sig.r = `0x${sig.r}`;
|
|
76
|
+
sig.s = `0x${sig.s}`;
|
|
77
|
+
return Signature.from(sig).serialized;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
sendTransaction = async (tx: TransactionRequest): Promise<any> => {
|
|
81
|
+
if (!this.provider) throw new USwapError("wallet_ledger_no_provider");
|
|
82
|
+
|
|
83
|
+
const signedTxHex = await this.signTransaction(tx);
|
|
84
|
+
|
|
85
|
+
return await this.provider.broadcastTransaction(signedTxHex);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
signTypedData(): Promise<string> {
|
|
89
|
+
throw new USwapError("wallet_ledger_method_not_supported", { method: "signTypedData" });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
signTransaction = async (tx: TransactionRequest) => {
|
|
93
|
+
const { Transaction } = await import("ethers");
|
|
94
|
+
await this.createTransportAndLedger();
|
|
95
|
+
|
|
96
|
+
const transactionCount = await this.provider?.getTransactionCount(tx.from || (await this.getAddress()));
|
|
97
|
+
|
|
98
|
+
const baseTx = {
|
|
99
|
+
chainId: tx.chainId || this.chainId,
|
|
100
|
+
data: tx.data,
|
|
101
|
+
gasLimit: tx.gasLimit,
|
|
102
|
+
...(tx.gasPrice && { gasPrice: tx.gasPrice }),
|
|
103
|
+
...(!tx.gasPrice &&
|
|
104
|
+
tx.maxFeePerGas && { maxFeePerGas: tx.maxFeePerGas, maxPriorityFeePerGas: tx.maxPriorityFeePerGas }),
|
|
105
|
+
nonce: tx.nonce !== undefined ? Number((tx.nonce || transactionCount || 0).toString()) : transactionCount,
|
|
106
|
+
to: tx.to?.toString(),
|
|
107
|
+
type: tx.type && !Number.isNaN(tx.type) ? tx.type : tx.maxFeePerGas ? 2 : 0,
|
|
108
|
+
value: tx.value,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ledger expects the tx to be serialized without the 0x prefix
|
|
112
|
+
const unsignedTx = Transaction.from(baseTx).unsignedSerialized.slice(2);
|
|
113
|
+
|
|
114
|
+
const { ledgerService } = await import("@ledgerhq/hw-app-eth");
|
|
115
|
+
|
|
116
|
+
const resolution = await ledgerService.resolveTransaction(unsignedTx, {}, { erc20: true, externalPlugins: true });
|
|
117
|
+
|
|
118
|
+
const signature = await this.ledgerApp?.signTransaction(this.derivationPath, unsignedTx, resolution);
|
|
119
|
+
|
|
120
|
+
if (!signature) throw new USwapError("wallet_ledger_signing_error");
|
|
121
|
+
|
|
122
|
+
const { r, s, v } = signature;
|
|
123
|
+
|
|
124
|
+
return Transaction.from({ ...baseTx, signature: { r: `0x${r}`, s: `0x${s}`, v: Number(BigInt(v)) } }).serialized;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type LedgerParams = { provider: Provider; derivationPath?: DerivationPathArray };
|
|
129
|
+
|
|
130
|
+
export const ArbitrumLedger = (params: LedgerParams) =>
|
|
131
|
+
new EVMLedgerInterface({ ...params, chainId: ChainId.Arbitrum });
|
|
132
|
+
export const AuroraLedger = (params: LedgerParams) => new EVMLedgerInterface({ ...params, chainId: ChainId.Aurora });
|
|
133
|
+
export const AvalancheLedger = (params: LedgerParams) =>
|
|
134
|
+
new EVMLedgerInterface({ ...params, chainId: ChainId.Avalanche });
|
|
135
|
+
export const BaseLedger = (params: LedgerParams) => new EVMLedgerInterface({ ...params, chainId: ChainId.Base });
|
|
136
|
+
export const EthereumLedger = (params: LedgerParams) =>
|
|
137
|
+
new EVMLedgerInterface({ ...params, chainId: ChainId.Ethereum });
|
|
138
|
+
export const GnosisLedger = (params: LedgerParams) => new EVMLedgerInterface({ ...params, chainId: ChainId.Gnosis });
|
|
139
|
+
export const OptimismLedger = (params: LedgerParams) =>
|
|
140
|
+
new EVMLedgerInterface({ ...params, chainId: ChainId.Optimism });
|
|
141
|
+
export const PolygonLedger = (params: LedgerParams) => new EVMLedgerInterface({ ...params, chainId: ChainId.Polygon });
|
|
142
|
+
export const BinanceSmartChainLedger = (params: LedgerParams) =>
|
|
143
|
+
new EVMLedgerInterface({ ...params, chainId: ChainId.BinanceSmartChain });
|
|
144
|
+
export const MonadLedger = (params: LedgerParams) => new EVMLedgerInterface({ ...params, chainId: ChainId.Monad });
|
|
145
|
+
export const XLayerLedger = (params: LedgerParams) => new EVMLedgerInterface({ ...params, chainId: ChainId.XLayer });
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { SignedTransaction, Transaction } from "@near-js/transactions";
|
|
6
|
+
import type { DerivationPathArray } from "@tcswap/helpers";
|
|
7
|
+
import type { NearSigner } from "@tcswap/toolboxes/near";
|
|
8
|
+
import { getLedgerTransport } from "../helpers/getLedgerTransport";
|
|
9
|
+
|
|
10
|
+
export async function getNearLedgerClient(derivationPath?: DerivationPathArray) {
|
|
11
|
+
const Near = (await import("@ledgerhq/hw-app-near")).default;
|
|
12
|
+
const { Chain, NetworkDerivationPath, USwapError } = await import("@tcswap/helpers");
|
|
13
|
+
const transport = await getLedgerTransport();
|
|
14
|
+
const nearApp = new Near(transport);
|
|
15
|
+
|
|
16
|
+
const path = (derivationPath || NetworkDerivationPath[Chain.Near]).join("'/").concat("'");
|
|
17
|
+
|
|
18
|
+
const { address, publicKey: pubKeyHex } = await nearApp.getAddress(path);
|
|
19
|
+
|
|
20
|
+
const signer = {
|
|
21
|
+
getAddress() {
|
|
22
|
+
return Promise.resolve(address);
|
|
23
|
+
},
|
|
24
|
+
async getPublicKey() {
|
|
25
|
+
const { PublicKey } = await import("@near-js/crypto");
|
|
26
|
+
return PublicKey.fromString(`ed25519:${pubKeyHex}`);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
signDelegateAction(_delegateAction: any) {
|
|
30
|
+
return Promise.reject(
|
|
31
|
+
new USwapError("wallet_ledger_method_not_supported", { method: "signDelegateAction", wallet: "Ledger" }),
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
signNep413Message(
|
|
36
|
+
_message: string,
|
|
37
|
+
_accountId: string,
|
|
38
|
+
_recipient: string,
|
|
39
|
+
_nonce: Uint8Array,
|
|
40
|
+
_callbackUrl?: string,
|
|
41
|
+
) {
|
|
42
|
+
return Promise.reject(
|
|
43
|
+
new USwapError("wallet_ledger_method_not_supported", { method: "signNep413Message", wallet: "Ledger" }),
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async signTransaction(transaction: Transaction) {
|
|
48
|
+
const { Signature, SignedTransaction } = await import("@near-js/transactions");
|
|
49
|
+
try {
|
|
50
|
+
const signatureArray = await nearApp.signTransaction(transaction.encode(), path);
|
|
51
|
+
if (!signatureArray) {
|
|
52
|
+
throw new Error("Signature undefined");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const signature = new Signature({ data: signatureArray, keyType: 0 });
|
|
56
|
+
|
|
57
|
+
const signedTransaction = new SignedTransaction({ signature, transaction });
|
|
58
|
+
|
|
59
|
+
return [signatureArray, signedTransaction] as [Uint8Array<ArrayBufferLike>, SignedTransaction];
|
|
60
|
+
} catch (error) {
|
|
61
|
+
throw new USwapError("wallet_ledger_signing_error", { error });
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return signer as NearSigner;
|
|
67
|
+
}
|