@swapkit/toolboxes 1.0.0-beta.0

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.
Files changed (89) hide show
  1. package/dist/chunk-fazw0jvt.js +3 -0
  2. package/dist/chunk-fazw0jvt.js.map +9 -0
  3. package/dist/chunk-tvrdndbw.js +4 -0
  4. package/dist/chunk-tvrdndbw.js.map +9 -0
  5. package/dist/cosmos/index.cjs +3 -0
  6. package/dist/cosmos/index.cjs.map +19 -0
  7. package/dist/cosmos/index.js +3 -0
  8. package/dist/cosmos/index.js.map +19 -0
  9. package/dist/evm/index.cjs +3 -0
  10. package/dist/evm/index.cjs.map +24 -0
  11. package/dist/evm/index.js +3 -0
  12. package/dist/evm/index.js.map +24 -0
  13. package/dist/index.cjs +3 -0
  14. package/dist/index.cjs.map +9 -0
  15. package/dist/index.js +3 -0
  16. package/dist/index.js.map +9 -0
  17. package/dist/radix/index.cjs +3 -0
  18. package/dist/radix/index.cjs.map +10 -0
  19. package/dist/radix/index.js +3 -0
  20. package/dist/radix/index.js.map +10 -0
  21. package/dist/solana/index.cjs +3 -0
  22. package/dist/solana/index.cjs.map +10 -0
  23. package/dist/solana/index.js +3 -0
  24. package/dist/solana/index.js.map +10 -0
  25. package/dist/substrate/index.cjs +3 -0
  26. package/dist/substrate/index.cjs.map +12 -0
  27. package/dist/substrate/index.js +3 -0
  28. package/dist/substrate/index.js.map +12 -0
  29. package/dist/utxo/index.cjs +3 -0
  30. package/dist/utxo/index.cjs.map +18 -0
  31. package/dist/utxo/index.js +3 -0
  32. package/dist/utxo/index.js.map +18 -0
  33. package/package.json +105 -0
  34. package/src/cosmos/index.ts +11 -0
  35. package/src/cosmos/thorchainUtils/addressFormat.ts +24 -0
  36. package/src/cosmos/thorchainUtils/index.ts +4 -0
  37. package/src/cosmos/thorchainUtils/messages.ts +244 -0
  38. package/src/cosmos/thorchainUtils/registry.ts +47 -0
  39. package/src/cosmos/thorchainUtils/types/client-types.ts +80 -0
  40. package/src/cosmos/thorchainUtils/types/index.ts +1 -0
  41. package/src/cosmos/thorchainUtils/types/proto/MsgCompiled.js +2806 -0
  42. package/src/cosmos/thorchainUtils/types/proto/MsgCompiled.ts +2802 -0
  43. package/src/cosmos/thorchainUtils/util.ts +46 -0
  44. package/src/cosmos/toolbox/BaseCosmosToolbox.ts +254 -0
  45. package/src/cosmos/toolbox/gaia.ts +39 -0
  46. package/src/cosmos/toolbox/getToolboxByChain.ts +29 -0
  47. package/src/cosmos/toolbox/kujira.ts +61 -0
  48. package/src/cosmos/toolbox/thorchain.ts +321 -0
  49. package/src/cosmos/types.ts +31 -0
  50. package/src/cosmos/util.ts +230 -0
  51. package/src/evm/__tests__/ethereum.test.ts +147 -0
  52. package/src/evm/api.ts +157 -0
  53. package/src/evm/contracts/eth/multicall.ts +165 -0
  54. package/src/evm/contracts/op/gasOracle.ts +151 -0
  55. package/src/evm/helpers.ts +145 -0
  56. package/src/evm/index.ts +20 -0
  57. package/src/evm/provider.ts +6 -0
  58. package/src/evm/toolbox/EVMToolbox.ts +662 -0
  59. package/src/evm/toolbox/arb.ts +61 -0
  60. package/src/evm/toolbox/avax.ts +36 -0
  61. package/src/evm/toolbox/base.ts +42 -0
  62. package/src/evm/toolbox/bsc.ts +34 -0
  63. package/src/evm/toolbox/eth.ts +44 -0
  64. package/src/evm/toolbox/getToolboxByChain.ts +42 -0
  65. package/src/evm/toolbox/matic.ts +42 -0
  66. package/src/evm/toolbox/op.ts +163 -0
  67. package/src/evm/types.ts +118 -0
  68. package/src/index.ts +0 -0
  69. package/src/radix/index.ts +151 -0
  70. package/src/radix/toolbox.ts +693 -0
  71. package/src/solana/index.ts +49 -0
  72. package/src/solana/toolbox.ts +272 -0
  73. package/src/substrate/index.ts +3 -0
  74. package/src/substrate/toolbox/baseSubstrateToolbox.ts +286 -0
  75. package/src/substrate/toolbox/index.ts +40 -0
  76. package/src/substrate/types/index.ts +2 -0
  77. package/src/substrate/types/network.ts +42 -0
  78. package/src/substrate/types/wallet.ts +78 -0
  79. package/src/utxo/helpers/api.ts +431 -0
  80. package/src/utxo/helpers/bchaddrjs.ts +177 -0
  81. package/src/utxo/helpers/coinselect.ts +96 -0
  82. package/src/utxo/helpers/index.ts +5 -0
  83. package/src/utxo/helpers/txSize.ts +104 -0
  84. package/src/utxo/helpers/utils.ts +45 -0
  85. package/src/utxo/index.ts +9 -0
  86. package/src/utxo/toolbox/bitcoinCash.ts +281 -0
  87. package/src/utxo/toolbox/index.ts +37 -0
  88. package/src/utxo/toolbox/utxo.ts +360 -0
  89. package/src/utxo/types.ts +70 -0
@@ -0,0 +1,96 @@
1
+ import { Chain, type UTXOChain } from "@swapkit/helpers";
2
+
3
+ import {
4
+ TX_OVERHEAD,
5
+ UTXOScriptType,
6
+ calculateTxSize,
7
+ getInputSize,
8
+ getOutputSize,
9
+ getScriptTypeForAddress,
10
+ } from "../helpers";
11
+ import type { TargetOutput, UTXOCalculateTxSizeParams, UTXOType } from "../types";
12
+
13
+ export const getDustThreshold = (chain: UTXOChain) => {
14
+ switch (chain) {
15
+ case Chain.Bitcoin:
16
+ case Chain.BitcoinCash:
17
+ return 550;
18
+ case Chain.Dash:
19
+ case Chain.Litecoin:
20
+ return 5500;
21
+ case Chain.Dogecoin:
22
+ return 100000;
23
+ default:
24
+ throw new Error("Invalid Chain");
25
+ }
26
+ };
27
+
28
+ export const accumulative = ({
29
+ inputs,
30
+ outputs,
31
+ feeRate: initialFeeRate = 1,
32
+ chain = Chain.Bitcoin,
33
+ }: UTXOCalculateTxSizeParams & { outputs: TargetOutput[]; chain: UTXOChain }) => {
34
+ const feeRate = Math.ceil(initialFeeRate);
35
+
36
+ const newTxType =
37
+ inputs[0] && "address" in inputs[0] && inputs[0].address
38
+ ? getScriptTypeForAddress(inputs[0].address)
39
+ : UTXOScriptType.P2PKH;
40
+ // skip input if adding it would cost more than input is worth
41
+ const filteredInputs = inputs.filter((input) => getInputSize(input) * feeRate <= input.value);
42
+
43
+ const txSizeWithoutInputs =
44
+ TX_OVERHEAD + outputs.reduce((total, output) => total + getOutputSize(output, newTxType), 0);
45
+
46
+ const amountToSend = outputs.reduce((total, output) => total + output.value, 0);
47
+
48
+ let fees = txSizeWithoutInputs * feeRate;
49
+ let inputsValue = 0;
50
+ const inputsToUse: typeof inputs = [];
51
+
52
+ for (const input of filteredInputs) {
53
+ const inputSize = getInputSize(input);
54
+ const inputFee = feeRate * inputSize;
55
+
56
+ fees += inputFee;
57
+ inputsValue += input.value;
58
+
59
+ inputsToUse.push(input);
60
+
61
+ const totalCost = fees + amountToSend;
62
+
63
+ // we need more inputs
64
+ if (inputsValue < totalCost) continue;
65
+
66
+ const remainder = inputsValue - totalCost;
67
+
68
+ const feeForExtraOutput = feeRate * getOutputSize({ address: "", value: 0 }, newTxType);
69
+
70
+ // potential change address
71
+ if (remainder > feeForExtraOutput) {
72
+ const feeAfterExtraOutput = feeForExtraOutput + fees;
73
+ const remainderAfterExtraOutput = inputsValue - (amountToSend + feeAfterExtraOutput);
74
+
75
+ // is it worth a change output aka can we send it in the future?
76
+ if (
77
+ remainderAfterExtraOutput >
78
+ Math.max(getInputSize({} as UTXOType) * feeRate, getDustThreshold(chain))
79
+ ) {
80
+ return {
81
+ inputs: inputsToUse,
82
+ outputs: outputs.concat({ value: remainderAfterExtraOutput, address: "" }),
83
+ fee: feeAfterExtraOutput,
84
+ };
85
+ }
86
+ }
87
+ return {
88
+ inputs: inputsToUse,
89
+ outputs,
90
+ fee: fees,
91
+ };
92
+ }
93
+
94
+ // We don't have enough inputs, let's calculate transaction fee accrude to the last input
95
+ return { fee: feeRate * calculateTxSize({ inputs, outputs, feeRate }) };
96
+ };
@@ -0,0 +1,5 @@
1
+ export * from "./api";
2
+ export * from "./bchaddrjs";
3
+ export * from "./coinselect";
4
+ export * from "./txSize";
5
+ export * from "./utils";
@@ -0,0 +1,104 @@
1
+ import { opcodes, script } from "bitcoinjs-lib";
2
+
3
+ import type {
4
+ TargetOutput,
5
+ UTXOCalculateTxSizeParams,
6
+ UTXOInputWithScriptType,
7
+ UTXOType,
8
+ } from "../types";
9
+
10
+ /**
11
+ * Minimum transaction fee
12
+ * 1000 satoshi/kB (similar to current `minrelaytxfee`)
13
+ * @see https://github.com/bitcoin/bitcoin/blob/db88db47278d2e7208c50d16ab10cb355067d071/src/validation.h#L56
14
+ */
15
+ export const MIN_TX_FEE = 1000;
16
+ export const TX_OVERHEAD = 4 + 1 + 1 + 4; //10
17
+ export const OP_RETURN_OVERHEAD = 1 + 8 + 1; //10
18
+ const TX_INPUT_BASE = 32 + 4 + 1 + 4; // 41
19
+ const TX_INPUT_PUBKEYHASH = 107;
20
+
21
+ export const compileMemo = (memo: string) => {
22
+ const data = Buffer.from(memo, "utf8"); // converts MEMO to buffer
23
+ return script.compile([opcodes.OP_RETURN as number, data]); // Compile OP_RETURN script
24
+ };
25
+
26
+ export enum UTXOScriptType {
27
+ P2PKH = "P2PKH", // legacy
28
+ // P2SH = 'P2SH', // multisig
29
+ P2WPKH = "P2WPKH", // bech32 - native segwit
30
+ // P2TR = "P2TR", // taproot
31
+ }
32
+
33
+ export const InputSizes: Record<UTXOScriptType, number> = {
34
+ [UTXOScriptType.P2PKH]: 148,
35
+ // [UTXOScriptType.P2SH]: 91,
36
+ [UTXOScriptType.P2WPKH]: 68,
37
+ };
38
+
39
+ export const OutputSizes: Record<UTXOScriptType, number> = {
40
+ [UTXOScriptType.P2PKH]: 34,
41
+ // [UTXOScriptType.P2SH]: 91,
42
+ [UTXOScriptType.P2WPKH]: 31,
43
+ };
44
+
45
+ export const getScriptTypeForAddress = (address: string) => {
46
+ if (address.startsWith("bc1") || address.startsWith("ltc1")) {
47
+ return UTXOScriptType.P2WPKH;
48
+ }
49
+ // if (address.startsWith('3') || address.startsWith('M')) {
50
+ // return UTXOScriptType.P2SH;
51
+ // }
52
+ if (
53
+ address.startsWith("1") ||
54
+ address.startsWith("3") ||
55
+ address.startsWith("L") ||
56
+ address.startsWith("M") ||
57
+ address.startsWith("X") ||
58
+ address.startsWith("D") ||
59
+ address.startsWith("bitcoincash:q") ||
60
+ address.startsWith("q")
61
+ ) {
62
+ return UTXOScriptType.P2PKH;
63
+ }
64
+ throw new Error("Invalid address");
65
+ };
66
+
67
+ export const calculateTxSize = ({ inputs, outputs, feeRate }: UTXOCalculateTxSizeParams) => {
68
+ const newTxType =
69
+ inputs[0] && "address" in inputs[0] && inputs[0].address
70
+ ? getScriptTypeForAddress(inputs[0].address)
71
+ : UTXOScriptType.P2PKH;
72
+ const inputSize = inputs
73
+ .filter(
74
+ (utxo) =>
75
+ utxo.value >=
76
+ InputSizes["type" in utxo ? utxo.type : UTXOScriptType.P2PKH] * Math.ceil(feeRate),
77
+ )
78
+ .reduce((total, utxo) => total + getInputSize(utxo), 0);
79
+
80
+ const outputSize =
81
+ outputs?.reduce((total, output) => total + getOutputSize(output), 0) || OutputSizes[newTxType];
82
+
83
+ return TX_OVERHEAD + inputSize + outputSize;
84
+ };
85
+
86
+ export const getInputSize = (input: UTXOInputWithScriptType | UTXOType) => {
87
+ if ("type" in input) {
88
+ return InputSizes[input.type];
89
+ }
90
+ if ("address" in input && input.address) {
91
+ return InputSizes[getScriptTypeForAddress(input.address as string)];
92
+ }
93
+ return TX_INPUT_BASE + TX_INPUT_PUBKEYHASH;
94
+ };
95
+
96
+ export const getOutputSize = (output: TargetOutput, scriptType?: UTXOScriptType) => {
97
+ if (output?.script) {
98
+ return OP_RETURN_OVERHEAD + output.script.length + (output.script.length >= 74 ? 2 : 1);
99
+ }
100
+ if (scriptType) {
101
+ return OutputSizes[scriptType];
102
+ }
103
+ return OutputSizes[UTXOScriptType.P2PKH];
104
+ };
@@ -0,0 +1,45 @@
1
+ import { Chain, FeeOption } from "@swapkit/helpers";
2
+ import { networks } from "bitcoinjs-lib";
3
+ // @ts-ignore TODO: check why wallets doesn't see modules included in toolbox
4
+ import coininfo from "coininfo";
5
+
6
+ const pid = typeof process !== "undefined" && process.pid ? process.pid.toString(36) : "";
7
+
8
+ export const getNetwork = (chain: Chain) => {
9
+ switch (chain) {
10
+ case Chain.Bitcoin:
11
+ return networks.bitcoin;
12
+ case Chain.BitcoinCash:
13
+ return coininfo.bitcoincash.main.toBitcoinJS();
14
+ case Chain.Dash:
15
+ return coininfo.dash.main.toBitcoinJS();
16
+ case Chain.Litecoin:
17
+ return coininfo.litecoin.main.toBitcoinJS();
18
+
19
+ case Chain.Dogecoin: {
20
+ const bip32 = { private: 0x04358394, public: 0x043587cf };
21
+ const test = coininfo.dogecoin.test;
22
+ test.versions.bip32 = bip32;
23
+ return coininfo.dogecoin.main.toBitcoinJS();
24
+ }
25
+ default:
26
+ throw new Error("Invalid chain");
27
+ }
28
+ };
29
+
30
+ export const standardFeeRates = (rate: number) => ({
31
+ [FeeOption.Average]: rate,
32
+ [FeeOption.Fast]: rate * 1.5,
33
+ [FeeOption.Fastest]: rate * 2.0,
34
+ });
35
+
36
+ let last = 0;
37
+ const now = () => {
38
+ const time = Date.now();
39
+ const lastTime = last || time;
40
+ last = lastTime;
41
+
42
+ return time > last ? time : lastTime + 1;
43
+ };
44
+
45
+ export const uniqid = () => pid + now().toString(36);
@@ -0,0 +1,9 @@
1
+ export { type Network, networks, Psbt, Transaction } from "bitcoinjs-lib";
2
+
3
+ /**
4
+ * Package
5
+ */
6
+ export * from "./toolbox/index";
7
+ export * from "./toolbox/utxo";
8
+ export * from "./helpers/index";
9
+ export * from "./types";
@@ -0,0 +1,281 @@
1
+ import * as secp256k1 from "@bitcoinerlab/secp256k1";
2
+ import {
3
+ HDNode,
4
+ Transaction,
5
+ TransactionBuilder,
6
+ address as bchAddress,
7
+ // @ts-ignore TODO: check why wallets doesn't see modules included in toolbox
8
+ } from "@psf/bitcoincashjs-lib";
9
+ import { mnemonicToSeedSync } from "@scure/bip39";
10
+ import { Chain, DerivationPath, FeeOption, type UTXOChain } from "@swapkit/helpers";
11
+ import { Psbt } from "bitcoinjs-lib";
12
+
13
+ import {
14
+ accumulative,
15
+ Network as bchNetwork,
16
+ compileMemo,
17
+ detectAddressNetwork,
18
+ getNetwork,
19
+ getUtxoApi,
20
+ isValidAddress,
21
+ toCashAddress,
22
+ toLegacyAddress,
23
+ } from "../helpers";
24
+ import type {
25
+ TargetOutput,
26
+ TransactionBuilderType,
27
+ TransactionType,
28
+ UTXOBuildTxParams,
29
+ UTXOType,
30
+ UTXOWalletTransferParams,
31
+ } from "../types";
32
+
33
+ import { BaseUTXOToolbox } from "./utxo";
34
+
35
+ // needed because TS can not infer types
36
+ type BCHMethods = {
37
+ stripPrefix: (address: string) => string;
38
+ stripToCashAddress: (address: string) => string;
39
+ validateAddress: (address: string, chain?: UTXOChain) => boolean;
40
+ createKeysForPath: (params: {
41
+ wif?: string;
42
+ phrase?: string;
43
+ derivationPath?: string;
44
+ }) => Promise<{ getAddress: (index?: number) => string }>;
45
+ getAddressFromKeys: (keys: { getAddress: (index?: number) => string }) => string;
46
+ buildBCHTx: (
47
+ params: UTXOBuildTxParams,
48
+ ) => Promise<{ builder: TransactionBuilderType; utxos: UTXOType[] }>;
49
+ buildTx: (params: UTXOBuildTxParams) => Promise<{ psbt: Psbt }>;
50
+ transfer: (
51
+ params: UTXOWalletTransferParams<
52
+ { builder: TransactionBuilderType; utxos: UTXOType[] },
53
+ TransactionType
54
+ >,
55
+ ) => Promise<string>;
56
+ };
57
+
58
+ const chain = Chain.BitcoinCash as UTXOChain;
59
+
60
+ export const stripToCashAddress = (address: string) => stripPrefix(toCashAddress(address));
61
+
62
+ const buildBCHTx: BCHMethods["buildBCHTx"] = async ({
63
+ assetValue,
64
+ recipient,
65
+ memo,
66
+ feeRate,
67
+ sender,
68
+ }) => {
69
+ if (!validateAddress(recipient)) throw new Error("Invalid address");
70
+ const utxos = await getUtxoApi(chain).scanUTXOs({
71
+ address: stripToCashAddress(sender),
72
+ fetchTxHex: true,
73
+ });
74
+
75
+ const compiledMemo = memo ? compileMemo(memo) : null;
76
+
77
+ const targetOutputs: TargetOutput[] = [];
78
+ // output to recipient
79
+ targetOutputs.push({ address: recipient, value: assetValue.getBaseValue("number") });
80
+ const { inputs, outputs } = accumulative({
81
+ inputs: utxos,
82
+ outputs: targetOutputs,
83
+ feeRate,
84
+ chain,
85
+ });
86
+
87
+ // .inputs and .outputs will be undefined if no solution was found
88
+ if (!(inputs && outputs)) throw new Error("Balance insufficient for transaction");
89
+
90
+ const builder = new TransactionBuilder(getNetwork(chain));
91
+
92
+ await Promise.all(
93
+ inputs.map(async (utxo: UTXOType) => {
94
+ const txHex = await getUtxoApi(chain).getRawTx(utxo.hash);
95
+
96
+ builder.addInput(Transaction.fromBuffer(Buffer.from(txHex, "hex")), utxo.index);
97
+ }),
98
+ );
99
+
100
+ for (const output of outputs) {
101
+ const address =
102
+ "address" in output && output.address ? output.address : toLegacyAddress(sender);
103
+ const outputScript = bchAddress.toOutputScript(toLegacyAddress(address), getNetwork(chain));
104
+
105
+ builder.addOutput(outputScript, output.value);
106
+ }
107
+
108
+ // add output for memo
109
+ if (compiledMemo) {
110
+ builder.addOutput(compiledMemo, 0); // Add OP_RETURN {script, value}
111
+ }
112
+
113
+ return { builder, utxos: inputs };
114
+ };
115
+
116
+ const transfer = async ({
117
+ signTransaction,
118
+ from,
119
+ recipient,
120
+ assetValue,
121
+ broadcastTx,
122
+ getFeeRates,
123
+ ...rest
124
+ }: UTXOWalletTransferParams<
125
+ { builder: TransactionBuilderType; utxos: UTXOType[] },
126
+ TransactionType
127
+ > & {
128
+ broadcastTx: (txHash: string) => Promise<string>;
129
+ getFeeRates: () => Promise<Record<FeeOption, number>>;
130
+ }) => {
131
+ if (!from) throw new Error("From address must be provided");
132
+ if (!recipient) throw new Error("Recipient address must be provided");
133
+ if (!signTransaction) throw new Error("signTransaction must be provided");
134
+
135
+ const feeRate = rest.feeRate || (await getFeeRates())[FeeOption.Fast];
136
+
137
+ // try out if psbt tx is faster/better/nicer
138
+ const { builder, utxos } = await buildBCHTx({
139
+ ...rest,
140
+ assetValue,
141
+ feeRate,
142
+ recipient,
143
+ sender: from,
144
+ });
145
+
146
+ const tx = await signTransaction({ builder, utxos });
147
+ const txHex = tx.toHex();
148
+
149
+ return broadcastTx(txHex);
150
+ };
151
+
152
+ const buildTx = async ({
153
+ assetValue,
154
+ recipient,
155
+ memo,
156
+ feeRate,
157
+ sender,
158
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: TODO: refactor
159
+ }: UTXOBuildTxParams) => {
160
+ const recipientCashAddress = toCashAddress(recipient);
161
+ if (!validateAddress(recipientCashAddress)) throw new Error("Invalid address");
162
+
163
+ const utxos = await getUtxoApi(chain).scanUTXOs({
164
+ address: stripToCashAddress(sender),
165
+ fetchTxHex: true,
166
+ });
167
+
168
+ const feeRateWhole = Number(feeRate.toFixed(0));
169
+ const compiledMemo = memo ? compileMemo(memo) : null;
170
+
171
+ const targetOutputs = [] as TargetOutput[];
172
+
173
+ // output to recipient
174
+ targetOutputs.push({
175
+ address: toLegacyAddress(recipient),
176
+ value: assetValue.getBaseValue("number"),
177
+ });
178
+
179
+ //2. add output memo to targets (optional)
180
+ if (compiledMemo) {
181
+ targetOutputs.push({ script: compiledMemo, value: 0 });
182
+ }
183
+
184
+ const { inputs, outputs } = accumulative({
185
+ inputs: utxos,
186
+ outputs: targetOutputs,
187
+ feeRate: feeRateWhole,
188
+ chain,
189
+ });
190
+
191
+ // .inputs and .outputs will be undefined if no solution was found
192
+ if (!(inputs && outputs)) throw new Error("Balance insufficient for transaction");
193
+ const psbt = new Psbt({ network: getNetwork(chain) }); // Network-specific
194
+
195
+ for (const { hash, index, witnessUtxo } of inputs) {
196
+ psbt.addInput({ hash, index, witnessUtxo });
197
+ }
198
+
199
+ // Outputs
200
+ for (const output of outputs) {
201
+ const address =
202
+ "address" in output && output.address ? output.address : toLegacyAddress(sender);
203
+ const params = output.script
204
+ ? compiledMemo
205
+ ? { script: compiledMemo, value: 0 }
206
+ : undefined
207
+ : { address, value: output.value };
208
+
209
+ if (params) {
210
+ psbt.addOutput(params);
211
+ }
212
+ }
213
+
214
+ return { psbt, utxos, inputs: inputs as UTXOType[] };
215
+ };
216
+
217
+ export const stripPrefix = (address: string) => address.replace(/(bchtest:|bitcoincash:)/, "");
218
+
219
+ export const validateAddress = (address: string) => {
220
+ const strippedAddress = stripPrefix(address);
221
+ return (
222
+ isValidAddress(strippedAddress) && detectAddressNetwork(strippedAddress) === bchNetwork.Mainnet
223
+ );
224
+ };
225
+
226
+ const createKeysForPath: BCHMethods["createKeysForPath"] = async ({
227
+ phrase,
228
+ derivationPath = `${DerivationPath.BCH}/0`,
229
+ wif,
230
+ }) => {
231
+ const { ECPairFactory } = await import("ecpair");
232
+
233
+ const network = getNetwork(chain);
234
+
235
+ if (wif) {
236
+ return ECPairFactory(secp256k1).fromWIF(wif, network);
237
+ }
238
+ if (!phrase) throw new Error("No phrase provided");
239
+
240
+ const masterHDNode = HDNode.fromSeedBuffer(Buffer.from(mnemonicToSeedSync(phrase)), network);
241
+ const keyPair = masterHDNode.derivePath(derivationPath).keyPair;
242
+ // TODO: Figure out same pattern as in BTC
243
+ // const testWif = keyPair.toWIF();
244
+ // const k = ECPairFactory(secp256k1).fromWIF(testWif, network);
245
+ // const a = payments.p2pkh({ pubkey: k.publicKey, network });
246
+
247
+ return keyPair;
248
+ };
249
+
250
+ const getAddressFromKeys = (keys: { getAddress: (index?: number) => string }) => {
251
+ const address = keys.getAddress(0);
252
+ return stripToCashAddress(address);
253
+ };
254
+
255
+ export const createBCHToolbox = (): Omit<
256
+ ReturnType<typeof BaseUTXOToolbox>,
257
+ "getAddressFromKeys" | "transfer" | "createKeysForPath"
258
+ > &
259
+ BCHMethods => {
260
+ const { getBalance, ...toolbox } = BaseUTXOToolbox(Chain.BitcoinCash);
261
+
262
+ return {
263
+ ...toolbox,
264
+ stripPrefix,
265
+ stripToCashAddress,
266
+ validateAddress,
267
+ createKeysForPath,
268
+ getAddressFromKeys,
269
+ buildBCHTx,
270
+ buildTx,
271
+ getBalance: (address: string, _potentialScamFilter?: boolean) =>
272
+ getBalance(stripPrefix(toCashAddress(address))),
273
+ transfer: (
274
+ params: UTXOWalletTransferParams<
275
+ { builder: TransactionBuilderType; utxos: UTXOType[] },
276
+ TransactionType
277
+ >,
278
+ ) =>
279
+ transfer({ ...params, getFeeRates: toolbox.getFeeRates, broadcastTx: toolbox.broadcastTx }),
280
+ };
281
+ };
@@ -0,0 +1,37 @@
1
+ import { Chain } from "@swapkit/helpers";
2
+
3
+ import { createBCHToolbox } from "./bitcoinCash";
4
+ import { BaseUTXOToolbox } from "./utxo";
5
+
6
+ type ToolboxType = {
7
+ BCH: typeof BCHToolbox;
8
+ BTC: typeof BTCToolbox;
9
+ DOGE: typeof DOGEToolbox;
10
+ LTC: typeof LTCToolbox;
11
+ DASH: typeof DASHToolbox;
12
+ };
13
+
14
+ export const BCHToolbox = createBCHToolbox;
15
+ export const BTCToolbox = () => BaseUTXOToolbox(Chain.Bitcoin);
16
+ export const DASHToolbox = () => BaseUTXOToolbox(Chain.Dash);
17
+ export const DOGEToolbox = () => BaseUTXOToolbox(Chain.Dogecoin);
18
+ export const LTCToolbox = () => BaseUTXOToolbox(Chain.Litecoin);
19
+
20
+ export const getToolboxByChain = <T extends keyof ToolboxType>(chain: T): ToolboxType[T] => {
21
+ switch (chain) {
22
+ case Chain.BitcoinCash:
23
+ return BCHToolbox as ToolboxType[T];
24
+ case Chain.Bitcoin:
25
+ return BTCToolbox as ToolboxType[T];
26
+ case Chain.Dogecoin:
27
+ return DOGEToolbox as ToolboxType[T];
28
+ case Chain.Litecoin:
29
+ return LTCToolbox as ToolboxType[T];
30
+ case Chain.Dash:
31
+ return DASHToolbox as ToolboxType[T];
32
+ default:
33
+ throw new Error(`Chain ${chain} is not supported`);
34
+ }
35
+ };
36
+
37
+ export { stripToCashAddress, stripPrefix, validateAddress } from "./bitcoinCash";