@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,49 @@
1
+ import type { PublicKey, SendOptions, Transaction, VersionedTransaction } from "@solana/web3.js";
2
+ import { Chain } from "@swapkit/helpers";
3
+ import type { SOLToolbox } from "./toolbox";
4
+
5
+ type DisplayEncoding = "utf8" | "hex";
6
+
7
+ type PhantomEvent = "connect" | "disconnect" | "accountChanged";
8
+
9
+ type PhantomRequestMethod =
10
+ | "connect"
11
+ | "disconnect"
12
+ | "signAndSendTransaction"
13
+ | "signAndSendTransactionV0"
14
+ | "signAndSendTransactionV0WithLookupTable"
15
+ | "signTransaction"
16
+ | "signAllTransactions"
17
+ | "signMessage";
18
+
19
+ interface ConnectOpts {
20
+ onlyIfTrusted: boolean;
21
+ }
22
+
23
+ export * from "./toolbox";
24
+
25
+ export type SolanaWallets = {
26
+ [Chain.Solana]: ReturnType<typeof SOLToolbox>;
27
+ };
28
+
29
+ export interface SolanaProvider {
30
+ connect: (opts?: Partial<ConnectOpts>) => Promise<{ publicKey: PublicKey }>;
31
+ disconnect: () => Promise<void>;
32
+ getAddress: () => Promise<string>;
33
+ isConnected: boolean | null;
34
+ isPhantom: boolean;
35
+ on: (event: PhantomEvent, handler: (args: any) => void) => void;
36
+ publicKey: PublicKey | null;
37
+ request: (method: PhantomRequestMethod, params: any) => Promise<unknown>;
38
+ signMessage: (message: Uint8Array | string, display?: DisplayEncoding) => Promise<any>;
39
+ signAndSendTransaction: (
40
+ transaction: Transaction | VersionedTransaction,
41
+ opts?: SendOptions,
42
+ ) => Promise<{ signature: string; publicKey: PublicKey }>;
43
+ signTransaction: <T extends Transaction | VersionedTransaction = Transaction>(
44
+ transaction: T,
45
+ ) => Promise<T>;
46
+ signAllTransactions: <T extends Transaction | VersionedTransaction = Transaction>(
47
+ transactions: T[],
48
+ ) => Promise<T[]>;
49
+ }
@@ -0,0 +1,271 @@
1
+ import type { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
2
+ import {
3
+ AssetValue,
4
+ Chain,
5
+ DerivationPath,
6
+ SKConfig,
7
+ SwapKitError,
8
+ SwapKitNumber,
9
+ type WalletTxParams,
10
+ } from "@swapkit/helpers";
11
+
12
+ export async function getAddressValidator() {
13
+ const { PublicKey } = await import("@solana/web3.js");
14
+
15
+ return (address: string) => {
16
+ try {
17
+ const pubkey = new PublicKey(address);
18
+ return PublicKey.isOnCurve(pubkey.toBytes());
19
+ } catch (_) {
20
+ return false;
21
+ }
22
+ };
23
+ }
24
+
25
+ export async function createSolanaTokenTransaction({
26
+ tokenAddress,
27
+ recipient,
28
+ from,
29
+ connection,
30
+ amount,
31
+ decimals,
32
+ }: {
33
+ tokenAddress: string;
34
+ recipient: string;
35
+ from: PublicKey;
36
+ connection: Connection;
37
+ amount: number;
38
+ decimals: number;
39
+ }) {
40
+ const {
41
+ getAssociatedTokenAddress,
42
+ getAccount,
43
+ createAssociatedTokenAccountInstruction,
44
+ createTransferCheckedInstruction,
45
+ } = await import("@solana/spl-token");
46
+ const { Transaction, PublicKey } = await import("@solana/web3.js");
47
+
48
+ const transaction = new Transaction();
49
+ const tokenPublicKey = new PublicKey(tokenAddress);
50
+ const fromSPLAddress = await getAssociatedTokenAddress(tokenPublicKey, from);
51
+
52
+ const recipientPublicKey = new PublicKey(recipient);
53
+ const recipientSPLAddress = await getAssociatedTokenAddress(tokenPublicKey, recipientPublicKey);
54
+
55
+ let recipientAccountExists = false;
56
+ try {
57
+ await getAccount(connection, recipientSPLAddress);
58
+ recipientAccountExists = true;
59
+ } catch (_) {
60
+ // Recipient's associated token account doesn't exist
61
+ }
62
+
63
+ if (!recipientAccountExists) {
64
+ transaction.add(
65
+ createAssociatedTokenAccountInstruction(
66
+ from,
67
+ recipientSPLAddress,
68
+ recipientPublicKey,
69
+ tokenPublicKey,
70
+ ),
71
+ );
72
+ }
73
+
74
+ transaction.add(
75
+ createTransferCheckedInstruction(
76
+ fromSPLAddress,
77
+ tokenPublicKey,
78
+ recipientSPLAddress,
79
+ from,
80
+ amount,
81
+ decimals,
82
+ ),
83
+ );
84
+
85
+ return transaction;
86
+ }
87
+
88
+ export const SOLToolbox = () => {
89
+ async function getConnection() {
90
+ const { Connection } = await import("@solana/web3.js");
91
+ return new Connection(SKConfig.get("rpcUrls").SOL, "confirmed");
92
+ }
93
+
94
+ return {
95
+ getConnection,
96
+ createKeysForPath,
97
+ getAddressFromKeys,
98
+ createSolanaTransaction: createSolanaTransaction(getConnection),
99
+ getBalance: getBalance(getConnection),
100
+ transfer: transfer(getConnection),
101
+ broadcastTransaction: broadcastTransaction(getConnection),
102
+ getAddressValidator,
103
+ };
104
+ };
105
+
106
+ function createSolanaTransaction(getConnection: () => Promise<Connection>) {
107
+ return async ({
108
+ recipient,
109
+ assetValue,
110
+ fromPublicKey,
111
+ memo,
112
+ isProgramDerivedAddress,
113
+ }: WalletTxParams & {
114
+ assetValue: AssetValue;
115
+ fromPublicKey: PublicKey;
116
+ isProgramDerivedAddress?: boolean;
117
+ }) => {
118
+ const { createMemoInstruction } = await import("@solana/spl-memo");
119
+ const { Transaction, PublicKey, SystemProgram } = await import("@solana/web3.js");
120
+ const validateAddress = await getAddressValidator();
121
+
122
+ if (!(isProgramDerivedAddress || validateAddress(recipient))) {
123
+ throw new SwapKitError("core_transaction_invalid_recipient_address");
124
+ }
125
+
126
+ const connection = await getConnection();
127
+
128
+ const transaction = assetValue.isGasAsset
129
+ ? new Transaction().add(
130
+ SystemProgram.transfer({
131
+ fromPubkey: fromPublicKey,
132
+ lamports: assetValue.getBaseValue("number"),
133
+ toPubkey: new PublicKey(recipient),
134
+ }),
135
+ )
136
+ : assetValue.address
137
+ ? await createSolanaTokenTransaction({
138
+ amount: assetValue.getBaseValue("number"),
139
+ connection,
140
+ decimals: assetValue.decimal as number,
141
+ from: fromPublicKey,
142
+ recipient,
143
+ tokenAddress: assetValue.address,
144
+ })
145
+ : undefined;
146
+
147
+ if (!transaction) {
148
+ throw new SwapKitError("core_transaction_invalid_sender_address");
149
+ }
150
+
151
+ if (memo) transaction.add(createMemoInstruction(memo));
152
+
153
+ const blockHash = await connection.getLatestBlockhash();
154
+ transaction.recentBlockhash = blockHash.blockhash;
155
+ transaction.feePayer = fromPublicKey;
156
+
157
+ return transaction;
158
+ };
159
+ }
160
+
161
+ function transfer(getConnection: () => Promise<Connection>) {
162
+ return async ({
163
+ recipient,
164
+ assetValue,
165
+ fromKeypair,
166
+ memo,
167
+ isProgramDerivedAddress,
168
+ }: WalletTxParams & {
169
+ assetValue: AssetValue;
170
+ fromKeypair: Keypair;
171
+ isProgramDerivedAddress?: boolean;
172
+ }) => {
173
+ const { sendAndConfirmTransaction } = await import("@solana/web3.js");
174
+ const connection = await getConnection();
175
+
176
+ const transaction = await createSolanaTransaction(getConnection)({
177
+ recipient,
178
+ assetValue,
179
+ memo,
180
+ fromPublicKey: fromKeypair.publicKey,
181
+ isProgramDerivedAddress,
182
+ });
183
+
184
+ return sendAndConfirmTransaction(connection, transaction, [fromKeypair]);
185
+ };
186
+ }
187
+
188
+ function broadcastTransaction(getConnection: () => Promise<Connection>) {
189
+ return async (transaction: Transaction) => {
190
+ const connection = await getConnection();
191
+ return connection.sendRawTransaction(transaction.serialize());
192
+ };
193
+ }
194
+
195
+ async function createKeysForPath({
196
+ phrase,
197
+ derivationPath = DerivationPath.SOL,
198
+ }: { phrase: string; derivationPath?: string }) {
199
+ const { HDKey } = await import("micro-key-producer/slip10.js");
200
+ const { mnemonicToSeedSync } = await import("@scure/bip39");
201
+ const { Keypair } = await import("@solana/web3.js");
202
+ const seed = mnemonicToSeedSync(phrase);
203
+ const hdKey = HDKey.fromMasterSeed(seed);
204
+
205
+ return Keypair.fromSeed(hdKey.derive(derivationPath, true).privateKey);
206
+ }
207
+
208
+ function getAddressFromKeys(keypair: Keypair) {
209
+ return keypair.publicKey.toString();
210
+ }
211
+
212
+ async function getTokenBalances({
213
+ connection,
214
+ address,
215
+ }: { connection: Connection; address: string }) {
216
+ const { PublicKey } = await import("@solana/web3.js");
217
+ const { TOKEN_PROGRAM_ID } = await import("@solana/spl-token");
218
+ const { TokenListProvider } = await import("@solana/spl-token-registry");
219
+
220
+ const tokenAccounts = await connection.getParsedTokenAccountsByOwner(new PublicKey(address), {
221
+ programId: TOKEN_PROGRAM_ID,
222
+ });
223
+ const tokenListProvider = new TokenListProvider();
224
+ const tokenListContainer = await tokenListProvider.resolve();
225
+ const tokenList = tokenListContainer.filterByChainId(101).getList();
226
+
227
+ // Group token balances by mint address
228
+ const tokenBalanceMap = new Map<string, { amount: bigint; decimal: number; symbol: string }>();
229
+
230
+ for await (const tokenAccountInfo of tokenAccounts.value) {
231
+ const accountInfo = tokenAccountInfo.account.data.parsed.info;
232
+ const mintAddress = accountInfo.mint;
233
+ const decimal = accountInfo.tokenAmount.decimals;
234
+ const amount = BigInt(accountInfo.tokenAmount.amount);
235
+
236
+ if (amount <= BigInt(0)) continue;
237
+
238
+ const tokenInfo = tokenList.find((token) => token.address === mintAddress);
239
+ const tokenSymbol = tokenInfo?.symbol ?? "UNKNOWN";
240
+ const existing = tokenBalanceMap.get(mintAddress);
241
+
242
+ tokenBalanceMap.set(mintAddress, {
243
+ amount: existing ? existing.amount + amount : amount,
244
+ decimal,
245
+ symbol: tokenSymbol,
246
+ });
247
+ }
248
+
249
+ // Convert grouped balances to AssetValue array
250
+ const tokenBalances: AssetValue[] = Array.from(tokenBalanceMap.entries()).map(
251
+ ([mintAddress, { amount, decimal, symbol }]) =>
252
+ new AssetValue({
253
+ value: SwapKitNumber.fromBigInt(amount, decimal),
254
+ decimal,
255
+ identifier: `${Chain.Solana}.${symbol}${mintAddress ? `-${mintAddress.toString()}` : ""}`,
256
+ }),
257
+ );
258
+
259
+ return tokenBalances;
260
+ }
261
+
262
+ function getBalance(getConnection: () => Promise<Connection>) {
263
+ return async (address: string) => {
264
+ const { PublicKey } = await import("@solana/web3.js");
265
+ const connection = await getConnection();
266
+ const SOLBalance = await connection.getBalance(new PublicKey(address));
267
+ const tokenBalances = await getTokenBalances({ connection, address });
268
+
269
+ return [AssetValue.from({ chain: Chain.Solana, value: BigInt(SOLBalance) }), ...tokenBalances];
270
+ };
271
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./toolbox/baseSubstrateToolbox";
2
+ export * from "./toolbox";
3
+ export * from "./types/index";
@@ -0,0 +1,288 @@
1
+ import type { ApiPromise } from "@polkadot/api";
2
+ import type { SubmittableExtrinsic } from "@polkadot/api/types";
3
+ import type { KeyringPair } from "@polkadot/keyring/types";
4
+ import type { Callback, IKeyringPair, ISubmittableResult, Signer } from "@polkadot/types/types";
5
+ import { hexToU8a, isHex, u8aToHex } from "@polkadot/util";
6
+ import {
7
+ decodeAddress as decodePolkadotAddress,
8
+ encodeAddress as encodePolkadotAddress,
9
+ } from "@polkadot/util-crypto";
10
+ import {
11
+ AssetValue,
12
+ Chain,
13
+ SKConfig,
14
+ type SubstrateChain,
15
+ SwapKitError,
16
+ SwapKitNumber,
17
+ } from "@swapkit/helpers";
18
+
19
+ import { Network, type SubstrateNetwork } from "../types/network";
20
+
21
+ // TODO combine this type with the more general SK type
22
+ type SubstrateTransferParams = {
23
+ recipient: string;
24
+ assetValue: AssetValue;
25
+ from?: string;
26
+ };
27
+
28
+ export const isKeyringPair = (account: IKeyringPair | Signer): account is IKeyringPair => {
29
+ return "address" in account;
30
+ };
31
+
32
+ export const createKeyring = async (phrase: string, networkPrefix: number) => {
33
+ const { Keyring } = await import("@polkadot/api");
34
+ const { cryptoWaitReady } = await import("@polkadot/util-crypto");
35
+ await cryptoWaitReady();
36
+
37
+ return new Keyring({ type: "sr25519", ss58Format: networkPrefix }).addFromUri(phrase);
38
+ };
39
+
40
+ const getNonce = (api: ApiPromise, address: string) => api.rpc.system.accountNextIndex(address);
41
+
42
+ const getBalance = async (api: ApiPromise, gasAsset: AssetValue, address: string) => {
43
+ const data = await api.query.system?.account?.(address);
44
+
45
+ // @ts-expect-error @Towan some parts of data missing?
46
+ if (!data?.data?.free || data?.data?.isEmpty) {
47
+ return [gasAsset.set(0)];
48
+ }
49
+
50
+ return [
51
+ gasAsset.set(
52
+ // @ts-expect-error @Towan some parts of data missing?
53
+ SwapKitNumber.fromBigInt(BigInt(data.data.free.toString()), gasAsset.decimal).getValue(
54
+ "string",
55
+ ),
56
+ ),
57
+ ];
58
+ };
59
+
60
+ const validateAddress = (address: string, networkPrefix: number) => {
61
+ try {
62
+ const decodedAddress = decodeAddress(address, networkPrefix);
63
+
64
+ encodeAddress(decodedAddress, "ss58", networkPrefix);
65
+
66
+ return true;
67
+ } catch (_error) {
68
+ return false;
69
+ }
70
+ };
71
+
72
+ const createTransfer = (
73
+ api: ApiPromise,
74
+ { recipient, amount }: { recipient: string; amount: number },
75
+ ) => api.tx.balances?.transferAllowDeath?.(recipient, amount);
76
+
77
+ const transfer = async (
78
+ api: ApiPromise,
79
+ signer: IKeyringPair | Signer,
80
+ { recipient, assetValue, from }: SubstrateTransferParams,
81
+ ) => {
82
+ const transfer = createTransfer(api, {
83
+ recipient,
84
+ amount: assetValue.getBaseValue("number"),
85
+ });
86
+
87
+ const isKeyring = isKeyringPair(signer);
88
+
89
+ if (!transfer) return;
90
+
91
+ const address = from || (isKeyring ? (signer as IKeyringPair).address : undefined);
92
+ if (!address) throw new SwapKitError("core_transaction_invalid_sender_address");
93
+
94
+ const nonce = await getNonce(api, address);
95
+
96
+ const tx = await transfer.signAndSend(isKeyring ? signer : address, {
97
+ signer: isKeyring ? undefined : signer,
98
+ nonce,
99
+ });
100
+
101
+ return tx?.toString();
102
+ };
103
+
104
+ const estimateTransactionFee = async (
105
+ api: ApiPromise,
106
+ signer: IKeyringPair | Signer,
107
+ gasAsset: AssetValue,
108
+ { recipient, assetValue, from }: SubstrateTransferParams,
109
+ ) => {
110
+ const transfer = createTransfer(api, { recipient, amount: assetValue.getBaseValue("number") });
111
+
112
+ const address = from || (isKeyringPair(signer) && signer.address);
113
+ if (!address) return;
114
+
115
+ const paymentInfo = (await transfer?.paymentInfo(address, {
116
+ nonce: await getNonce(api, address),
117
+ })) || { partialFee: 0 };
118
+ return gasAsset.set(
119
+ SwapKitNumber.fromBigInt(BigInt(paymentInfo.partialFee.toString()), gasAsset.decimal).getValue(
120
+ "string",
121
+ ),
122
+ );
123
+ };
124
+
125
+ const broadcast = async (
126
+ tx: SubmittableExtrinsic<"promise">,
127
+ callback?: Callback<ISubmittableResult>,
128
+ ) => {
129
+ if (callback) return tx.send(callback);
130
+ const hash = await tx.send();
131
+ return hash.toString();
132
+ };
133
+
134
+ const sign = async (signer: IKeyringPair, tx: SubmittableExtrinsic<"promise">) => {
135
+ const signedTx = await tx.signAsync(signer);
136
+ return signedTx;
137
+ };
138
+
139
+ const signAndBroadcastKeyring = (
140
+ signer: IKeyringPair,
141
+ tx: SubmittableExtrinsic<"promise">,
142
+ callback?: Callback<ISubmittableResult>,
143
+ ) => {
144
+ if (callback) return tx.signAndSend(signer, callback);
145
+ const hash = tx.signAndSend(signer);
146
+ return hash.toString();
147
+ };
148
+
149
+ const signAndBroadcast = async ({
150
+ signer,
151
+ address,
152
+ tx,
153
+ callback,
154
+ api,
155
+ }: {
156
+ signer: Signer;
157
+ address: string;
158
+ tx: SubmittableExtrinsic<"promise">;
159
+ api: ApiPromise;
160
+ callback?: Callback<ISubmittableResult>;
161
+ }) => {
162
+ const nonce = await getNonce(api, address);
163
+ if (callback) {
164
+ tx.signAndSend(address, { nonce, signer }, callback);
165
+ }
166
+ const hash = tx.signAndSend(address, { nonce, signer });
167
+ return hash.toString();
168
+ };
169
+
170
+ function convertAddress(address: string, newPrefix: number) {
171
+ const decodedAddress = decodePolkadotAddress(address);
172
+ const convertedAddress = encodePolkadotAddress(decodedAddress, newPrefix);
173
+ return convertedAddress;
174
+ }
175
+
176
+ function decodeAddress(address: string, networkPrefix?: number) {
177
+ return isHex(address)
178
+ ? hexToU8a(address)
179
+ : decodePolkadotAddress(address, undefined, networkPrefix);
180
+ }
181
+
182
+ function encodeAddress(
183
+ address: Uint8Array,
184
+ encoding: "ss58" | "hex" = "ss58",
185
+ networkPrefix?: number,
186
+ ) {
187
+ if (encoding === "hex") {
188
+ return u8aToHex(address);
189
+ }
190
+
191
+ return encodePolkadotAddress(address, networkPrefix);
192
+ }
193
+
194
+ export const BaseSubstrateToolbox = ({
195
+ api,
196
+ network,
197
+ gasAsset,
198
+ signer,
199
+ }: {
200
+ api: ApiPromise;
201
+ network: SubstrateNetwork;
202
+ gasAsset: AssetValue;
203
+ signer: IKeyringPair | Signer;
204
+ }) => ({
205
+ api,
206
+ network,
207
+ gasAsset,
208
+ decodeAddress,
209
+ encodeAddress,
210
+ convertAddress,
211
+ createKeyring: (phrase: string) => createKeyring(phrase, network.prefix),
212
+ getAddress: (keyring: IKeyringPair | Signer = signer) =>
213
+ isKeyringPair(keyring) ? keyring.address : undefined,
214
+ createTransfer: ({ recipient, assetValue }: { recipient: string; assetValue: AssetValue }) =>
215
+ createTransfer(api, { recipient, amount: assetValue.getBaseValue("number") }),
216
+ getBalance: (address: string) => getBalance(api, gasAsset, address),
217
+ validateAddress: (address: string) => validateAddress(address, network.prefix),
218
+ transfer: (params: SubstrateTransferParams) => transfer(api, signer, params),
219
+ estimateTransactionFee: (params: SubstrateTransferParams) =>
220
+ estimateTransactionFee(api, signer, gasAsset, params),
221
+ sign: (tx: SubmittableExtrinsic<"promise">) => {
222
+ if (isKeyringPair(signer)) {
223
+ return sign(signer, tx);
224
+ }
225
+ throw new SwapKitError(
226
+ "core_wallet_not_keypair_wallet",
227
+ "Signer does not have keyring pair capabilities required for signing.",
228
+ );
229
+ },
230
+ broadcast: (tx: SubmittableExtrinsic<"promise">, callback?: Callback<ISubmittableResult>) =>
231
+ broadcast(tx, callback),
232
+ signAndBroadcast: ({
233
+ tx,
234
+ callback,
235
+ address,
236
+ }: {
237
+ tx: SubmittableExtrinsic<"promise">;
238
+ callback?: Callback<ISubmittableResult>;
239
+ address?: string;
240
+ }) => {
241
+ if (isKeyringPair(signer)) {
242
+ return signAndBroadcastKeyring(signer, tx, callback);
243
+ }
244
+
245
+ if (address) {
246
+ return signAndBroadcast({ signer, address, tx, callback, api });
247
+ }
248
+
249
+ throw new SwapKitError(
250
+ "core_wallet_not_keypair_wallet",
251
+ "Signer does not have keyring pair capabilities required for signing.",
252
+ );
253
+ },
254
+ });
255
+
256
+ export const substrateValidateAddress = ({
257
+ address,
258
+ chain,
259
+ }: { address: string; chain: Chain.Polkadot | Chain.Chainflip }) => {
260
+ const { prefix } = chain === Chain.Polkadot ? Network.DOT : Network.FLIP;
261
+
262
+ return validateAddress(address, prefix) || validateAddress(address, Network.GENERIC.prefix);
263
+ };
264
+
265
+ export async function ToolboxFactory({
266
+ generic,
267
+ chain,
268
+ signer,
269
+ }: ToolboxParams & { chain: SubstrateChain }) {
270
+ const { ApiPromise, WsProvider } = await import("@polkadot/api");
271
+
272
+ const provider = new WsProvider(SKConfig.get("rpcUrls")[chain]);
273
+ const api = await ApiPromise.create({ provider });
274
+ const gasAsset = AssetValue.from({ chain });
275
+ const network = generic ? Network.GENERIC : Network[chain];
276
+
277
+ return BaseSubstrateToolbox({ api, signer, gasAsset, network });
278
+ }
279
+
280
+ export type ToolboxParams = {
281
+ generic?: boolean;
282
+ signer: KeyringPair | Signer;
283
+ };
284
+
285
+ export type BaseSubstrateWallet = ReturnType<typeof BaseSubstrateToolbox>;
286
+ export type SubstrateWallets = {
287
+ [chain in SubstrateChain]: BaseSubstrateWallet;
288
+ };
@@ -0,0 +1,40 @@
1
+ import { AssetValue, Chain } from "@swapkit/helpers";
2
+
3
+ import { ToolboxFactory, type ToolboxParams } from "./baseSubstrateToolbox";
4
+
5
+ export const PolkadotToolbox = ({ signer, generic = false }: ToolboxParams) => {
6
+ return ToolboxFactory({ chain: Chain.Polkadot, generic, signer });
7
+ };
8
+
9
+ export const ChainflipToolbox = async ({ signer, generic = false }: ToolboxParams) => {
10
+ const toolbox = await ToolboxFactory({ chain: Chain.Chainflip, generic, signer });
11
+
12
+ async function getBalance(address: string) {
13
+ // @ts-expect-error @Towan some parts of data missing?
14
+ // biome-ignore lint/correctness/noUnsafeOptionalChaining: @Towan some parts of data missing?
15
+ const { balance } = await toolbox.api.query.flip?.account?.(address);
16
+
17
+ return [AssetValue.from({ chain: Chain.Chainflip, value: BigInt(balance.toString()) })];
18
+ }
19
+
20
+ return { ...toolbox, getBalance };
21
+ };
22
+
23
+ type ToolboxType = {
24
+ DOT: ReturnType<typeof PolkadotToolbox>;
25
+ FLIP: ReturnType<typeof ChainflipToolbox>;
26
+ };
27
+
28
+ export const getToolboxByChain = <T extends keyof ToolboxType>(
29
+ chain: T,
30
+ params: ToolboxParams,
31
+ ): ToolboxType[T] => {
32
+ switch (chain) {
33
+ case Chain.Chainflip:
34
+ return ChainflipToolbox(params);
35
+ case Chain.Polkadot:
36
+ return PolkadotToolbox(params);
37
+ default:
38
+ throw new Error(`Chain ${chain} is not supported`);
39
+ }
40
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./network";
2
+ export * from "./wallet";
@@ -0,0 +1,42 @@
1
+ import type { SubstrateChain } from "@swapkit/helpers";
2
+
3
+ export const polkadotNetwork = {
4
+ prefix: 0,
5
+ network: "polkadot",
6
+ displayName: "Polkadot Relay Chain",
7
+ symbols: ["DOT"],
8
+ decimals: [10],
9
+ standardAccount: "*25519",
10
+ website: "https://polkadot.network",
11
+ };
12
+
13
+ export const chainflipNetwork = {
14
+ prefix: 2112,
15
+ network: "chainflip",
16
+ displayName: "Chainflip",
17
+ symbols: ["FLIP"],
18
+ decimals: [18],
19
+ standardAccount: "*25519",
20
+ website: "https://chainflip.io/",
21
+ };
22
+
23
+ export const subtrateNetwork = {
24
+ prefix: 42,
25
+ network: "substrate",
26
+ displayName: "Substrate",
27
+ symbols: [],
28
+ decimals: [],
29
+ standardAccount: "*25519",
30
+ website: "https://substrate.io/",
31
+ };
32
+
33
+ export const Network: Record<SubstrateChain | "GENERIC", SubstrateNetwork> = {
34
+ DOT: polkadotNetwork,
35
+ FLIP: chainflipNetwork,
36
+ GENERIC: subtrateNetwork,
37
+ };
38
+
39
+ export type SubstrateNetwork =
40
+ | typeof polkadotNetwork
41
+ | typeof chainflipNetwork
42
+ | typeof subtrateNetwork;