@swapkit/toolboxes 0.0.0-nightly-20250304130539

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 (83) 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 +18 -0
  11. package/dist/evm/index.js +3 -0
  12. package/dist/evm/index.js.map +18 -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 +17 -0
  31. package/dist/utxo/index.js +3 -0
  32. package/dist/utxo/index.js.map +17 -0
  33. package/package.json +102 -0
  34. package/src/cosmos/index.ts +8 -0
  35. package/src/cosmos/thorchainUtils/addressFormat.ts +23 -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 +79 -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 +257 -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 +305 -0
  49. package/src/cosmos/types.ts +42 -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 +213 -0
  56. package/src/evm/index.ts +15 -0
  57. package/src/evm/toolbox/baseEVMToolbox.ts +670 -0
  58. package/src/evm/toolbox/evm.ts +89 -0
  59. package/src/evm/toolbox/getToolboxByChain.ts +37 -0
  60. package/src/evm/toolbox/op.ts +152 -0
  61. package/src/evm/types.ts +110 -0
  62. package/src/index.ts +0 -0
  63. package/src/radix/index.ts +151 -0
  64. package/src/radix/toolbox.ts +693 -0
  65. package/src/solana/index.ts +49 -0
  66. package/src/solana/toolbox.ts +271 -0
  67. package/src/substrate/index.ts +3 -0
  68. package/src/substrate/toolbox/baseSubstrateToolbox.ts +288 -0
  69. package/src/substrate/toolbox/index.ts +40 -0
  70. package/src/substrate/types/index.ts +2 -0
  71. package/src/substrate/types/network.ts +42 -0
  72. package/src/substrate/types/wallet.ts +78 -0
  73. package/src/utxo/helpers/api.ts +431 -0
  74. package/src/utxo/helpers/bchaddrjs.ts +177 -0
  75. package/src/utxo/helpers/coinselect.ts +96 -0
  76. package/src/utxo/helpers/index.ts +5 -0
  77. package/src/utxo/helpers/txSize.ts +103 -0
  78. package/src/utxo/helpers/utils.ts +48 -0
  79. package/src/utxo/index.ts +7 -0
  80. package/src/utxo/toolbox/bitcoinCash.ts +268 -0
  81. package/src/utxo/toolbox/index.ts +41 -0
  82. package/src/utxo/toolbox/utxo.ts +372 -0
  83. package/src/utxo/types.ts +51 -0
@@ -0,0 +1,78 @@
1
+ import type { Signer as InjectedSigner } from "@polkadot/api/types";
2
+ import type { ProviderInterface } from "@polkadot/rpc-provider/types";
3
+ import type { ExtDef } from "@polkadot/types/extrinsic/signedExtensions/types";
4
+ import type { KeypairType } from "@polkadot/util-crypto/types";
5
+
6
+ type This = typeof globalThis;
7
+
8
+ export type Unsubcall = () => void;
9
+
10
+ export interface InjectedAccount {
11
+ address: string;
12
+ genesisHash?: string | null;
13
+ name?: string;
14
+ type?: KeypairType;
15
+ }
16
+
17
+ export interface InjectedAccounts {
18
+ get: (anyType?: boolean) => Promise<InjectedAccount[]>;
19
+ subscribe: (cb: (accounts: InjectedAccount[]) => void | Promise<void>) => Unsubcall;
20
+ }
21
+ export interface InjectedExtensionInfo {
22
+ name: string;
23
+ version: string;
24
+ }
25
+ export interface ProviderMeta {
26
+ network: string;
27
+ node: "full" | "light";
28
+ source: string;
29
+ transport: string;
30
+ }
31
+ export interface MetadataDefBase {
32
+ chain: string;
33
+ genesisHash: string;
34
+ icon: string;
35
+ ss58Format: number;
36
+ chainType?: "substrate" | "ethereum";
37
+ }
38
+ export interface MetadataDef extends MetadataDefBase {
39
+ color?: string;
40
+ specVersion: number;
41
+ tokenDecimals: number;
42
+ tokenSymbol: string;
43
+ types: Record<string, Record<string, string> | string>;
44
+ metaCalls?: string;
45
+ userExtensions?: ExtDef;
46
+ }
47
+ export interface InjectedMetadataKnown {
48
+ genesisHash: string;
49
+ specVersion: number;
50
+ }
51
+ export interface InjectedMetadata {
52
+ get: () => Promise<InjectedMetadataKnown[]>;
53
+ provide: (definition: MetadataDef) => Promise<boolean>;
54
+ }
55
+ export type ProviderList = Record<string, ProviderMeta>;
56
+ export interface InjectedProvider extends ProviderInterface {
57
+ listProviders: () => Promise<ProviderList>;
58
+ startProvider: (key: string) => Promise<ProviderMeta>;
59
+ }
60
+ export interface InjectedProviderWithMeta {
61
+ provider: InjectedProvider;
62
+ meta: ProviderMeta;
63
+ }
64
+ export interface Injected {
65
+ accounts: InjectedAccounts;
66
+ metadata?: InjectedMetadata;
67
+ provider?: InjectedProvider;
68
+ signer: InjectedSigner;
69
+ }
70
+ export interface InjectedWindowProvider {
71
+ connect?: (origin: string) => Promise<InjectedExtension>;
72
+ enable?: (origin: string) => Promise<Injected>;
73
+ version?: string;
74
+ }
75
+ export interface InjectedWindow extends This {
76
+ injectedWeb3: Record<string, InjectedWindowProvider>;
77
+ }
78
+ export type InjectedExtension = InjectedExtensionInfo & Injected;
@@ -0,0 +1,431 @@
1
+ import { Chain, RequestClient, SKConfig, type UTXOChain, warnOnce } from "@swapkit/helpers";
2
+ import { uniqid } from "./utils";
3
+
4
+ type BlockchairParams<T> = T & { chain: Chain; apiKey?: string };
5
+ type BlockchairFetchUnspentUtxoParams = BlockchairParams<{
6
+ offset?: number;
7
+ limit?: number;
8
+ address: string;
9
+ }>;
10
+
11
+ async function broadcastUTXOTx({ chain, txHash }: { chain: Chain; txHash: string }) {
12
+ const rpcUrl = SKConfig.get("rpcUrls")[chain];
13
+ const body = JSON.stringify({
14
+ jsonrpc: "2.0",
15
+ method: "sendrawtransaction",
16
+ params: [txHash],
17
+ id: uniqid(),
18
+ });
19
+
20
+ const response = await RequestClient.post<{
21
+ id: string;
22
+ result: string;
23
+ error: { message: string; code?: number } | null;
24
+ }>(rpcUrl, { headers: { "Content-Type": "application/json" }, body });
25
+
26
+ if (response.error) {
27
+ throw new Error(`failed to broadcast a transaction: ${response.error?.message}`);
28
+ }
29
+
30
+ if (response.result.includes('"code":-26')) {
31
+ throw new Error("Invalid transaction: the transaction amount was too low");
32
+ }
33
+
34
+ return response.result;
35
+ }
36
+
37
+ function baseUrl(chain: Chain) {
38
+ return `https://api.blockchair.com/${mapChainToBlockchairChain(chain)}`;
39
+ }
40
+
41
+ function getDefaultTxFeeByChain(chain: Chain) {
42
+ switch (chain) {
43
+ case Chain.Bitcoin:
44
+ return 5;
45
+ case Chain.Dogecoin:
46
+ return 10000;
47
+ case Chain.Litecoin:
48
+ return 1;
49
+ default:
50
+ return 2;
51
+ }
52
+ }
53
+
54
+ function mapChainToBlockchairChain(chain: Chain) {
55
+ switch (chain) {
56
+ case Chain.BitcoinCash:
57
+ return "bitcoin-cash";
58
+ case Chain.Litecoin:
59
+ return "litecoin";
60
+ case Chain.Dash:
61
+ return "dash";
62
+ case Chain.Dogecoin:
63
+ return "dogecoin";
64
+ case Chain.Polkadot:
65
+ return "polkadot";
66
+ default:
67
+ return "bitcoin";
68
+ }
69
+ }
70
+
71
+ async function getSuggestedTxFee(chain: Chain) {
72
+ try {
73
+ //Use Bitgo API for fee estimation
74
+ //Refer: https://app.bitgo.com/docs/#operation/v2.tx.getfeeestimate
75
+ const { feePerKb } = await RequestClient.get<{
76
+ feePerKb: number;
77
+ cpfpFeePerKb: number;
78
+ numBlocks: number;
79
+ feeByBlockTarget: { 1: number; 3: number };
80
+ }>(`https://app.bitgo.com/api/v2/${chain.toLowerCase()}/tx/fee`);
81
+ const suggestedFee = feePerKb / 1000;
82
+
83
+ return Math.max(suggestedFee, getDefaultTxFeeByChain(chain));
84
+ } catch (_error) {
85
+ return getDefaultTxFeeByChain(chain);
86
+ }
87
+ }
88
+
89
+ async function blockchairRequest<T>(url: string, apiKey?: string): Promise<T> {
90
+ try {
91
+ const response = await RequestClient.get<BlockchairResponse<T>>(url);
92
+ if (!response || response.context.code !== 200) throw new Error(`failed to query ${url}`);
93
+
94
+ return response.data as T;
95
+ } catch (error) {
96
+ if (!apiKey) throw error;
97
+ const response = await RequestClient.get<BlockchairResponse<T>>(
98
+ `${url}${apiKey ? `&key=${apiKey}` : ""}`,
99
+ );
100
+
101
+ if (!response || response.context.code !== 200) throw new Error(`failed to query ${url}`);
102
+
103
+ return response.data as T;
104
+ }
105
+ }
106
+
107
+ async function getAddressData({ address, chain, apiKey }: BlockchairParams<{ address?: string }>) {
108
+ if (!address) throw new Error("address is required");
109
+
110
+ try {
111
+ const response = await blockchairRequest<BlockchairAddressResponse>(
112
+ `${baseUrl(chain)}/dashboards/address/${address}?transaction_details=true`,
113
+ apiKey,
114
+ );
115
+
116
+ return response[address];
117
+ } catch (_error) {
118
+ return { utxo: [], address: { balance: 0, transaction_count: 0 } };
119
+ }
120
+ }
121
+
122
+ async function getUnconfirmedBalance({
123
+ address,
124
+ chain,
125
+ apiKey,
126
+ }: BlockchairParams<{ address?: string }>) {
127
+ const response = await getAddressData({ address, chain, apiKey });
128
+
129
+ return response?.address.balance || 0;
130
+ }
131
+
132
+ async function getConfirmedBalance({
133
+ chain,
134
+ address,
135
+ apiKey,
136
+ }: BlockchairParams<{ address?: string }>) {
137
+ if (!address) throw new Error("address is required");
138
+
139
+ try {
140
+ const response = await blockchairRequest<BlockchairMultipleBalancesResponse>(
141
+ `${baseUrl(chain)}/addresses/balances?addresses=${address}`,
142
+ apiKey,
143
+ );
144
+
145
+ return response[address] || 0;
146
+ } catch (_error) {
147
+ return 0;
148
+ }
149
+ }
150
+
151
+ async function getRawTx({ chain, apiKey, txHash }: BlockchairParams<{ txHash?: string }>) {
152
+ if (!txHash) throw new Error("txHash is required");
153
+
154
+ try {
155
+ const rawTxResponse = await blockchairRequest<BlockchairRawTransactionResponse>(
156
+ `${baseUrl(chain)}/raw/transaction/${txHash}`,
157
+ apiKey,
158
+ );
159
+ return rawTxResponse?.[txHash]?.raw_transaction || "";
160
+ } catch (error) {
161
+ console.error(error);
162
+ return "";
163
+ }
164
+ }
165
+
166
+ async function fetchUnspentUtxoBatch({
167
+ chain,
168
+ address,
169
+ apiKey,
170
+ offset = 0,
171
+ limit = 100,
172
+ }: BlockchairFetchUnspentUtxoParams) {
173
+ const response = await blockchairRequest<BlockchairOutputsResponse[]>(
174
+ `${baseUrl(chain)}/outputs?q=is_spent(false),recipient(${address})&limit=${limit}&offset=${offset}`,
175
+ apiKey,
176
+ );
177
+
178
+ const txs = response
179
+ .filter(({ is_spent }) => !is_spent)
180
+ .map(({ script_hex, block_id, transaction_hash, index, value, spending_signature_hex }) => ({
181
+ hash: transaction_hash,
182
+ index,
183
+ value,
184
+ txHex: spending_signature_hex,
185
+ script_hex,
186
+ is_confirmed: block_id !== -1,
187
+ }));
188
+
189
+ return txs;
190
+ }
191
+
192
+ async function getUnspentUtxos({
193
+ chain,
194
+ address,
195
+ apiKey,
196
+ offset = 0,
197
+ limit = 100,
198
+ }: BlockchairFetchUnspentUtxoParams): Promise<Awaited<ReturnType<typeof fetchUnspentUtxoBatch>>> {
199
+ if (!address) throw new Error("address is required");
200
+
201
+ try {
202
+ const txs = await fetchUnspentUtxoBatch({ chain, address, apiKey, offset, limit });
203
+
204
+ if (txs.length <= limit) return txs;
205
+
206
+ const nextBatch = await getUnspentUtxos({
207
+ chain,
208
+ address,
209
+ apiKey,
210
+ offset: offset + limit,
211
+ limit,
212
+ });
213
+
214
+ return [...txs, ...nextBatch];
215
+ } catch (error) {
216
+ console.error(error);
217
+ return [];
218
+ }
219
+ }
220
+
221
+ async function scanUTXOs({
222
+ address,
223
+ chain,
224
+ apiKey,
225
+ fetchTxHex = true,
226
+ }: BlockchairParams<{ address: string; fetchTxHex?: boolean }>) {
227
+ const utxos = await getUnspentUtxos({ chain, address, apiKey });
228
+ const results = [];
229
+
230
+ for (const { hash, index, script_hex, value } of utxos) {
231
+ let txHex: string | undefined;
232
+ if (fetchTxHex) {
233
+ txHex = await getRawTx({ txHash: hash, chain, apiKey });
234
+ }
235
+ results.push({
236
+ address,
237
+ hash,
238
+ index,
239
+ txHex,
240
+ value,
241
+ witnessUtxo: { value, script: Buffer.from(script_hex, "hex") },
242
+ });
243
+ }
244
+ return results;
245
+ }
246
+
247
+ function utxoApi(chain: UTXOChain) {
248
+ const apiKey = SKConfig.get("apiKeys").blockchair || "";
249
+
250
+ warnOnce(!apiKey, "No Blockchair API key found. Functionality will be limited.");
251
+
252
+ return {
253
+ broadcastTx: (txHash: string) => broadcastUTXOTx({ txHash, chain }),
254
+ getConfirmedBalance: (address: string) => getConfirmedBalance({ chain, address, apiKey }),
255
+ getRawTx: (txHash: string) => getRawTx({ txHash, chain, apiKey }),
256
+ getSuggestedTxFee: () => getSuggestedTxFee(chain),
257
+ getBalance: (address: string) => getUnconfirmedBalance({ address, chain, apiKey }),
258
+ getAddressData: (address: string) => getAddressData({ address, chain, apiKey }),
259
+ scanUTXOs: (params: { address: string; fetchTxHex?: boolean }) =>
260
+ scanUTXOs({ ...params, chain, apiKey }),
261
+ };
262
+ }
263
+
264
+ /**
265
+ * "Factory" to ensure typing for custom UTXO APIs
266
+ */
267
+ export function createCustomUtxoApi(methods: ReturnType<typeof utxoApi>) {
268
+ return methods;
269
+ }
270
+
271
+ export function getUtxoApi(chain: UTXOChain) {
272
+ const customUtxoApi = SKConfig.get("apis")[chain];
273
+
274
+ if (customUtxoApi) {
275
+ warnOnce(true, "Using custom UTXO API. Be sure to implement all methods to avoid issues.");
276
+ return customUtxoApi as ReturnType<typeof utxoApi>;
277
+ }
278
+
279
+ return utxoApi(chain);
280
+ }
281
+
282
+ interface BlockchairMultipleBalancesResponse {
283
+ [key: string]: number;
284
+ }
285
+
286
+ interface BlockchairVin {
287
+ txid: string;
288
+ vout: number;
289
+ scriptSig: {
290
+ asm: string;
291
+ hex: string;
292
+ };
293
+ sequence: number;
294
+ }
295
+
296
+ interface BlockchairVout {
297
+ value: number;
298
+ n: number;
299
+ scriptPubKey: {
300
+ asm: string;
301
+ hex: string;
302
+ address: string;
303
+ type: string;
304
+ addresses: string[];
305
+ reqSigs: number;
306
+ };
307
+ }
308
+
309
+ interface BlockchairTransaction {
310
+ block_id: number;
311
+ hash: string;
312
+ time: string;
313
+ balance_change: number;
314
+ }
315
+
316
+ interface BlockchairUtxo {
317
+ block_id: number;
318
+ transaction_hash: string;
319
+ index: number;
320
+ value: number;
321
+ }
322
+
323
+ interface BlockchairAddressCoreData {
324
+ type: string;
325
+ script_hex: string;
326
+ balance: number;
327
+ balance_usd: number;
328
+ received: number;
329
+ received_usd: number;
330
+ spent: number;
331
+ spent_usd: number;
332
+ output_count: number;
333
+ unspent_output_count: number;
334
+ first_seen_receiving: string;
335
+ last_seen_receiving: string;
336
+ first_seen_spending: null | string;
337
+ last_seen_spending: null | string;
338
+ transaction_count: number;
339
+ scripthash_type: null | string;
340
+ }
341
+
342
+ interface BlockchairInputOutputCommonData {
343
+ block_id: number;
344
+ transaction_id: number;
345
+ index: number;
346
+ transaction_hash: string;
347
+ date: string;
348
+ time: string;
349
+ value: number;
350
+ value_usd: number;
351
+ recipient: string;
352
+ type: string;
353
+ script_hex: string;
354
+ is_from_coinbase: boolean;
355
+ is_spendable: boolean | null;
356
+ is_spent: boolean;
357
+ lifespan: number | null;
358
+ cdd: number | null;
359
+ }
360
+
361
+ interface BlockchairSpendingBlockData {
362
+ spending_block_id: number | null;
363
+ spending_transaction_id: number | null;
364
+ spending_index: number | null;
365
+ spending_transaction_hash: string | null;
366
+ spending_date: string | null;
367
+ spending_time: string | null;
368
+ spending_value_usd: number | null;
369
+ spending_sequence: number | null;
370
+ spending_signature_hex: string | null;
371
+ spending_witness: string | null;
372
+ }
373
+
374
+ interface BlockchairAddressResponse {
375
+ [key: string]: {
376
+ address: BlockchairAddressCoreData;
377
+ transactions: BlockchairTransaction[];
378
+ utxo: BlockchairUtxo[];
379
+ };
380
+ }
381
+
382
+ interface BlockchairOutputsResponse
383
+ extends BlockchairSpendingBlockData,
384
+ BlockchairInputOutputCommonData {}
385
+
386
+ interface BlockchairRawTransactionResponse {
387
+ [key: string]: {
388
+ raw_transaction: string;
389
+ decoded_raw_transaction: {
390
+ txid: string;
391
+ hash: string;
392
+ version: number;
393
+ size: number;
394
+ vsize: number;
395
+ weight: number;
396
+ locktime: number;
397
+ vin: BlockchairVin[];
398
+ vout: BlockchairVout[];
399
+ };
400
+ };
401
+ }
402
+
403
+ interface BlockchairResponse<T> {
404
+ data: T;
405
+ context: {
406
+ code: number;
407
+ source: string;
408
+ results: number;
409
+ state: number;
410
+ market_price_usd: number;
411
+ cache: {
412
+ live: boolean;
413
+ duration: number;
414
+ since: string;
415
+ until: string;
416
+ time: any;
417
+ };
418
+ api: {
419
+ version: string;
420
+ last_major_update: string;
421
+ next_major_update: null | string;
422
+ documentation: string;
423
+ notice: string;
424
+ };
425
+ servers: string;
426
+ time: number;
427
+ render_time: number;
428
+ full_time: number;
429
+ request_cost: number;
430
+ };
431
+ }
@@ -0,0 +1,177 @@
1
+ import base58check from "bs58check";
2
+ // @ts-expect-error
3
+ import cashaddr from "cashaddrjs";
4
+
5
+ enum Format {
6
+ Legacy = "legacy",
7
+ Bitpay = "bitpay",
8
+ Cashaddr = "cashaddr",
9
+ }
10
+ enum Network {
11
+ Mainnet = "mainnet",
12
+ Testnet = "testnet",
13
+ }
14
+ enum Type {
15
+ P2PKH = "p2pkh",
16
+ P2SH = "p2sh",
17
+ }
18
+
19
+ const VERSION_BYTE = {
20
+ [Format.Legacy]: {
21
+ [Network.Mainnet]: {
22
+ [Type.P2PKH]: 0,
23
+ [Type.P2SH]: 5,
24
+ },
25
+ [Network.Testnet]: {
26
+ [Type.P2PKH]: 111,
27
+ [Type.P2SH]: 196,
28
+ },
29
+ },
30
+ [Format.Bitpay]: {
31
+ [Network.Mainnet]: {
32
+ [Type.P2PKH]: 28,
33
+ [Type.P2SH]: 40,
34
+ },
35
+ [Network.Testnet]: {
36
+ [Type.P2PKH]: 111,
37
+ [Type.P2SH]: 196,
38
+ },
39
+ },
40
+ };
41
+
42
+ type DecodedType = {
43
+ format: Format;
44
+ network: Network;
45
+ type: Type;
46
+ hash: any;
47
+ };
48
+
49
+ function isValidAddress(input: any) {
50
+ try {
51
+ decodeAddress(input);
52
+ return true;
53
+ } catch (_error) {
54
+ return false;
55
+ }
56
+ }
57
+
58
+ function detectAddressNetwork(address: string) {
59
+ return decodeAddress(address)?.network;
60
+ }
61
+
62
+ function toLegacyAddress(address: string): string {
63
+ const decoded = decodeAddress(address);
64
+ if (decoded?.format === Format.Legacy) {
65
+ return address;
66
+ }
67
+ return encodeAsLegacy(decoded);
68
+ }
69
+
70
+ function toCashAddress(address: string): string {
71
+ const decoded = decodeAddress(address);
72
+ return encodeAsCashaddr(decoded);
73
+ }
74
+
75
+ function decodeAddress(address: string) {
76
+ try {
77
+ return decodeBase58Address(address);
78
+ } catch (_error) {
79
+ // Try to decode as cashaddr if base58 decoding fails.
80
+ }
81
+ try {
82
+ return decodeCashAddress(address);
83
+ } catch (_error) {
84
+ // Try to decode as bitpay if cashaddr decoding fails.
85
+ }
86
+ throw new Error("Received an invalid Bitcoin Cash address as input.");
87
+ }
88
+
89
+ function decodeBase58Address(address: string) {
90
+ try {
91
+ const payload = base58check.decode(address);
92
+
93
+ // BASE_58_CHECK_PAYLOAD_LENGTH
94
+ if (payload.length !== 21)
95
+ throw new Error("Received an invalid Bitcoin Cash address as input.");
96
+ const versionByte = payload[0];
97
+ const hash = Array.prototype.slice.call(payload, 1);
98
+
99
+ switch (versionByte) {
100
+ case VERSION_BYTE[Format.Legacy][Network.Mainnet][Type.P2PKH]:
101
+ return { hash, format: Format.Legacy, network: Network.Mainnet, type: Type.P2PKH };
102
+
103
+ case VERSION_BYTE[Format.Legacy][Network.Mainnet][Type.P2SH]:
104
+ return { hash, format: Format.Legacy, network: Network.Mainnet, type: Type.P2SH };
105
+
106
+ case VERSION_BYTE[Format.Legacy][Network.Testnet][Type.P2PKH]:
107
+ return { hash, format: Format.Legacy, network: Network.Testnet, type: Type.P2PKH };
108
+
109
+ case VERSION_BYTE[Format.Legacy][Network.Testnet][Type.P2SH]:
110
+ return { hash, format: Format.Legacy, network: Network.Testnet, type: Type.P2SH };
111
+
112
+ case VERSION_BYTE[Format.Bitpay][Network.Mainnet][Type.P2PKH]:
113
+ return { hash, format: Format.Bitpay, network: Network.Mainnet, type: Type.P2PKH };
114
+
115
+ case VERSION_BYTE[Format.Bitpay][Network.Mainnet][Type.P2SH]:
116
+ return { hash, format: Format.Bitpay, network: Network.Mainnet, type: Type.P2SH };
117
+
118
+ default:
119
+ throw new Error("Received an invalid Bitcoin Cash address as input.");
120
+ }
121
+ } catch (_error) {
122
+ throw new Error("Received an invalid Bitcoin Cash address as input.");
123
+ }
124
+ }
125
+
126
+ function decodeCashAddress(address: string) {
127
+ if (address.indexOf(":") !== -1) {
128
+ try {
129
+ return decodeCashAddressWithPrefix(address);
130
+ } catch (_error) {
131
+ // Try to decode as legacy if cashaddr decoding fails.
132
+ }
133
+ } else {
134
+ const prefixes = ["bitcoincash", "bchtest", "bchreg"];
135
+ for (const prefix of prefixes) {
136
+ try {
137
+ return decodeCashAddressWithPrefix(`${prefix}:${address}`);
138
+ } catch (_error) {
139
+ // Try next prefix if decoding fails.
140
+ }
141
+ }
142
+ }
143
+
144
+ throw new Error("Received an invalid Bitcoin Cash address as input.");
145
+ }
146
+
147
+ function decodeCashAddressWithPrefix(address: string): DecodedType {
148
+ try {
149
+ const { hash, prefix, type } = cashaddr.decode(address);
150
+
151
+ return {
152
+ format: Format.Cashaddr,
153
+ hash: Array.prototype.slice.call(hash, 0),
154
+ network: prefix === "bitcoincash" ? Network.Mainnet : Network.Testnet,
155
+ type: type === "P2PKH" ? Type.P2PKH : Type.P2SH,
156
+ };
157
+ } catch (_error) {
158
+ throw new Error("Received an invalid Bitcoin Cash address as input.");
159
+ }
160
+ }
161
+
162
+ function encodeAsLegacy(decoded: DecodedType) {
163
+ const versionByte = VERSION_BYTE[Format.Legacy][decoded.network][decoded.type];
164
+ const buffer = Buffer.alloc(1 + decoded.hash.length);
165
+ buffer[0] = versionByte;
166
+ buffer.set(decoded.hash, 1);
167
+ return base58check.encode(buffer);
168
+ }
169
+
170
+ function encodeAsCashaddr(decoded: DecodedType) {
171
+ const prefix = decoded.network === Network.Mainnet ? "bitcoincash" : "bchtest";
172
+ const type = decoded.type === Type.P2PKH ? "P2PKH" : "P2SH";
173
+ const hash = new Uint8Array(decoded.hash);
174
+ return cashaddr.encode(prefix, type, hash);
175
+ }
176
+
177
+ export { detectAddressNetwork, isValidAddress, Network, toCashAddress, toLegacyAddress };