@swapkit/toolboxes 1.0.0-beta.3 → 1.0.0-beta.31
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-fjfxga2v.js → chunk-5yxc1e69.js} +1 -1
- package/dist/{chunk-fjfxga2v.js.map → chunk-5yxc1e69.js.map} +1 -1
- package/dist/chunk-6f98phv2.js +4 -0
- package/dist/{chunk-0h4xdrwz.js.map → chunk-6f98phv2.js.map} +2 -2
- package/dist/{chunk-0f0249b1.js → chunk-9bqegm61.js} +1 -1
- package/dist/{chunk-p1kdg37m.js → chunk-s47y8512.js} +2 -2
- package/dist/{chunk-p1kdg37m.js.map → chunk-s47y8512.js.map} +1 -1
- package/dist/chunk-vtd17cje.js +3 -0
- package/dist/chunk-vtd17cje.js.map +10 -0
- package/dist/chunk-zcdeg6h9.js +4 -0
- package/dist/chunk-zcdeg6h9.js.map +10 -0
- package/dist/src/cosmos/index.cjs +3 -0
- package/dist/src/cosmos/index.cjs.map +16 -0
- package/dist/src/cosmos/index.js +3 -0
- package/dist/src/cosmos/index.js.map +16 -0
- package/dist/src/evm/index.cjs +3 -0
- package/dist/src/evm/index.cjs.map +18 -0
- package/dist/src/evm/index.js +3 -0
- package/dist/src/evm/index.js.map +18 -0
- package/dist/src/index.cjs +3 -0
- package/dist/src/index.cjs.map +10 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +10 -0
- package/dist/src/near/index.cjs +3 -0
- package/dist/src/near/index.cjs.map +13 -0
- package/dist/src/near/index.js +3 -0
- package/dist/src/near/index.js.map +13 -0
- package/dist/{radix → src/radix}/index.cjs +2 -2
- package/dist/src/radix/index.cjs.map +10 -0
- package/dist/src/radix/index.js +3 -0
- package/dist/src/radix/index.js.map +10 -0
- package/dist/src/ripple/index.cjs +3 -0
- package/dist/src/ripple/index.cjs.map +10 -0
- package/dist/src/ripple/index.js +3 -0
- package/dist/src/ripple/index.js.map +10 -0
- package/dist/src/solana/index.cjs +3 -0
- package/dist/src/solana/index.cjs.map +10 -0
- package/dist/src/solana/index.js +3 -0
- package/dist/src/solana/index.js.map +10 -0
- package/dist/src/substrate/index.cjs +3 -0
- package/dist/src/substrate/index.cjs.map +12 -0
- package/dist/src/substrate/index.js +3 -0
- package/dist/src/substrate/index.js.map +12 -0
- package/dist/src/tron/index.cjs +3 -0
- package/dist/src/tron/index.cjs.map +11 -0
- package/dist/src/tron/index.js +3 -0
- package/dist/src/tron/index.js.map +11 -0
- package/dist/src/utxo/index.cjs +5 -0
- package/dist/src/utxo/index.cjs.map +17 -0
- package/dist/src/utxo/index.js +5 -0
- package/dist/src/utxo/index.js.map +17 -0
- package/package.json +49 -37
- package/src/cosmos/thorchainUtils/addressFormat.ts +4 -1
- package/src/cosmos/thorchainUtils/messages.ts +2 -2
- package/src/cosmos/thorchainUtils/registry.ts +3 -3
- package/src/cosmos/toolbox/cosmos.ts +35 -16
- package/src/cosmos/toolbox/index.ts +2 -2
- package/src/cosmos/toolbox/thorchain.ts +11 -9
- package/src/cosmos/util.ts +76 -6
- package/src/evm/__tests__/address-validation.test.ts +86 -0
- package/src/evm/__tests__/ethereum.test.ts +1 -1
- package/src/evm/helpers.ts +4 -3
- package/src/evm/toolbox/baseEVMToolbox.ts +37 -25
- package/src/evm/toolbox/index.ts +2 -2
- package/src/evm/toolbox/op.ts +21 -7
- package/src/index.ts +118 -100
- package/src/near/__tests__/core.test.ts +80 -0
- package/src/near/helpers/contractFactory.ts +22 -0
- package/src/near/helpers/core.ts +89 -0
- package/src/near/helpers/gasEstimation.ts +110 -0
- package/src/near/helpers/index.ts +5 -0
- package/src/near/helpers/nep141.ts +110 -0
- package/src/near/index.ts +24 -0
- package/src/near/toolbox.ts +498 -0
- package/src/near/types/contract.ts +48 -0
- package/src/near/types/nep141.ts +66 -0
- package/src/near/types.ts +57 -0
- package/src/radix/index.ts +8 -2
- package/src/ripple/index.ts +15 -26
- package/src/solana/toolbox.ts +73 -2
- package/src/substrate/balance.ts +92 -0
- package/src/substrate/substrate.ts +7 -5
- package/src/tron/__tests__/toolbox.test.ts +147 -0
- package/src/tron/helpers/trc20.abi.ts +40 -0
- package/src/tron/index.ts +16 -0
- package/src/tron/toolbox.ts +336 -0
- package/src/tron/types.ts +31 -0
- package/src/utxo/__tests__/zcash-integration.test.ts +114 -0
- package/src/utxo/helpers/api.ts +66 -16
- package/src/utxo/helpers/bchaddrjs.ts +8 -8
- package/src/utxo/helpers/coinselect.ts +4 -2
- package/src/utxo/helpers/txSize.ts +4 -3
- package/src/utxo/index.ts +1 -0
- package/src/utxo/toolbox/bitcoinCash.ts +22 -14
- package/src/utxo/toolbox/index.ts +16 -4
- package/src/utxo/toolbox/utxo.ts +42 -27
- package/src/utxo/toolbox/zcash.ts +208 -0
- package/src/utxo/types.ts +2 -0
- package/dist/chunk-0h4xdrwz.js +0 -4
- package/dist/cosmos/index.cjs +0 -3
- package/dist/cosmos/index.cjs.map +0 -16
- package/dist/cosmos/index.js +0 -3
- package/dist/cosmos/index.js.map +0 -16
- package/dist/evm/index.cjs +0 -3
- package/dist/evm/index.cjs.map +0 -18
- package/dist/evm/index.js +0 -3
- package/dist/evm/index.js.map +0 -18
- package/dist/index.cjs +0 -3
- package/dist/index.cjs.map +0 -10
- package/dist/index.js +0 -3
- package/dist/index.js.map +0 -10
- package/dist/radix/index.cjs.map +0 -10
- package/dist/radix/index.js +0 -3
- package/dist/radix/index.js.map +0 -10
- package/dist/ripple/index.cjs +0 -3
- package/dist/ripple/index.cjs.map +0 -10
- package/dist/ripple/index.js +0 -3
- package/dist/ripple/index.js.map +0 -10
- package/dist/solana/index.cjs +0 -3
- package/dist/solana/index.cjs.map +0 -10
- package/dist/solana/index.js +0 -3
- package/dist/solana/index.js.map +0 -10
- package/dist/substrate/index.cjs +0 -3
- package/dist/substrate/index.cjs.map +0 -11
- package/dist/substrate/index.js +0 -3
- package/dist/substrate/index.js.map +0 -11
- package/dist/utxo/index.cjs +0 -3
- package/dist/utxo/index.cjs.map +0 -16
- package/dist/utxo/index.js +0 -3
- package/dist/utxo/index.js.map +0 -16
- /package/dist/{chunk-0f0249b1.js.map → chunk-9bqegm61.js.map} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { SwapKitError } from "@swapkit/helpers";
|
|
1
2
|
import base58check from "bs58check";
|
|
2
|
-
// @ts-
|
|
3
|
+
// @ts-ignore
|
|
3
4
|
import cashaddr from "cashaddrjs";
|
|
4
5
|
|
|
5
6
|
enum Format {
|
|
@@ -83,7 +84,7 @@ function decodeAddress(address: string) {
|
|
|
83
84
|
} catch (_error) {
|
|
84
85
|
// Try to decode as bitpay if cashaddr decoding fails.
|
|
85
86
|
}
|
|
86
|
-
throw new
|
|
87
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
function decodeBase58Address(address: string) {
|
|
@@ -91,8 +92,7 @@ function decodeBase58Address(address: string) {
|
|
|
91
92
|
const payload = base58check.decode(address);
|
|
92
93
|
|
|
93
94
|
// BASE_58_CHECK_PAYLOAD_LENGTH
|
|
94
|
-
if (payload.length !== 21)
|
|
95
|
-
throw new Error("Received an invalid Bitcoin Cash address as input.");
|
|
95
|
+
if (payload.length !== 21) throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
96
96
|
const versionByte = payload[0];
|
|
97
97
|
const hash = Array.prototype.slice.call(payload, 1);
|
|
98
98
|
|
|
@@ -116,10 +116,10 @@ function decodeBase58Address(address: string) {
|
|
|
116
116
|
return { hash, format: Format.Bitpay, network: UtxoNetwork.Mainnet, type: Type.P2SH };
|
|
117
117
|
|
|
118
118
|
default:
|
|
119
|
-
throw new
|
|
119
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
120
120
|
}
|
|
121
121
|
} catch (_error) {
|
|
122
|
-
throw new
|
|
122
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -141,7 +141,7 @@ function decodeCashAddress(address: string) {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
throw new
|
|
144
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
function decodeCashAddressWithPrefix(address: string): DecodedType {
|
|
@@ -155,7 +155,7 @@ function decodeCashAddressWithPrefix(address: string): DecodedType {
|
|
|
155
155
|
type: type === "P2PKH" ? Type.P2PKH : Type.P2SH,
|
|
156
156
|
};
|
|
157
157
|
} catch (_error) {
|
|
158
|
-
throw new
|
|
158
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Chain, type UTXOChain } from "@swapkit/helpers";
|
|
1
|
+
import { Chain, SwapKitError, type UTXOChain } from "@swapkit/helpers";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
TX_OVERHEAD,
|
|
@@ -20,8 +20,10 @@ export const getDustThreshold = (chain: UTXOChain) => {
|
|
|
20
20
|
return 5500;
|
|
21
21
|
case Chain.Dogecoin:
|
|
22
22
|
return 100000;
|
|
23
|
+
case Chain.Zcash:
|
|
24
|
+
return 546;
|
|
23
25
|
default:
|
|
24
|
-
throw new
|
|
26
|
+
throw new SwapKitError("toolbox_utxo_not_supported", { chain });
|
|
25
27
|
}
|
|
26
28
|
};
|
|
27
29
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SwapKitError } from "@swapkit/helpers";
|
|
2
|
+
import { opcodes, script } from "bitcoinjs-lib";
|
|
1
3
|
import type {
|
|
2
4
|
TargetOutput,
|
|
3
5
|
UTXOCalculateTxSizeParams,
|
|
@@ -16,8 +18,7 @@ export const OP_RETURN_OVERHEAD = 1 + 8 + 1; //10
|
|
|
16
18
|
const TX_INPUT_BASE = 32 + 4 + 1 + 4; // 41
|
|
17
19
|
const TX_INPUT_PUBKEYHASH = 107;
|
|
18
20
|
|
|
19
|
-
export
|
|
20
|
-
const { script, opcodes } = await import("bitcoinjs-lib");
|
|
21
|
+
export function compileMemo(memo: string) {
|
|
21
22
|
const data = Buffer.from(memo, "utf8"); // converts MEMO to buffer
|
|
22
23
|
return script.compile([opcodes.OP_RETURN as number, data]); // Compile OP_RETURN script
|
|
23
24
|
}
|
|
@@ -60,7 +61,7 @@ export const getScriptTypeForAddress = (address: string) => {
|
|
|
60
61
|
) {
|
|
61
62
|
return UTXOScriptType.P2PKH;
|
|
62
63
|
}
|
|
63
|
-
throw new
|
|
64
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
export const calculateTxSize = ({ inputs, outputs, feeRate }: UTXOCalculateTxSizeParams) => {
|
package/src/utxo/index.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Transaction,
|
|
3
|
+
TransactionBuilder,
|
|
4
|
+
address as bchAddress,
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
} from "@psf/bitcoincashjs-lib";
|
|
1
7
|
import {
|
|
2
8
|
Chain,
|
|
3
9
|
type ChainSigner,
|
|
4
10
|
type DerivationPathArray,
|
|
5
11
|
FeeOption,
|
|
6
12
|
NetworkDerivationPath,
|
|
13
|
+
SwapKitError,
|
|
7
14
|
derivationPathToString,
|
|
8
15
|
updateDerivationPath,
|
|
9
16
|
} from "@swapkit/helpers";
|
|
17
|
+
import { Psbt } from "bitcoinjs-lib";
|
|
10
18
|
import type { UtxoToolboxParams } from ".";
|
|
11
19
|
import {
|
|
12
20
|
accumulative,
|
|
@@ -130,13 +138,8 @@ async function createTransaction({
|
|
|
130
138
|
feeRate,
|
|
131
139
|
sender,
|
|
132
140
|
}: UTXOBuildTxParams) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
TransactionBuilder,
|
|
136
|
-
address: bchAddress,
|
|
137
|
-
// @ts-ignore
|
|
138
|
-
} = await import("@psf/bitcoincashjs-lib");
|
|
139
|
-
if (!bchValidateAddress(recipient)) throw new Error("Invalid address");
|
|
141
|
+
if (!bchValidateAddress(recipient))
|
|
142
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address: recipient });
|
|
140
143
|
const utxos = await getUtxoApi(chain).scanUTXOs({
|
|
141
144
|
address: stripToCashAddress(sender),
|
|
142
145
|
fetchTxHex: true,
|
|
@@ -158,9 +161,10 @@ async function createTransaction({
|
|
|
158
161
|
});
|
|
159
162
|
|
|
160
163
|
// .inputs and .outputs will be undefined if no solution was found
|
|
161
|
-
if (!(inputs && outputs))
|
|
164
|
+
if (!(inputs && outputs))
|
|
165
|
+
throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
|
|
162
166
|
const getNetwork = await getUtxoNetwork();
|
|
163
|
-
const builder = new TransactionBuilder(getNetwork(chain));
|
|
167
|
+
const builder = new TransactionBuilder(getNetwork(chain)) as TransactionBuilderType;
|
|
164
168
|
|
|
165
169
|
await Promise.all(
|
|
166
170
|
inputs.map(async (utxo: UTXOType) => {
|
|
@@ -203,8 +207,11 @@ function transfer({
|
|
|
203
207
|
...rest
|
|
204
208
|
}: UTXOTransferParams) {
|
|
205
209
|
const from = await signer?.getAddress();
|
|
206
|
-
if (!(signer && from)) throw new
|
|
207
|
-
if (!recipient)
|
|
210
|
+
if (!(signer && from)) throw new SwapKitError("toolbox_utxo_no_signer");
|
|
211
|
+
if (!recipient)
|
|
212
|
+
throw new SwapKitError("toolbox_utxo_invalid_params", {
|
|
213
|
+
error: "Recipient address must be provided",
|
|
214
|
+
});
|
|
208
215
|
|
|
209
216
|
const feeRate = rest.feeRate || (await getFeeRates())[feeOptionKey];
|
|
210
217
|
|
|
@@ -226,9 +233,9 @@ function transfer({
|
|
|
226
233
|
|
|
227
234
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: TODO: refactor
|
|
228
235
|
async function buildTx({ assetValue, recipient, memo, feeRate, sender }: UTXOBuildTxParams) {
|
|
229
|
-
const { Psbt } = await import("bitcoinjs-lib");
|
|
230
236
|
const recipientCashAddress = toCashAddress(recipient);
|
|
231
|
-
if (!bchValidateAddress(recipientCashAddress))
|
|
237
|
+
if (!bchValidateAddress(recipientCashAddress))
|
|
238
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address: recipientCashAddress });
|
|
232
239
|
|
|
233
240
|
const utxos = await getUtxoApi(chain).scanUTXOs({
|
|
234
241
|
address: stripToCashAddress(sender),
|
|
@@ -259,7 +266,8 @@ async function buildTx({ assetValue, recipient, memo, feeRate, sender }: UTXOBui
|
|
|
259
266
|
});
|
|
260
267
|
|
|
261
268
|
// .inputs and .outputs will be undefined if no solution was found
|
|
262
|
-
if (!(inputs && outputs))
|
|
269
|
+
if (!(inputs && outputs))
|
|
270
|
+
throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
|
|
263
271
|
const getNetwork = await getUtxoNetwork();
|
|
264
272
|
const psbt = new Psbt({ network: getNetwork(chain) }); // Network-specific
|
|
265
273
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
Chain,
|
|
3
3
|
type ChainSigner,
|
|
4
4
|
type DerivationPathArray,
|
|
5
|
+
SwapKitError,
|
|
5
6
|
type UTXOChain,
|
|
6
7
|
} from "@swapkit/helpers";
|
|
7
8
|
import type { Psbt } from "bitcoinjs-lib";
|
|
@@ -9,11 +10,13 @@ import type { Psbt } from "bitcoinjs-lib";
|
|
|
9
10
|
import type { TransactionBuilderType, TransactionType, UTXOType } from "../types";
|
|
10
11
|
import { createBCHToolbox } from "./bitcoinCash";
|
|
11
12
|
import { createUTXOToolbox } from "./utxo";
|
|
13
|
+
import { createZcashToolbox } from "./zcash";
|
|
12
14
|
|
|
13
15
|
type BCHToolbox = Awaited<ReturnType<typeof createBCHToolbox>>;
|
|
14
16
|
type CommonUTXOToolbox = Awaited<
|
|
15
|
-
ReturnType<typeof createUTXOToolbox<Exclude<UTXOChain, Chain.BitcoinCash>>>
|
|
17
|
+
ReturnType<typeof createUTXOToolbox<Exclude<UTXOChain, Chain.BitcoinCash | Chain.Zcash>>>
|
|
16
18
|
>;
|
|
19
|
+
type ZcashToolbox = Awaited<ReturnType<typeof createZcashToolbox>>;
|
|
17
20
|
|
|
18
21
|
export type UTXOToolboxes = {
|
|
19
22
|
[Chain.BitcoinCash]: BCHToolbox;
|
|
@@ -21,6 +24,7 @@ export type UTXOToolboxes = {
|
|
|
21
24
|
[Chain.Dogecoin]: CommonUTXOToolbox;
|
|
22
25
|
[Chain.Litecoin]: CommonUTXOToolbox;
|
|
23
26
|
[Chain.Dash]: CommonUTXOToolbox;
|
|
27
|
+
[Chain.Zcash]: ZcashToolbox;
|
|
24
28
|
};
|
|
25
29
|
|
|
26
30
|
export type UTXOWallets = {
|
|
@@ -35,6 +39,9 @@ export type UtxoToolboxParams = {
|
|
|
35
39
|
[Chain.Dogecoin]: { signer: ChainSigner<Psbt, Psbt> };
|
|
36
40
|
[Chain.Litecoin]: { signer: ChainSigner<Psbt, Psbt> };
|
|
37
41
|
[Chain.Dash]: { signer: ChainSigner<Psbt, Psbt> };
|
|
42
|
+
[Chain.Zcash]: {
|
|
43
|
+
signer?: ChainSigner<Psbt, Psbt>;
|
|
44
|
+
};
|
|
38
45
|
};
|
|
39
46
|
|
|
40
47
|
export async function getUtxoToolbox<T extends keyof UTXOToolboxes>(
|
|
@@ -53,19 +60,24 @@ export async function getUtxoToolbox<T extends keyof UTXOToolboxes>(
|
|
|
53
60
|
return toolbox as UTXOToolboxes[T];
|
|
54
61
|
}
|
|
55
62
|
|
|
63
|
+
case Chain.Zcash: {
|
|
64
|
+
const toolbox = await createZcashToolbox(params as UtxoToolboxParams[Chain.Zcash]);
|
|
65
|
+
return toolbox as UTXOToolboxes[T];
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
case Chain.Bitcoin:
|
|
57
69
|
case Chain.Dogecoin:
|
|
58
70
|
case Chain.Litecoin:
|
|
59
71
|
case Chain.Dash: {
|
|
60
72
|
const toolbox = await createUTXOToolbox({
|
|
61
73
|
chain,
|
|
62
|
-
...(params as UtxoToolboxParams[Exclude<T, Chain.BitcoinCash>]),
|
|
74
|
+
...(params as UtxoToolboxParams[Exclude<T, Chain.BitcoinCash | Chain.Zcash>]),
|
|
63
75
|
});
|
|
64
|
-
return toolbox as UTXOToolboxes[Exclude<T, Chain.BitcoinCash>];
|
|
76
|
+
return toolbox as UTXOToolboxes[Exclude<T, Chain.BitcoinCash | Chain.Zcash>];
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
default:
|
|
68
|
-
throw new
|
|
80
|
+
throw new SwapKitError("toolbox_utxo_not_supported", { chain });
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
|
package/src/utxo/toolbox/utxo.ts
CHANGED
|
@@ -6,13 +6,16 @@ import {
|
|
|
6
6
|
type DerivationPathArray,
|
|
7
7
|
FeeOption,
|
|
8
8
|
NetworkDerivationPath,
|
|
9
|
+
SwapKitError,
|
|
9
10
|
SwapKitNumber,
|
|
10
11
|
type UTXOChain,
|
|
12
|
+
applyFeeMultiplier,
|
|
11
13
|
derivationPathToString,
|
|
12
14
|
updateDerivationPath,
|
|
13
15
|
} from "@swapkit/helpers";
|
|
14
|
-
import
|
|
16
|
+
import { Psbt, address as btcLibAddress, initEccLib, payments } from "bitcoinjs-lib";
|
|
15
17
|
import type { ECPairInterface } from "ecpair";
|
|
18
|
+
import ECPairFactory from "ecpair";
|
|
16
19
|
import type { UtxoToolboxParams } from ".";
|
|
17
20
|
import { getBalance } from "../../utils";
|
|
18
21
|
import {
|
|
@@ -34,9 +37,16 @@ import type {
|
|
|
34
37
|
} from "../types";
|
|
35
38
|
import { bchValidateAddress } from "./bitcoinCash";
|
|
36
39
|
|
|
40
|
+
import secp256k1 from "@bitcoinerlab/secp256k1";
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
import { ECPair, HDNode } from "@psf/bitcoincashjs-lib";
|
|
43
|
+
import { HDKey } from "@scure/bip32";
|
|
44
|
+
import { mnemonicToSeedSync } from "@scure/bip39";
|
|
45
|
+
import { validateZcashAddress } from "./zcash";
|
|
46
|
+
|
|
37
47
|
export const nonSegwitChains = [Chain.Dash, Chain.Dogecoin];
|
|
38
48
|
|
|
39
|
-
|
|
49
|
+
function addInputsAndOutputs({
|
|
40
50
|
inputs,
|
|
41
51
|
outputs,
|
|
42
52
|
chain,
|
|
@@ -62,9 +72,6 @@ async function addInputsAndOutputs({
|
|
|
62
72
|
psbt.addInput({ hash: utxo.hash, index: utxo.index, ...witnessInfo, ...nonWitnessInfo });
|
|
63
73
|
}
|
|
64
74
|
|
|
65
|
-
const { initEccLib } = await import("bitcoinjs-lib");
|
|
66
|
-
const secp256k1 = await import("@bitcoinerlab/secp256k1");
|
|
67
|
-
|
|
68
75
|
for (const output of outputs) {
|
|
69
76
|
const address = "address" in output && output.address ? output.address : sender;
|
|
70
77
|
const hasOutputScript = output.script;
|
|
@@ -103,8 +110,6 @@ async function createTransaction({
|
|
|
103
110
|
inputs: UTXOType[];
|
|
104
111
|
}> {
|
|
105
112
|
const chain = assetValue.chain as UTXOChain;
|
|
106
|
-
|
|
107
|
-
const { Psbt } = await import("bitcoinjs-lib");
|
|
108
113
|
const compiledMemo = memo ? await compileMemo(memo) : null;
|
|
109
114
|
|
|
110
115
|
const inputsAndOutputs = await getInputsAndTargetOutputs({
|
|
@@ -118,7 +123,8 @@ async function createTransaction({
|
|
|
118
123
|
const { inputs, outputs } = accumulative({ ...inputsAndOutputs, feeRate, chain });
|
|
119
124
|
|
|
120
125
|
// .inputs and .outputs will be undefined if no solution was found
|
|
121
|
-
if (!(inputs && outputs))
|
|
126
|
+
if (!(inputs && outputs))
|
|
127
|
+
throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
|
|
122
128
|
const getNetwork = await getUtxoNetwork();
|
|
123
129
|
const psbt = new Psbt({ network: getNetwork(chain) });
|
|
124
130
|
|
|
@@ -141,8 +147,6 @@ async function createTransaction({
|
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
export async function getUTXOAddressValidator() {
|
|
144
|
-
const secp256k1 = await import("@bitcoinerlab/secp256k1");
|
|
145
|
-
const { initEccLib, address: btcLibAddress } = await import("bitcoinjs-lib");
|
|
146
150
|
const getNetwork = await getUtxoNetwork();
|
|
147
151
|
|
|
148
152
|
return function validateAddress({ address, chain }: { address: string; chain: UTXOChain }) {
|
|
@@ -150,6 +154,10 @@ export async function getUTXOAddressValidator() {
|
|
|
150
154
|
return bchValidateAddress(address);
|
|
151
155
|
}
|
|
152
156
|
|
|
157
|
+
if (chain === Chain.Zcash) {
|
|
158
|
+
return validateZcashAddress(address);
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
try {
|
|
154
162
|
initEccLib(secp256k1);
|
|
155
163
|
btcLibAddress.toOutputScript(address, getNetwork(chain));
|
|
@@ -348,6 +356,7 @@ type CreateKeysForPathReturnType = {
|
|
|
348
356
|
[Chain.Dash]: ECPairInterface;
|
|
349
357
|
[Chain.Dogecoin]: ECPairInterface;
|
|
350
358
|
[Chain.Litecoin]: ECPairInterface;
|
|
359
|
+
[Chain.Zcash]: ECPairInterface;
|
|
351
360
|
};
|
|
352
361
|
|
|
353
362
|
export async function getCreateKeysForPath<T extends keyof CreateKeysForPathReturnType>(
|
|
@@ -359,13 +368,7 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
|
|
|
359
368
|
derivationPath?: string;
|
|
360
369
|
}) => CreateKeysForPathReturnType[T]
|
|
361
370
|
> {
|
|
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
371
|
const getNetwork = await getUtxoNetwork();
|
|
367
|
-
// @ts-ignore
|
|
368
|
-
const { HDNode, ECPair } = await import("@psf/bitcoincashjs-lib");
|
|
369
372
|
|
|
370
373
|
switch (chain) {
|
|
371
374
|
case Chain.BitcoinCash: {
|
|
@@ -379,7 +382,8 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
|
|
|
379
382
|
if (wif) {
|
|
380
383
|
return ECPair.fromWIF(wif, network) as BchECPair;
|
|
381
384
|
}
|
|
382
|
-
if (!phrase)
|
|
385
|
+
if (!phrase)
|
|
386
|
+
throw new SwapKitError("toolbox_utxo_invalid_params", { error: "No phrase provided" });
|
|
383
387
|
|
|
384
388
|
const masterHDNode = HDNode.fromSeedBuffer(
|
|
385
389
|
Buffer.from(mnemonicToSeedSync(phrase)),
|
|
@@ -397,13 +401,17 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
|
|
|
397
401
|
case Chain.Bitcoin:
|
|
398
402
|
case Chain.Dogecoin:
|
|
399
403
|
case Chain.Litecoin:
|
|
404
|
+
case Chain.Zcash:
|
|
400
405
|
case Chain.Dash: {
|
|
401
406
|
return function createKeysForPath({
|
|
402
407
|
phrase,
|
|
403
408
|
wif,
|
|
404
409
|
derivationPath,
|
|
405
410
|
}: { phrase?: string; wif?: string; derivationPath: string }) {
|
|
406
|
-
if (!(wif || phrase))
|
|
411
|
+
if (!(wif || phrase))
|
|
412
|
+
throw new SwapKitError("toolbox_utxo_invalid_params", {
|
|
413
|
+
error: "Either phrase or wif must be provided",
|
|
414
|
+
});
|
|
407
415
|
|
|
408
416
|
const factory = ECPairFactory(secp256k1);
|
|
409
417
|
const network = getNetwork(chain);
|
|
@@ -412,7 +420,10 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
|
|
|
412
420
|
|
|
413
421
|
const seed = mnemonicToSeedSync(phrase as string);
|
|
414
422
|
const master = HDKey.fromMasterSeed(seed, network).derive(derivationPath);
|
|
415
|
-
if (!master.privateKey)
|
|
423
|
+
if (!master.privateKey)
|
|
424
|
+
throw new SwapKitError("toolbox_utxo_invalid_params", {
|
|
425
|
+
error: "Could not get private key from phrase",
|
|
426
|
+
});
|
|
416
427
|
|
|
417
428
|
return factory.fromPrivateKey(Buffer.from(master.privateKey), { network });
|
|
418
429
|
} as (params: {
|
|
@@ -422,20 +433,21 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
|
|
|
422
433
|
}) => CreateKeysForPathReturnType[T];
|
|
423
434
|
}
|
|
424
435
|
default:
|
|
425
|
-
throw new
|
|
436
|
+
throw new SwapKitError("toolbox_utxo_not_supported", { chain });
|
|
426
437
|
}
|
|
427
438
|
}
|
|
428
439
|
|
|
429
440
|
export async function addressFromKeysGetter(chain: UTXOChain) {
|
|
430
|
-
const { payments } = await import("bitcoinjs-lib");
|
|
431
441
|
const getNetwork = await getUtxoNetwork();
|
|
432
442
|
|
|
433
443
|
return function getAddressFromKeys(keys: ECPairInterface | BchECPair) {
|
|
434
|
-
if (!keys)
|
|
444
|
+
if (!keys)
|
|
445
|
+
throw new SwapKitError("toolbox_utxo_invalid_params", { error: "Keys must be provided" });
|
|
435
446
|
|
|
436
447
|
const method = nonSegwitChains.includes(chain) ? payments.p2pkh : payments.p2wpkh;
|
|
437
448
|
const { address } = method({ pubkey: keys.publicKey as Buffer, network: getNetwork(chain) });
|
|
438
|
-
if (!address)
|
|
449
|
+
if (!address)
|
|
450
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { error: "Address not defined" });
|
|
439
451
|
|
|
440
452
|
return address;
|
|
441
453
|
};
|
|
@@ -453,8 +465,11 @@ function transfer(signer?: ChainSigner<Psbt, Psbt>) {
|
|
|
453
465
|
|
|
454
466
|
const chain = assetValue.chain as UTXOChain;
|
|
455
467
|
|
|
456
|
-
if (!(signer && from)) throw new
|
|
457
|
-
if (!recipient)
|
|
468
|
+
if (!(signer && from)) throw new SwapKitError("toolbox_utxo_no_signer");
|
|
469
|
+
if (!recipient)
|
|
470
|
+
throw new SwapKitError("toolbox_utxo_invalid_params", {
|
|
471
|
+
error: "Recipient address must be provided",
|
|
472
|
+
});
|
|
458
473
|
const txFeeRate = feeRate || (await getFeeRates(chain))[feeOptionKey || FeeOption.Fast];
|
|
459
474
|
|
|
460
475
|
const { psbt } = await createTransaction({
|
|
@@ -476,8 +491,8 @@ async function getFeeRates(chain: UTXOChain) {
|
|
|
476
491
|
|
|
477
492
|
return {
|
|
478
493
|
[FeeOption.Average]: suggestedFeeRate,
|
|
479
|
-
[FeeOption.Fast]: suggestedFeeRate
|
|
480
|
-
[FeeOption.Fastest]: suggestedFeeRate
|
|
494
|
+
[FeeOption.Fast]: applyFeeMultiplier(suggestedFeeRate, FeeOption.Fast),
|
|
495
|
+
[FeeOption.Fastest]: applyFeeMultiplier(suggestedFeeRate, FeeOption.Fastest),
|
|
481
496
|
};
|
|
482
497
|
}
|
|
483
498
|
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import secp256k1 from "@bitcoinerlab/secp256k1";
|
|
2
|
+
import { HDKey } from "@scure/bip32";
|
|
3
|
+
import { mnemonicToSeedSync } from "@scure/bip39";
|
|
4
|
+
import {
|
|
5
|
+
Chain,
|
|
6
|
+
type ChainSigner,
|
|
7
|
+
type DerivationPathArray,
|
|
8
|
+
FeeOption,
|
|
9
|
+
NetworkDerivationPath,
|
|
10
|
+
SKConfig,
|
|
11
|
+
SwapKitError,
|
|
12
|
+
derivationPathToString,
|
|
13
|
+
updateDerivationPath,
|
|
14
|
+
} from "@swapkit/helpers";
|
|
15
|
+
import type { Psbt } from "bitcoinjs-lib";
|
|
16
|
+
import { hash160 } from "bitcoinjs-lib/src/crypto";
|
|
17
|
+
import bs58check from "bs58check";
|
|
18
|
+
import ECPairFactory from "ecpair";
|
|
19
|
+
import { P, match } from "ts-pattern";
|
|
20
|
+
import { getUtxoNetwork } from "../helpers";
|
|
21
|
+
import type { UTXOBuildTxParams, UTXOTransferParams } from "../types";
|
|
22
|
+
import { createUTXOToolbox } from "./utxo";
|
|
23
|
+
|
|
24
|
+
const chain = Chain.Zcash;
|
|
25
|
+
const network = getUtxoNetwork()(chain);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Custom Zcash address generation that handles 2-byte prefixes
|
|
29
|
+
*
|
|
30
|
+
* Zcash transparent addresses use 2-byte version prefixes, unlike Bitcoin's single byte.
|
|
31
|
+
* This is incompatible with bitcoinjs-lib's payment methods which expect UInt8 values.
|
|
32
|
+
*
|
|
33
|
+
* @param publicKey - The public key buffer to generate address from
|
|
34
|
+
* @param isTestnet - Whether to generate testnet or mainnet address
|
|
35
|
+
* @returns A valid Zcash transparent address (t1... for mainnet, tm... for testnet)
|
|
36
|
+
*/
|
|
37
|
+
function generateZcashAddress(publicKey: Buffer, isTestnet = false): string {
|
|
38
|
+
// Hash the public key using RIPEMD160(SHA256(pubkey))
|
|
39
|
+
const publicKeyHash = hash160(publicKey);
|
|
40
|
+
|
|
41
|
+
// Zcash uses 2-byte prefixes for transparent addresses
|
|
42
|
+
// These prefixes ensure addresses start with expected characters when base58 encoded
|
|
43
|
+
const prefix = isTestnet
|
|
44
|
+
? Buffer.from([0x1c, 0xba]) // testnet prefix (results in tm... addresses)
|
|
45
|
+
: Buffer.from([0x1c, 0xb8]); // mainnet prefix (results in t1... addresses)
|
|
46
|
+
|
|
47
|
+
// Combine prefix + hash (22 bytes total: 2 byte prefix + 20 byte hash)
|
|
48
|
+
const payload = Buffer.concat([prefix, publicKeyHash]);
|
|
49
|
+
|
|
50
|
+
// Encode with base58check for final address
|
|
51
|
+
return bs58check.encode(payload);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function validateZcashAddress(address: string) {
|
|
55
|
+
try {
|
|
56
|
+
// Shielded addresses are not supported
|
|
57
|
+
if (address.startsWith("z")) {
|
|
58
|
+
console.warn(
|
|
59
|
+
"Shielded Zcash addresses (z-addresses) are not supported. Use transparent addresses (t1/t3) only.",
|
|
60
|
+
);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isMainnet = address.startsWith("t1");
|
|
65
|
+
const isTestnet = address.startsWith("t3");
|
|
66
|
+
|
|
67
|
+
if (!(isMainnet || isTestnet)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Verify network matches address type
|
|
72
|
+
const { isStagenet } = SKConfig.get("envs");
|
|
73
|
+
if ((isMainnet && isStagenet) || (isTestnet && !isStagenet)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return validateBase58Check(address, network);
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validateBase58Check(
|
|
84
|
+
address: string,
|
|
85
|
+
network: ReturnType<ReturnType<typeof getUtxoNetwork>>,
|
|
86
|
+
) {
|
|
87
|
+
try {
|
|
88
|
+
const decoded = bs58check.decode(address);
|
|
89
|
+
|
|
90
|
+
if (decoded.length < 21) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const version = decoded[0];
|
|
95
|
+
return version === network.pubKeyHash || version === network.scriptHash;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const ECPair = ECPairFactory(secp256k1);
|
|
102
|
+
|
|
103
|
+
type ZcashSigner = ChainSigner<Psbt, Psbt>;
|
|
104
|
+
|
|
105
|
+
async function createZcashSignerFromPhrase({
|
|
106
|
+
phrase,
|
|
107
|
+
derivationPathString,
|
|
108
|
+
}: {
|
|
109
|
+
phrase: string;
|
|
110
|
+
derivationPathString: string;
|
|
111
|
+
}) {
|
|
112
|
+
const seed = mnemonicToSeedSync(phrase);
|
|
113
|
+
const root = HDKey.fromMasterSeed(seed);
|
|
114
|
+
|
|
115
|
+
const node = root.derive(derivationPathString);
|
|
116
|
+
|
|
117
|
+
if (!node.privateKey) {
|
|
118
|
+
throw new Error("Unable to derive private key");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const keyPair = ECPair.fromPrivateKey(Buffer.from(node.privateKey), { network });
|
|
122
|
+
|
|
123
|
+
const { isStagenet } = SKConfig.get("envs");
|
|
124
|
+
const address = generateZcashAddress(keyPair.publicKey, isStagenet);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
getAddress() {
|
|
128
|
+
return Promise.resolve(address);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
signTransaction(psbt: Psbt) {
|
|
132
|
+
for (let i = 0; i < psbt.inputCount; i++) {
|
|
133
|
+
psbt.signInput(i, keyPair);
|
|
134
|
+
}
|
|
135
|
+
return Promise.resolve(psbt);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function createZcashToolbox(
|
|
141
|
+
toolboxParams:
|
|
142
|
+
| {
|
|
143
|
+
signer?: ZcashSigner;
|
|
144
|
+
}
|
|
145
|
+
| {
|
|
146
|
+
phrase?: string;
|
|
147
|
+
derivationPath?: DerivationPathArray;
|
|
148
|
+
index?: number;
|
|
149
|
+
},
|
|
150
|
+
) {
|
|
151
|
+
const signer = await match(toolboxParams)
|
|
152
|
+
.with({ signer: P.not(P.nullish) }, ({ signer }) => Promise.resolve(signer))
|
|
153
|
+
.with({ phrase: P.string }, ({ phrase, derivationPath, index = 0 }) => {
|
|
154
|
+
// Handle derivation path processing at toolbox level
|
|
155
|
+
const baseDerivationPath = derivationPath ||
|
|
156
|
+
NetworkDerivationPath[Chain.Zcash] || [44, 133, 0, 0, 0];
|
|
157
|
+
const updatedDerivationPath = updateDerivationPath(baseDerivationPath, { index });
|
|
158
|
+
const derivationPathString = derivationPathToString(updatedDerivationPath);
|
|
159
|
+
|
|
160
|
+
return createZcashSignerFromPhrase({ phrase, derivationPathString });
|
|
161
|
+
})
|
|
162
|
+
.otherwise(() => Promise.resolve(undefined));
|
|
163
|
+
|
|
164
|
+
const { getFeeRates, broadcastTx, ...toolbox } = await createUTXOToolbox({
|
|
165
|
+
chain: Chain.Zcash,
|
|
166
|
+
signer,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
function getAddressFromKeys(keys: { getAddress: () => string }) {
|
|
170
|
+
return keys.getAddress();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function transfer({
|
|
174
|
+
recipient,
|
|
175
|
+
assetValue,
|
|
176
|
+
feeOptionKey = FeeOption.Fast,
|
|
177
|
+
...rest
|
|
178
|
+
}: UTXOTransferParams) {
|
|
179
|
+
const from = await signer?.getAddress();
|
|
180
|
+
if (!(signer && from)) throw new SwapKitError("toolbox_utxo_no_signer");
|
|
181
|
+
|
|
182
|
+
const feeRate = rest.feeRate || (await getFeeRates())[feeOptionKey];
|
|
183
|
+
|
|
184
|
+
const buildTxParams: UTXOBuildTxParams = {
|
|
185
|
+
...rest,
|
|
186
|
+
assetValue,
|
|
187
|
+
feeRate,
|
|
188
|
+
recipient,
|
|
189
|
+
sender: from,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const { psbt } = await toolbox.createTransaction(buildTxParams);
|
|
193
|
+
const signedPsbt = await signer.signTransaction(psbt);
|
|
194
|
+
signedPsbt.finalizeAllInputs();
|
|
195
|
+
const txHex = signedPsbt.extractTransaction().toHex();
|
|
196
|
+
|
|
197
|
+
return broadcastTx(txHex);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
...toolbox,
|
|
202
|
+
broadcastTx,
|
|
203
|
+
getFeeRates,
|
|
204
|
+
transfer,
|
|
205
|
+
getAddressFromKeys,
|
|
206
|
+
validateAddress: validateZcashAddress,
|
|
207
|
+
};
|
|
208
|
+
}
|
package/src/utxo/types.ts
CHANGED
|
@@ -52,4 +52,6 @@ export type TransactionBuilderType = {
|
|
|
52
52
|
signatureAlgorithm?: string,
|
|
53
53
|
): void;
|
|
54
54
|
build(): TransactionType;
|
|
55
|
+
addOutput(addressOrScriptBuffer: string | Buffer, value: number): void;
|
|
56
|
+
addInput(txHash: string | Buffer, vout: number, sequence?: number, prevOutScript?: Buffer): void;
|
|
55
57
|
};
|
package/dist/chunk-0h4xdrwz.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{AssetValue as c,BaseDecimal as p}from"@swapkit/helpers";import{SwapKitApi as u}from"@swapkit/helpers/api";var l=typeof process!=="undefined"&&process.pid?process.pid.toString(36):"",a=0;function g(){function t(){let e=Date.now(),n=a||e;return a=n,e>a?e:n+1}return l+t().toString(36)}function w(t){return async function e(n,i=!0){return(await u.getChainBalance({chain:t,address:n,scamFilter:i})).map(({identifier:r,value:o,decimal:s})=>{return new c({decimal:s||p[t],value:o,identifier:r})})}}
|
|
2
|
-
export{g as a,w as b};
|
|
3
|
-
|
|
4
|
-
//# debugId=7D1F4061F0771C8264756E2164756E21
|