@swapkit/toolboxes 1.0.0-beta.0 → 1.0.0-beta.2
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-0f0249b1.js +3 -0
- package/dist/chunk-0f0249b1.js.map +10 -0
- package/dist/chunk-0h4xdrwz.js +4 -0
- package/dist/chunk-0h4xdrwz.js.map +10 -0
- package/dist/chunk-4yap1fvd.js +3 -0
- package/dist/chunk-4yap1fvd.js.map +10 -0
- package/dist/chunk-fjfxga2v.js +3 -0
- package/dist/chunk-fjfxga2v.js.map +10 -0
- package/dist/{chunk-tvrdndbw.js → chunk-p1kdg37m.js} +2 -2
- package/dist/{chunk-tvrdndbw.js.map → chunk-p1kdg37m.js.map} +1 -1
- package/dist/cosmos/index.cjs +2 -2
- package/dist/cosmos/index.cjs.map +10 -13
- package/dist/cosmos/index.js +2 -2
- package/dist/cosmos/index.js.map +10 -13
- package/dist/evm/index.cjs +2 -2
- package/dist/evm/index.cjs.map +10 -16
- package/dist/evm/index.js +2 -2
- package/dist/evm/index.js.map +10 -16
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +4 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +4 -3
- package/dist/radix/index.cjs +2 -2
- package/dist/radix/index.cjs.map +3 -3
- package/dist/radix/index.js +2 -2
- package/dist/radix/index.js.map +3 -3
- package/dist/ripple/index.cjs +3 -0
- package/dist/ripple/index.cjs.map +10 -0
- package/dist/ripple/index.js +3 -0
- package/dist/ripple/index.js.map +10 -0
- package/dist/solana/index.cjs +2 -2
- package/dist/solana/index.cjs.map +3 -3
- package/dist/solana/index.js +2 -2
- package/dist/solana/index.js.map +3 -3
- package/dist/substrate/index.cjs +2 -2
- package/dist/substrate/index.cjs.map +5 -6
- package/dist/substrate/index.js +2 -2
- package/dist/substrate/index.js.map +5 -6
- package/dist/utxo/index.cjs +2 -2
- package/dist/utxo/index.cjs.map +9 -11
- package/dist/utxo/index.js +2 -2
- package/dist/utxo/index.js.map +9 -11
- package/package.json +30 -24
- package/src/cosmos/index.ts +2 -9
- package/src/cosmos/thorchainUtils/addressFormat.ts +1 -2
- package/src/cosmos/thorchainUtils/index.ts +1 -1
- package/src/cosmos/thorchainUtils/messages.ts +74 -56
- package/src/cosmos/thorchainUtils/registry.ts +16 -23
- package/src/cosmos/thorchainUtils/types/{proto/MsgCompiled.ts → MsgCompiled.ts} +1 -3
- package/src/cosmos/thorchainUtils/types/client-types.ts +16 -23
- package/src/cosmos/toolbox/cosmos.ts +334 -0
- package/src/cosmos/toolbox/index.ts +33 -0
- package/src/cosmos/toolbox/thorchain.ts +118 -131
- package/src/cosmos/types.ts +37 -18
- package/src/cosmos/util.ts +21 -71
- package/src/evm/__tests__/ethereum.test.ts +110 -116
- package/src/evm/api.ts +11 -147
- package/src/evm/helpers.ts +111 -83
- package/src/evm/index.ts +1 -17
- package/src/evm/toolbox/baseEVMToolbox.ts +742 -0
- package/src/evm/toolbox/evm.ts +69 -0
- package/src/evm/toolbox/index.ts +36 -0
- package/src/evm/toolbox/op.ts +97 -143
- package/src/evm/types.ts +50 -28
- package/src/index.ts +235 -0
- package/src/radix/index.ts +18 -19
- package/src/ripple/index.ts +203 -0
- package/src/solana/index.ts +11 -5
- package/src/solana/toolbox.ts +223 -133
- package/src/substrate/index.ts +2 -3
- package/src/substrate/{toolbox/baseSubstrateToolbox.ts → substrate.ts} +104 -72
- package/src/substrate/types.ts +120 -0
- package/src/utils.ts +27 -0
- package/src/utxo/helpers/api.ts +27 -23
- package/src/utxo/helpers/bchaddrjs.ts +21 -21
- package/src/utxo/helpers/index.ts +0 -1
- package/src/utxo/helpers/txSize.ts +3 -4
- package/src/utxo/index.ts +3 -7
- package/src/utxo/toolbox/bitcoinCash.ts +164 -154
- package/src/utxo/toolbox/index.ts +63 -24
- package/src/utxo/toolbox/utxo.ts +376 -229
- package/src/utxo/types.ts +24 -39
- package/src/cosmos/thorchainUtils/types/proto/MsgCompiled.js +0 -2806
- package/src/cosmos/thorchainUtils/util.ts +0 -46
- package/src/cosmos/toolbox/BaseCosmosToolbox.ts +0 -254
- package/src/cosmos/toolbox/gaia.ts +0 -39
- package/src/cosmos/toolbox/getToolboxByChain.ts +0 -29
- package/src/cosmos/toolbox/kujira.ts +0 -61
- package/src/evm/provider.ts +0 -6
- package/src/evm/toolbox/EVMToolbox.ts +0 -662
- package/src/evm/toolbox/arb.ts +0 -61
- package/src/evm/toolbox/avax.ts +0 -36
- package/src/evm/toolbox/base.ts +0 -42
- package/src/evm/toolbox/bsc.ts +0 -34
- package/src/evm/toolbox/eth.ts +0 -44
- package/src/evm/toolbox/getToolboxByChain.ts +0 -42
- package/src/evm/toolbox/matic.ts +0 -42
- package/src/radix/toolbox.ts +0 -693
- package/src/substrate/toolbox/index.ts +0 -40
- package/src/substrate/types/index.ts +0 -2
- package/src/substrate/types/network.ts +0 -42
- package/src/substrate/types/wallet.ts +0 -78
- package/src/utxo/helpers/utils.ts +0 -45
package/src/utxo/toolbox/utxo.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
26
|
+
getUtxoNetwork,
|
|
25
27
|
} from "../helpers";
|
|
26
|
-
import type {
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
derivationPath,
|
|
39
|
+
async function addInputsAndOutputs({
|
|
40
|
+
inputs,
|
|
41
|
+
outputs,
|
|
36
42
|
chain,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
62
|
+
psbt.addInput({ hash: utxo.hash, index: utxo.index, ...witnessInfo, ...nonWitnessInfo });
|
|
63
|
+
}
|
|
43
64
|
|
|
44
|
-
|
|
65
|
+
const { initEccLib } = await import("bitcoinjs-lib");
|
|
66
|
+
const secp256k1 = await import("@bitcoinerlab/secp256k1");
|
|
45
67
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
return true;
|
|
58
|
-
} catch (_error) {
|
|
59
|
-
return false;
|
|
87
|
+
psbt.addOutput(mappedOutput);
|
|
60
88
|
}
|
|
61
|
-
}
|
|
62
89
|
|
|
63
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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");
|
|
108
|
+
const compiledMemo = memo ? await compileMemo(memo) : null;
|
|
109
|
+
|
|
110
|
+
const inputsAndOutputs = await getInputsAndTargetOutputs({
|
|
82
111
|
assetValue,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
112
|
+
recipient,
|
|
113
|
+
memo,
|
|
114
|
+
sender,
|
|
115
|
+
fetchTxHex,
|
|
116
|
+
});
|
|
88
117
|
|
|
89
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
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
|
-
|
|
112
|
-
};
|
|
125
|
+
if (chain === Chain.Dogecoin) psbt.setMaximumFeeRate(650000000);
|
|
113
126
|
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
136
|
+
return {
|
|
137
|
+
psbt: mappedPsbt,
|
|
138
|
+
utxos: inputsAndOutputs.inputs,
|
|
139
|
+
inputs: mappedInputs,
|
|
140
|
+
};
|
|
118
141
|
}
|
|
119
142
|
|
|
120
|
-
function
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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");
|
|
145
|
+
const { initEccLib, address: btcLibAddress } = await import("bitcoinjs-lib");
|
|
146
|
+
const getNetwork = await getUtxoNetwork();
|
|
135
147
|
|
|
136
|
-
|
|
137
|
-
|
|
148
|
+
return function validateAddress({ address, chain }: { address: string; chain: UTXOChain }) {
|
|
149
|
+
if (chain === Chain.BitcoinCash) {
|
|
150
|
+
return bchValidateAddress(address);
|
|
151
|
+
}
|
|
138
152
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
170
|
+
async function signTransaction(psbt: Psbt) {
|
|
171
|
+
await psbt.signAllInputs(keyPair);
|
|
172
|
+
return psbt;
|
|
173
|
+
}
|
|
175
174
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
async function getAddress() {
|
|
176
|
+
const addressGetter = await addressFromKeysGetter(chain);
|
|
177
|
+
return addressGetter(keyPair);
|
|
178
|
+
}
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
return {
|
|
181
|
+
getAddress,
|
|
182
|
+
signTransaction,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
181
185
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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(
|
|
213
|
-
|
|
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
|
-
|
|
216
|
-
feeRate,
|
|
217
|
-
fetchTxHex = false,
|
|
260
|
+
sender,
|
|
218
261
|
memo,
|
|
219
262
|
recipient,
|
|
220
|
-
|
|
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
|
-
|
|
265
|
+
const feeRateWhole = feeRate ? Math.floor(feeRate) : (await getFeeRates(chain))[feeOptionKey];
|
|
240
266
|
|
|
241
|
-
|
|
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
|
|
290
|
-
outputs.push({ address: from, script
|
|
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
|
-
|
|
302
|
-
|
|
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
|
-
|
|
330
|
+
sender: string;
|
|
329
331
|
memo?: string;
|
|
330
332
|
feeOptionKey?: FeeOption;
|
|
331
333
|
feeRate?: number;
|
|
332
334
|
fetchTxHex?: boolean;
|
|
333
335
|
}) => {
|
|
334
|
-
const
|
|
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
|
-
|
|
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
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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");
|
|
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
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
429
|
+
export async function addressFromKeysGetter(chain: UTXOChain) {
|
|
430
|
+
const { payments } = await import("bitcoinjs-lib");
|
|
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
|
+
}
|