@swapkit/toolboxes 1.0.0-beta.0 → 1.0.0-beta.10

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 (103) hide show
  1. package/dist/chunk-0f0249b1.js +3 -0
  2. package/dist/chunk-0f0249b1.js.map +10 -0
  3. package/dist/chunk-0h4xdrwz.js +4 -0
  4. package/dist/chunk-0h4xdrwz.js.map +10 -0
  5. package/dist/chunk-4yap1fvd.js +3 -0
  6. package/dist/chunk-4yap1fvd.js.map +10 -0
  7. package/dist/chunk-fjfxga2v.js +3 -0
  8. package/dist/chunk-fjfxga2v.js.map +10 -0
  9. package/dist/{chunk-tvrdndbw.js → chunk-p1kdg37m.js} +2 -2
  10. package/dist/{chunk-tvrdndbw.js.map → chunk-p1kdg37m.js.map} +1 -1
  11. package/dist/cosmos/index.cjs +2 -2
  12. package/dist/cosmos/index.cjs.map +10 -13
  13. package/dist/cosmos/index.js +2 -2
  14. package/dist/cosmos/index.js.map +10 -13
  15. package/dist/evm/index.cjs +2 -2
  16. package/dist/evm/index.cjs.map +10 -16
  17. package/dist/evm/index.js +2 -2
  18. package/dist/evm/index.js.map +10 -16
  19. package/dist/index.cjs +2 -2
  20. package/dist/index.cjs.map +4 -3
  21. package/dist/index.js +2 -2
  22. package/dist/index.js.map +4 -3
  23. package/dist/radix/index.cjs +2 -2
  24. package/dist/radix/index.cjs.map +3 -3
  25. package/dist/radix/index.js +2 -2
  26. package/dist/radix/index.js.map +3 -3
  27. package/dist/ripple/index.cjs +3 -0
  28. package/dist/ripple/index.cjs.map +10 -0
  29. package/dist/ripple/index.js +3 -0
  30. package/dist/ripple/index.js.map +10 -0
  31. package/dist/solana/index.cjs +2 -2
  32. package/dist/solana/index.cjs.map +3 -3
  33. package/dist/solana/index.js +2 -2
  34. package/dist/solana/index.js.map +3 -3
  35. package/dist/substrate/index.cjs +2 -2
  36. package/dist/substrate/index.cjs.map +5 -6
  37. package/dist/substrate/index.js +2 -2
  38. package/dist/substrate/index.js.map +5 -6
  39. package/dist/utxo/index.cjs +2 -2
  40. package/dist/utxo/index.cjs.map +9 -11
  41. package/dist/utxo/index.js +2 -2
  42. package/dist/utxo/index.js.map +9 -11
  43. package/package.json +27 -23
  44. package/src/cosmos/index.ts +2 -9
  45. package/src/cosmos/thorchainUtils/addressFormat.ts +1 -2
  46. package/src/cosmos/thorchainUtils/index.ts +1 -1
  47. package/src/cosmos/thorchainUtils/messages.ts +74 -56
  48. package/src/cosmos/thorchainUtils/registry.ts +19 -26
  49. package/src/cosmos/thorchainUtils/types/{proto/MsgCompiled.ts → MsgCompiled.ts} +1 -3
  50. package/src/cosmos/thorchainUtils/types/client-types.ts +16 -23
  51. package/src/cosmos/toolbox/cosmos.ts +334 -0
  52. package/src/cosmos/toolbox/index.ts +33 -0
  53. package/src/cosmos/toolbox/thorchain.ts +125 -137
  54. package/src/cosmos/types.ts +37 -18
  55. package/src/cosmos/util.ts +24 -74
  56. package/src/evm/__tests__/ethereum.test.ts +110 -116
  57. package/src/evm/api.ts +11 -147
  58. package/src/evm/helpers.ts +111 -83
  59. package/src/evm/index.ts +1 -17
  60. package/src/evm/toolbox/baseEVMToolbox.ts +744 -0
  61. package/src/evm/toolbox/evm.ts +69 -0
  62. package/src/evm/toolbox/index.ts +36 -0
  63. package/src/evm/toolbox/op.ts +97 -143
  64. package/src/evm/types.ts +50 -28
  65. package/src/index.ts +235 -0
  66. package/src/radix/index.ts +18 -19
  67. package/src/ripple/index.ts +199 -0
  68. package/src/solana/index.ts +11 -5
  69. package/src/solana/toolbox.ts +227 -137
  70. package/src/substrate/index.ts +2 -3
  71. package/src/substrate/{toolbox/baseSubstrateToolbox.ts → substrate.ts} +104 -72
  72. package/src/substrate/types.ts +120 -0
  73. package/src/utils.ts +27 -0
  74. package/src/utxo/helpers/api.ts +27 -23
  75. package/src/utxo/helpers/bchaddrjs.ts +21 -21
  76. package/src/utxo/helpers/index.ts +0 -1
  77. package/src/utxo/helpers/txSize.ts +3 -4
  78. package/src/utxo/index.ts +3 -7
  79. package/src/utxo/toolbox/bitcoinCash.ts +165 -155
  80. package/src/utxo/toolbox/index.ts +63 -24
  81. package/src/utxo/toolbox/utxo.ts +376 -229
  82. package/src/utxo/types.ts +26 -39
  83. package/src/cosmos/thorchainUtils/types/proto/MsgCompiled.js +0 -2806
  84. package/src/cosmos/thorchainUtils/util.ts +0 -46
  85. package/src/cosmos/toolbox/BaseCosmosToolbox.ts +0 -254
  86. package/src/cosmos/toolbox/gaia.ts +0 -39
  87. package/src/cosmos/toolbox/getToolboxByChain.ts +0 -29
  88. package/src/cosmos/toolbox/kujira.ts +0 -61
  89. package/src/evm/provider.ts +0 -6
  90. package/src/evm/toolbox/EVMToolbox.ts +0 -662
  91. package/src/evm/toolbox/arb.ts +0 -61
  92. package/src/evm/toolbox/avax.ts +0 -36
  93. package/src/evm/toolbox/base.ts +0 -42
  94. package/src/evm/toolbox/bsc.ts +0 -34
  95. package/src/evm/toolbox/eth.ts +0 -44
  96. package/src/evm/toolbox/getToolboxByChain.ts +0 -42
  97. package/src/evm/toolbox/matic.ts +0 -42
  98. package/src/radix/toolbox.ts +0 -693
  99. package/src/substrate/toolbox/index.ts +0 -40
  100. package/src/substrate/types/index.ts +0 -2
  101. package/src/substrate/types/network.ts +0 -42
  102. package/src/substrate/types/wallet.ts +0 -78
  103. package/src/utxo/helpers/utils.ts +0 -45
@@ -1,17 +1,20 @@
1
- import * as secp256k1 from "@bitcoinerlab/secp256k1";
2
- import { HDKey } from "@scure/bip32";
3
- import { mnemonicToSeedSync } from "@scure/bip39";
4
1
  import {
5
2
  AssetValue,
6
- BaseDecimal,
7
3
  Chain,
4
+ type ChainSigner,
5
+ DerivationPath,
6
+ type DerivationPathArray,
8
7
  FeeOption,
8
+ NetworkDerivationPath,
9
9
  SwapKitNumber,
10
10
  type UTXOChain,
11
+ derivationPathToString,
12
+ updateDerivationPath,
11
13
  } from "@swapkit/helpers";
12
- import { Psbt, address as btcLibAddress, initEccLib, payments } from "bitcoinjs-lib";
14
+ import type { Psbt } from "bitcoinjs-lib";
13
15
  import type { ECPairInterface } from "ecpair";
14
-
16
+ import type { UtxoToolboxParams } from ".";
17
+ import { getBalance } from "../../utils";
15
18
  import {
16
19
  UTXOScriptType,
17
20
  accumulative,
@@ -19,227 +22,249 @@ import {
19
22
  compileMemo,
20
23
  getDustThreshold,
21
24
  getInputSize,
22
- getNetwork,
23
25
  getUtxoApi,
24
- standardFeeRates,
26
+ getUtxoNetwork,
25
27
  } from "../helpers";
26
- import type { TargetOutput, UTXOBuildTxParams, UTXOType, UTXOWalletTransferParams } from "../types";
27
- import { validateAddress as validateBCHAddress } from "./bitcoinCash";
28
- import type { BCHToolbox, BTCToolbox, DASHToolbox, DOGEToolbox, LTCToolbox } from "./index";
28
+ import type {
29
+ BchECPair,
30
+ TargetOutput,
31
+ UTXOBuildTxParams,
32
+ UTXOTransferParams,
33
+ UTXOType,
34
+ } from "../types";
35
+ import { bchValidateAddress } from "./bitcoinCash";
29
36
 
30
37
  export const nonSegwitChains = [Chain.Dash, Chain.Dogecoin];
31
38
 
32
- async function createKeysForPath({
33
- phrase,
34
- wif,
35
- derivationPath,
39
+ async function addInputsAndOutputs({
40
+ inputs,
41
+ outputs,
36
42
  chain,
37
- }: { phrase?: string; wif?: string; derivationPath: string; chain: Chain }) {
38
- const { ECPairFactory } = await import("ecpair");
39
- if (!(wif || phrase)) throw new Error("Either phrase or wif must be provided");
43
+ psbt,
44
+ sender,
45
+ compiledMemo,
46
+ }: {
47
+ inputs: UTXOType[];
48
+ outputs: TargetOutput[];
49
+ chain: UTXOChain;
50
+ psbt: Psbt;
51
+ sender: string;
52
+ compiledMemo: Buffer<ArrayBufferLike> | null;
53
+ }) {
54
+ for (const utxo of inputs) {
55
+ const witnessInfo = !!utxo.witnessUtxo &&
56
+ !nonSegwitChains.includes(chain) && { witnessUtxo: utxo.witnessUtxo };
57
+
58
+ const nonWitnessInfo = nonSegwitChains.includes(chain) && {
59
+ nonWitnessUtxo: utxo.txHex ? Buffer.from(utxo.txHex, "hex") : undefined,
60
+ };
40
61
 
41
- const factory = ECPairFactory(secp256k1);
42
- const network = getNetwork(chain);
62
+ psbt.addInput({ hash: utxo.hash, index: utxo.index, ...witnessInfo, ...nonWitnessInfo });
63
+ }
43
64
 
44
- if (wif) return factory.fromWIF(wif, network);
65
+ const { initEccLib } = (await import("bitcoinjs-lib")).default;
66
+ const secp256k1 = (await import("@bitcoinerlab/secp256k1")).default;
45
67
 
46
- const seed = mnemonicToSeedSync(phrase as string);
47
- const master = HDKey.fromMasterSeed(seed, network).derive(derivationPath);
48
- if (!master.privateKey) throw new Error("Could not get private key from phrase");
68
+ for (const output of outputs) {
69
+ const address = "address" in output && output.address ? output.address : sender;
70
+ const hasOutputScript = output.script;
49
71
 
50
- return factory.fromPrivateKey(Buffer.from(master.privateKey), { network });
51
- }
72
+ if (hasOutputScript && !compiledMemo) {
73
+ continue;
74
+ }
75
+
76
+ const mappedOutput = hasOutputScript
77
+ ? {
78
+ script: compiledMemo as Buffer<ArrayBufferLike>,
79
+ value: 0,
80
+ }
81
+ : {
82
+ address,
83
+ value: output.value,
84
+ };
52
85
 
53
- function validateAddress({ address, chain }: { address: string; chain: UTXOChain }) {
54
- try {
55
86
  initEccLib(secp256k1);
56
- btcLibAddress.toOutputScript(address, getNetwork(chain));
57
- return true;
58
- } catch (_error) {
59
- return false;
87
+ psbt.addOutput(mappedOutput);
60
88
  }
61
- }
62
89
 
63
- function getAddressFromKeys({ keys, chain }: { chain: UTXOChain; keys: ECPairInterface }) {
64
- if (!keys) throw new Error("Keys must be provided");
65
-
66
- const method = nonSegwitChains.includes(chain) ? payments.p2pkh : payments.p2wpkh;
67
- const { address } = method({ pubkey: keys.publicKey, network: getNetwork(chain) });
68
- if (!address) throw new Error("Address not defined");
69
-
70
- return address;
90
+ return { psbt, inputs };
71
91
  }
72
92
 
73
- function transfer(chain: UTXOChain) {
74
- return async function transfer({
75
- signTransaction,
76
- from,
77
- memo,
78
- recipient,
79
- feeOptionKey,
80
- broadcastTx,
81
- feeRate,
93
+ async function createTransaction({
94
+ assetValue,
95
+ recipient,
96
+ memo,
97
+ feeRate,
98
+ sender,
99
+ fetchTxHex = false,
100
+ }: UTXOBuildTxParams): Promise<{
101
+ psbt: Psbt;
102
+ utxos: UTXOType[];
103
+ inputs: UTXOType[];
104
+ }> {
105
+ const chain = assetValue.chain as UTXOChain;
106
+
107
+ const { Psbt } = (await import("bitcoinjs-lib")).default;
108
+ const compiledMemo = memo ? await compileMemo(memo) : null;
109
+
110
+ const inputsAndOutputs = await getInputsAndTargetOutputs({
82
111
  assetValue,
83
- }: UTXOWalletTransferParams<Psbt, Psbt>) {
84
- if (!from) throw new Error("From address must be provided");
85
- if (!recipient) throw new Error("Recipient address must be provided");
86
- if (!signTransaction) throw new Error("Sign transaction must be provided");
87
- const txFeeRate = feeRate || (await getFeeRates(chain))[feeOptionKey || FeeOption.Fast];
112
+ recipient,
113
+ memo,
114
+ sender,
115
+ fetchTxHex,
116
+ });
88
117
 
89
- const { psbt } = await buildTx(chain)({
90
- recipient,
91
- feeRate: txFeeRate,
92
- sender: from,
93
- fetchTxHex: nonSegwitChains.includes(chain),
94
- assetValue,
95
- memo,
96
- });
97
- const signedPsbt = await signTransaction(psbt);
98
- signedPsbt.finalizeAllInputs(); // Finalise inputs
99
- // TX extracted and formatted to hex
100
- return broadcastTx(signedPsbt.extractTransaction().toHex());
101
- };
102
- }
118
+ const { inputs, outputs } = accumulative({ ...inputsAndOutputs, feeRate, chain });
103
119
 
104
- const getBalance = async ({ address, chain }: { address: string; chain: UTXOChain }) => {
105
- const baseBalance = await getUtxoApi(chain).getBalance(address);
106
- const balance = SwapKitNumber.fromBigInt(BigInt(baseBalance), BaseDecimal[chain]).getValue(
107
- "string",
108
- );
109
- const asset = AssetValue.from({ asset: `${chain}.${chain}`, value: balance });
120
+ // .inputs and .outputs will be undefined if no solution was found
121
+ if (!(inputs && outputs)) throw new Error("Insufficient Balance for transaction");
122
+ const getNetwork = await getUtxoNetwork();
123
+ const psbt = new Psbt({ network: getNetwork(chain) });
110
124
 
111
- return [asset];
112
- };
125
+ if (chain === Chain.Dogecoin) psbt.setMaximumFeeRate(650000000);
113
126
 
114
- async function getFeeRates(chain: UTXOChain) {
115
- const suggestedFeeRate = await getUtxoApi(chain).getSuggestedTxFee();
127
+ const { psbt: mappedPsbt, inputs: mappedInputs } = await addInputsAndOutputs({
128
+ inputs,
129
+ outputs,
130
+ chain,
131
+ psbt,
132
+ sender,
133
+ compiledMemo,
134
+ });
116
135
 
117
- return standardFeeRates(suggestedFeeRate);
136
+ return {
137
+ psbt: mappedPsbt,
138
+ utxos: inputsAndOutputs.inputs,
139
+ inputs: mappedInputs,
140
+ };
118
141
  }
119
142
 
120
- function getInputsAndTargetOutputs(chain: UTXOChain) {
121
- return async function getInputsAndTargetOutputs({
122
- assetValue,
123
- recipient,
124
- memo,
125
- sender,
126
- fetchTxHex = false,
127
- }: {
128
- assetValue: AssetValue;
129
- recipient: string;
130
- memo?: string;
131
- sender: string;
132
- fetchTxHex?: boolean;
133
- }) {
134
- const inputs = await getUtxoApi(chain).scanUTXOs({ address: sender, fetchTxHex });
143
+ export async function getUTXOAddressValidator() {
144
+ const secp256k1 = (await import("@bitcoinerlab/secp256k1")).default;
145
+ const { initEccLib, address: btcLibAddress } = (await import("bitcoinjs-lib")).default;
146
+ const getNetwork = await getUtxoNetwork();
135
147
 
136
- //1. add output amount and recipient to targets
137
- //2. add output memo to targets (optional)
148
+ return function validateAddress({ address, chain }: { address: string; chain: UTXOChain }) {
149
+ if (chain === Chain.BitcoinCash) {
150
+ return bchValidateAddress(address);
151
+ }
138
152
 
139
- return {
140
- inputs,
141
- outputs: [
142
- { address: recipient, value: Number(assetValue.bigIntValue) },
143
- ...(memo ? [{ address: "", script: compileMemo(memo), value: 0 }] : []),
144
- ],
145
- };
153
+ try {
154
+ initEccLib(secp256k1);
155
+ btcLibAddress.toOutputScript(address, getNetwork(chain));
156
+ return true;
157
+ } catch (_error) {
158
+ return false;
159
+ }
146
160
  };
147
161
  }
148
162
 
149
- export function buildTx(chain: UTXOChain) {
150
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: TODO: refactor
151
- return async function buildTx({
152
- assetValue,
153
- recipient,
154
- memo,
155
- feeRate,
156
- sender,
157
- fetchTxHex = false,
158
- }: UTXOBuildTxParams): Promise<{
159
- psbt: Psbt;
160
- utxos: UTXOType[];
161
- inputs: UTXOType[];
162
- }> {
163
- const compiledMemo = memo ? compileMemo(memo) : null;
164
- const getInputsAndOutputs = getInputsAndTargetOutputs(chain);
165
-
166
- const inputsAndOutputs = await getInputsAndOutputs({
167
- assetValue,
168
- recipient,
169
- memo,
170
- sender,
171
- fetchTxHex,
172
- });
163
+ async function createSignerWithKeys({
164
+ chain,
165
+ phrase,
166
+ derivationPath,
167
+ }: { chain: UTXOChain; phrase: string; derivationPath: string }) {
168
+ const keyPair = (await getCreateKeysForPath(chain as Chain.Bitcoin))({ phrase, derivationPath });
173
169
 
174
- const { inputs, outputs } = accumulative({ ...inputsAndOutputs, feeRate, chain });
170
+ async function signTransaction(psbt: Psbt) {
171
+ await psbt.signAllInputs(keyPair);
172
+ return psbt;
173
+ }
175
174
 
176
- // .inputs and .outputs will be undefined if no solution was found
177
- if (!(inputs && outputs)) throw new Error("Insufficient Balance for transaction");
178
- const psbt = new Psbt({ network: getNetwork(chain) });
175
+ async function getAddress() {
176
+ const addressGetter = await addressFromKeysGetter(chain);
177
+ return addressGetter(keyPair);
178
+ }
179
179
 
180
- if (chain === Chain.Dogecoin) psbt.setMaximumFeeRate(650000000);
180
+ return {
181
+ getAddress,
182
+ signTransaction,
183
+ };
184
+ }
181
185
 
182
- for (const utxo of inputs) {
183
- psbt.addInput({
184
- hash: utxo.hash,
185
- index: utxo.index,
186
- ...(!!utxo.witnessUtxo &&
187
- !nonSegwitChains.includes(chain) && { witnessUtxo: utxo.witnessUtxo }),
188
- ...(nonSegwitChains.includes(chain) && {
189
- nonWitnessUtxo: utxo.txHex ? Buffer.from(utxo.txHex, "hex") : undefined,
190
- }),
191
- });
186
+ export async function createUTXOToolbox<T extends UTXOChain>({
187
+ chain,
188
+ ...toolboxParams
189
+ }: (
190
+ | UtxoToolboxParams[T]
191
+ | {
192
+ phrase?: string;
193
+ derivationPath?: DerivationPathArray;
194
+ index?: number;
192
195
  }
196
+ ) & { chain: T }) {
197
+ const phrase = "phrase" in toolboxParams ? toolboxParams.phrase : undefined;
193
198
 
194
- for (const output of outputs) {
195
- const address = "address" in output && output.address ? output.address : sender;
196
- const params = output.script
197
- ? compiledMemo
198
- ? { script: compiledMemo, value: 0 }
199
- : undefined
200
- : { address, value: output.value };
201
-
202
- if (params) {
203
- initEccLib(secp256k1);
204
- psbt.addOutput(params);
205
- }
206
- }
199
+ const index = "index" in toolboxParams ? toolboxParams.index || 0 : 0;
200
+
201
+ const derivationPath = derivationPathToString(
202
+ "derivationPath" in toolboxParams && toolboxParams.derivationPath
203
+ ? toolboxParams.derivationPath
204
+ : updateDerivationPath(NetworkDerivationPath[chain], { index }),
205
+ );
206
+
207
+ const signer = phrase
208
+ ? await createSignerWithKeys({ chain, phrase, derivationPath })
209
+ : "signer" in toolboxParams
210
+ ? toolboxParams.signer
211
+ : undefined;
207
212
 
208
- return { psbt, utxos: inputsAndOutputs.inputs, inputs };
213
+ function getAddress() {
214
+ return Promise.resolve(signer?.getAddress());
215
+ }
216
+
217
+ // const { signer } = params || {};
218
+ const getAddressFromKeys = await addressFromKeysGetter(chain);
219
+ const validateAddress = await getUTXOAddressValidator();
220
+ const createKeysForPath = await getCreateKeysForPath(chain);
221
+
222
+ return {
223
+ accumulative,
224
+ calculateTxSize,
225
+ getAddressFromKeys,
226
+ getAddress,
227
+ validateAddress: (address: string) => validateAddress({ address, chain }),
228
+ broadcastTx: (txHash: string) => getUtxoApi(chain).broadcastTx(txHash),
229
+ createTransaction,
230
+ createKeysForPath,
231
+ getFeeRates: () => getFeeRates(chain),
232
+ getInputsOutputsFee,
233
+ transfer: transfer(signer as UtxoToolboxParams["BTC"]["signer"]),
234
+ getPrivateKeyFromMnemonic: (params: { phrase: string; derivationPath: string }) => {
235
+ const keys = createKeysForPath(params);
236
+ return keys.toWIF();
237
+ },
238
+
239
+ getBalance: getBalance(chain),
240
+ estimateTransactionFee: estimateTransactionFee(chain),
241
+ estimateMaxSendableAmount: estimateMaxSendableAmount(chain),
209
242
  };
210
243
  }
211
244
 
212
- function getInputsOutputsFee(chain: UTXOChain) {
213
- return async function getInputsOutputsFee({
245
+ async function getInputsOutputsFee({
246
+ assetValue,
247
+ feeOptionKey = FeeOption.Fast,
248
+ feeRate,
249
+ memo,
250
+ sender,
251
+ recipient,
252
+ }: Omit<UTXOBuildTxParams, "feeRate"> & {
253
+ feeOptionKey?: FeeOption;
254
+ feeRate?: number;
255
+ }) {
256
+ const chain = assetValue.chain as UTXOChain;
257
+
258
+ const inputsAndOutputs = await getInputsAndTargetOutputs({
214
259
  assetValue,
215
- feeOptionKey = FeeOption.Fast,
216
- feeRate,
217
- fetchTxHex = false,
260
+ sender,
218
261
  memo,
219
262
  recipient,
220
- from,
221
- }: {
222
- assetValue: AssetValue;
223
- recipient: string;
224
- memo?: string;
225
- from: string;
226
- feeOptionKey?: FeeOption;
227
- feeRate?: number;
228
- fetchTxHex?: boolean;
229
- }) {
230
- const getInputsAndOutputs = getInputsAndTargetOutputs(chain);
231
- const inputsAndOutputs = await getInputsAndOutputs({
232
- assetValue,
233
- recipient,
234
- memo,
235
- sender: from,
236
- fetchTxHex,
237
- });
263
+ });
238
264
 
239
- const feeRateWhole = feeRate ? Math.floor(feeRate) : (await getFeeRates(chain))[feeOptionKey];
265
+ const feeRateWhole = feeRate ? Math.floor(feeRate) : (await getFeeRates(chain))[feeOptionKey];
240
266
 
241
- return accumulative({ ...inputsAndOutputs, feeRate: feeRateWhole, chain });
242
- };
267
+ return accumulative({ ...inputsAndOutputs, feeRate: feeRateWhole, chain });
243
268
  }
244
269
 
245
270
  function estimateMaxSendableAmount(chain: UTXOChain) {
@@ -286,8 +311,8 @@ function estimateMaxSendableAmount(chain: UTXOChain) {
286
311
  : recipients;
287
312
 
288
313
  if (memo) {
289
- const compiledMemo = compileMemo(memo);
290
- outputs.push({ address: from, script: compiledMemo, value: 0 });
314
+ const script = await compileMemo(memo);
315
+ outputs.push({ address: from, script, value: 0 });
291
316
  }
292
317
 
293
318
  const txSize = calculateTxSize({ inputs, outputs, feeRate: feeRateWhole });
@@ -298,63 +323,185 @@ function estimateMaxSendableAmount(chain: UTXOChain) {
298
323
  };
299
324
  }
300
325
 
301
- export const BaseUTXOToolbox = (chain: UTXOChain) => ({
302
- accumulative,
303
- calculateTxSize,
304
- getFeeRates: () => getFeeRates(chain),
305
- buildTx: buildTx(chain),
306
- transfer: transfer(chain),
307
- getInputsOutputsFee: getInputsOutputsFee(chain),
308
-
309
- broadcastTx: (txHash: string) => getUtxoApi(chain).broadcastTx(txHash),
310
- getAddressFromKeys: (keys: ECPairInterface) => getAddressFromKeys({ keys, chain }),
311
- validateAddress: (address: string) => validateAddress({ address, chain }),
312
- createKeysForPath: (params: any) => createKeysForPath({ ...params, chain }),
313
-
314
- getPrivateKeyFromMnemonic: async (params: {
315
- phrase: string;
316
- derivationPath: string;
317
- }) => {
318
- const keys = await createKeysForPath({ ...params, chain });
319
- return keys.toWIF();
320
- },
321
-
322
- getBalance: async (address: string, _potentialScamFilter?: boolean) =>
323
- getBalance({ address, chain }),
324
-
325
- estimateTransactionFee: async (params: {
326
+ function estimateTransactionFee(chain: UTXOChain) {
327
+ return async (params: {
326
328
  assetValue: AssetValue;
327
329
  recipient: string;
328
- from: string;
330
+ sender: string;
329
331
  memo?: string;
330
332
  feeOptionKey?: FeeOption;
331
333
  feeRate?: number;
332
334
  fetchTxHex?: boolean;
333
335
  }) => {
334
- const getInputsFee = getInputsOutputsFee(chain);
335
- const inputFees = await getInputsFee(params);
336
+ const inputFees = await getInputsOutputsFee(params);
336
337
 
337
338
  return AssetValue.from({
338
339
  chain,
339
340
  value: SwapKitNumber.fromBigInt(BigInt(inputFees.fee), 8).getValue("string"),
340
341
  });
341
- },
342
+ };
343
+ }
342
344
 
343
- estimateMaxSendableAmount: async (params: any) => estimateMaxSendableAmount({ ...params, chain }),
344
- });
345
+ type CreateKeysForPathReturnType = {
346
+ [Chain.BitcoinCash]: BchECPair;
347
+ [Chain.Bitcoin]: ECPairInterface;
348
+ [Chain.Dash]: ECPairInterface;
349
+ [Chain.Dogecoin]: ECPairInterface;
350
+ [Chain.Litecoin]: ECPairInterface;
351
+ };
345
352
 
346
- export function utxoValidateAddress({ chain, address }: { chain: UTXOChain; address: string }) {
347
- return chain === Chain.BitcoinCash
348
- ? validateBCHAddress(address)
349
- : validateAddress({ address, chain });
353
+ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathReturnType>(
354
+ chain: T,
355
+ ): Promise<
356
+ (params: {
357
+ wif?: string;
358
+ phrase?: string;
359
+ derivationPath?: string;
360
+ }) => CreateKeysForPathReturnType[T]
361
+ > {
362
+ const { ECPairFactory } = await import("ecpair");
363
+ const secp256k1 = (await import("@bitcoinerlab/secp256k1")).default;
364
+ const { HDKey } = await import("@scure/bip32");
365
+ const { mnemonicToSeedSync } = await import("@scure/bip39");
366
+ const getNetwork = await getUtxoNetwork();
367
+ // @ts-ignore
368
+ const { HDNode, ECPair } = await import("@psf/bitcoincashjs-lib");
369
+
370
+ switch (chain) {
371
+ case Chain.BitcoinCash: {
372
+ return function createKeysForPath({
373
+ phrase,
374
+ derivationPath = `${DerivationPath.BCH}/0`,
375
+ wif,
376
+ }: { wif?: string; phrase?: string; derivationPath?: string }) {
377
+ const network = getNetwork(chain);
378
+
379
+ if (wif) {
380
+ return ECPair.fromWIF(wif, network) as BchECPair;
381
+ }
382
+ if (!phrase) throw new Error("No phrase provided");
383
+
384
+ const masterHDNode = HDNode.fromSeedBuffer(
385
+ Buffer.from(mnemonicToSeedSync(phrase)),
386
+ network,
387
+ );
388
+ const keyPair = masterHDNode.derivePath(derivationPath).keyPair;
389
+
390
+ return keyPair as BchECPair;
391
+ } as (params: {
392
+ wif?: string;
393
+ phrase?: string;
394
+ derivationPath?: string;
395
+ }) => CreateKeysForPathReturnType[T];
396
+ }
397
+ case Chain.Bitcoin:
398
+ case Chain.Dogecoin:
399
+ case Chain.Litecoin:
400
+ case Chain.Dash: {
401
+ return function createKeysForPath({
402
+ phrase,
403
+ wif,
404
+ derivationPath,
405
+ }: { phrase?: string; wif?: string; derivationPath: string }) {
406
+ if (!(wif || phrase)) throw new Error("Either phrase or wif must be provided");
407
+
408
+ const factory = ECPairFactory(secp256k1);
409
+ const network = getNetwork(chain);
410
+
411
+ if (wif) return factory.fromWIF(wif, network);
412
+
413
+ const seed = mnemonicToSeedSync(phrase as string);
414
+ const master = HDKey.fromMasterSeed(seed, network).derive(derivationPath);
415
+ if (!master.privateKey) throw new Error("Could not get private key from phrase");
416
+
417
+ return factory.fromPrivateKey(Buffer.from(master.privateKey), { network });
418
+ } as (params: {
419
+ wif?: string;
420
+ phrase?: string;
421
+ derivationPath?: string;
422
+ }) => CreateKeysForPathReturnType[T];
423
+ }
424
+ default:
425
+ throw new Error(`Chain ${chain} is not supported`);
426
+ }
350
427
  }
351
428
 
352
- export type BaseUTXOWallet = ReturnType<typeof BaseUTXOToolbox>;
353
- type UTXOWalletType = {
354
- [Chain.Bitcoin]: ReturnType<typeof BTCToolbox>;
355
- [Chain.BitcoinCash]: ReturnType<typeof BCHToolbox>;
356
- [Chain.Dogecoin]: ReturnType<typeof DOGEToolbox>;
357
- [Chain.Litecoin]: ReturnType<typeof LTCToolbox>;
358
- [Chain.Dash]: ReturnType<typeof DASHToolbox>;
359
- };
360
- export type UTXOWallets = { [chain in UTXOChain]: BaseUTXOWallet & UTXOWalletType[chain] };
429
+ export async function addressFromKeysGetter(chain: UTXOChain) {
430
+ const { payments } = (await import("bitcoinjs-lib")).default;
431
+ const getNetwork = await getUtxoNetwork();
432
+
433
+ return function getAddressFromKeys(keys: ECPairInterface | BchECPair) {
434
+ if (!keys) throw new Error("Keys must be provided");
435
+
436
+ const method = nonSegwitChains.includes(chain) ? payments.p2pkh : payments.p2wpkh;
437
+ const { address } = method({ pubkey: keys.publicKey as Buffer, network: getNetwork(chain) });
438
+ if (!address) throw new Error("Address not defined");
439
+
440
+ return address;
441
+ };
442
+ }
443
+
444
+ function transfer(signer?: ChainSigner<Psbt, Psbt>) {
445
+ return async function transfer({
446
+ memo,
447
+ recipient,
448
+ feeOptionKey,
449
+ feeRate,
450
+ assetValue,
451
+ }: UTXOTransferParams) {
452
+ const from = await signer?.getAddress();
453
+
454
+ const chain = assetValue.chain as UTXOChain;
455
+
456
+ if (!(signer && from)) throw new Error("From address must be provided");
457
+ if (!recipient) throw new Error("Recipient address must be provided");
458
+ const txFeeRate = feeRate || (await getFeeRates(chain))[feeOptionKey || FeeOption.Fast];
459
+
460
+ const { psbt } = await createTransaction({
461
+ recipient,
462
+ feeRate: txFeeRate,
463
+ sender: from,
464
+ assetValue,
465
+ memo,
466
+ });
467
+ const signedPsbt = await signer.signTransaction(psbt);
468
+ signedPsbt.finalizeAllInputs(); // Finalise inputs
469
+ // TX extracted and formatted to hex
470
+ return getUtxoApi(chain).broadcastTx(signedPsbt.extractTransaction().toHex());
471
+ };
472
+ }
473
+
474
+ async function getFeeRates(chain: UTXOChain) {
475
+ const suggestedFeeRate = await getUtxoApi(chain).getSuggestedTxFee();
476
+
477
+ return {
478
+ [FeeOption.Average]: suggestedFeeRate,
479
+ [FeeOption.Fast]: suggestedFeeRate * 1.5,
480
+ [FeeOption.Fastest]: suggestedFeeRate * 2.0,
481
+ };
482
+ }
483
+
484
+ async function getInputsAndTargetOutputs({
485
+ assetValue,
486
+ recipient,
487
+ memo,
488
+ sender,
489
+ fetchTxHex: fetchTxOverwrite = false,
490
+ }: Omit<UTXOBuildTxParams, "feeRate">) {
491
+ const chain = assetValue.chain as UTXOChain;
492
+
493
+ const fetchTxHex = fetchTxOverwrite || nonSegwitChains.includes(chain);
494
+
495
+ const inputs = await getUtxoApi(chain).scanUTXOs({ address: sender, fetchTxHex });
496
+
497
+ //1. add output amount and recipient to targets
498
+ //2. add output memo to targets (optional)
499
+
500
+ return {
501
+ inputs,
502
+ outputs: [
503
+ { address: recipient, value: Number(assetValue.bigIntValue) },
504
+ ...(memo ? [{ address: "", script: await compileMemo(memo), value: 0 }] : []),
505
+ ],
506
+ };
507
+ }