@swapkit/toolboxes 1.0.0-beta.22 → 1.0.0-beta.24

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 (51) hide show
  1. package/dist/{chunk-12xtvbsp.js → chunk-6f98phv2.js} +2 -2
  2. package/dist/{chunk-12xtvbsp.js.map → chunk-6f98phv2.js.map} +1 -1
  3. package/dist/{chunk-kbnwrc5b.js → chunk-zcdeg6h9.js} +2 -2
  4. package/dist/{chunk-kbnwrc5b.js.map → chunk-zcdeg6h9.js.map} +1 -1
  5. package/dist/src/cosmos/index.cjs +2 -2
  6. package/dist/src/cosmos/index.cjs.map +6 -6
  7. package/dist/src/cosmos/index.js +2 -2
  8. package/dist/src/cosmos/index.js.map +6 -6
  9. package/dist/src/evm/index.js +2 -2
  10. package/dist/src/evm/index.js.map +1 -1
  11. package/dist/src/index.cjs +2 -2
  12. package/dist/src/index.cjs.map +3 -3
  13. package/dist/src/index.js +2 -2
  14. package/dist/src/index.js.map +3 -3
  15. package/dist/src/near/index.cjs +2 -2
  16. package/dist/src/near/index.cjs.map +4 -4
  17. package/dist/src/near/index.js +2 -2
  18. package/dist/src/near/index.js.map +4 -4
  19. package/dist/src/substrate/index.cjs +2 -2
  20. package/dist/src/substrate/index.cjs.map +5 -4
  21. package/dist/src/substrate/index.js +2 -2
  22. package/dist/src/substrate/index.js.map +5 -4
  23. package/dist/src/tron/index.cjs +2 -2
  24. package/dist/src/tron/index.cjs.map +3 -3
  25. package/dist/src/tron/index.js +2 -2
  26. package/dist/src/tron/index.js.map +3 -3
  27. package/dist/src/utxo/index.cjs +4 -2
  28. package/dist/src/utxo/index.cjs.map +8 -7
  29. package/dist/src/utxo/index.js +4 -2
  30. package/dist/src/utxo/index.js.map +8 -7
  31. package/package.json +2 -2
  32. package/src/cosmos/thorchainUtils/registry.ts +3 -3
  33. package/src/cosmos/toolbox/cosmos.ts +21 -7
  34. package/src/cosmos/toolbox/thorchain.ts +6 -6
  35. package/src/cosmos/util.ts +66 -3
  36. package/src/evm/__tests__/address-validation.test.ts +86 -0
  37. package/src/index.ts +1 -0
  38. package/src/near/__tests__/core.test.ts +80 -0
  39. package/src/near/helpers/core.ts +4 -1
  40. package/src/near/toolbox.ts +26 -25
  41. package/src/substrate/balance.ts +92 -0
  42. package/src/substrate/substrate.ts +6 -4
  43. package/src/tron/__tests__/address-validation.test.ts +123 -0
  44. package/src/tron/toolbox.ts +10 -10
  45. package/src/utxo/__tests__/zcash-integration.test.ts +114 -0
  46. package/src/utxo/helpers/api.ts +36 -0
  47. package/src/utxo/helpers/coinselect.ts +2 -0
  48. package/src/utxo/index.ts +1 -0
  49. package/src/utxo/toolbox/index.ts +14 -3
  50. package/src/utxo/toolbox/utxo.ts +7 -0
  51. package/src/utxo/toolbox/zcash.ts +208 -0
@@ -0,0 +1,86 @@
1
+ import { beforeAll, describe, expect, test } from "bun:test";
2
+ import { Chain } from "@swapkit/helpers";
3
+ import { getEvmToolbox } from "../toolbox";
4
+
5
+ const context: {
6
+ validateAddress: (address: string) => boolean;
7
+ } = {} as any;
8
+
9
+ beforeAll(async () => {
10
+ // Get EVM toolbox for address validation
11
+ const toolbox = await getEvmToolbox(Chain.Ethereum);
12
+ context.validateAddress = toolbox.validateAddress;
13
+ });
14
+
15
+ describe("EVM Address Validation", () => {
16
+ test("should validate valid EVM addresses", () => {
17
+ const validAddresses = [
18
+ "0xa052Ddf1c1739419B90FB7bf722843AD3e63114B", // User provided address
19
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC contract
20
+ "0x6d6e022eE439C8aB8B7a7dBb0576f8090319CDc6", // Test address
21
+ "0xE29E61479420Dd1029A9946710Ac31A0d140e77F", // Another valid address
22
+ "0x0000000000000000000000000000000000000000", // Zero address
23
+ "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF", // Max address
24
+ "0x1234567890123456789012345678901234567890", // Mixed case
25
+ ];
26
+
27
+ for (const address of validAddresses) {
28
+ expect(context.validateAddress(address)).toBe(true);
29
+ }
30
+ });
31
+
32
+ test("should reject invalid EVM addresses", () => {
33
+ const invalidAddresses = [
34
+ "", // Empty string
35
+ "invalid", // Random string
36
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // TRON address
37
+ "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", // Bitcoin address
38
+ "cosmos1abc123", // Cosmos address
39
+ "0xa052Ddf1c1739419B90FB7bf722843AD3e63114", // Too short (missing 1 char)
40
+ "0xa052Ddf1c1739419B90FB7bf722843AD3e63114BB", // Too long (extra char)
41
+ "0XA052DDF1C1739419B90FB7BF722843AD3E63114B", // Uppercase 0X prefix
42
+ "0xG052Ddf1c1739419B90FB7bf722843AD3e63114B", // Invalid hex character
43
+ "0x", // Only prefix
44
+ ];
45
+
46
+ for (const address of invalidAddresses) {
47
+ expect(context.validateAddress(address)).toBe(false);
48
+ }
49
+ });
50
+
51
+ test("should handle case normalization properly", () => {
52
+ const address = "0xa052Ddf1c1739419B90FB7bf722843AD3e63114B"; // Proper checksum
53
+ const lowerCase = address.toLowerCase();
54
+
55
+ // Valid case variations should be accepted
56
+ expect(context.validateAddress(address)).toBe(true);
57
+ expect(context.validateAddress(lowerCase)).toBe(true);
58
+ });
59
+
60
+ test("should reject invalid checksum addresses", () => {
61
+ const invalidChecksumAddress = "0xA052dDF1C1739419b90fb7BF722843ad3E63114b"; // Invalid checksum
62
+
63
+ // Should reject mixed case with invalid checksum
64
+ expect(context.validateAddress(invalidChecksumAddress)).toBe(false);
65
+ });
66
+
67
+ test("should handle edge cases", () => {
68
+ const edgeCases = [null, undefined, 123, {}, [], true, false];
69
+
70
+ for (const testCase of edgeCases) {
71
+ // Should not throw but return false for invalid inputs
72
+ expect(context.validateAddress(testCase as any)).toBe(false);
73
+ }
74
+ });
75
+
76
+ test("should validate checksummed addresses", () => {
77
+ const checksummedAddresses = [
78
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC with proper checksum
79
+ "0x6d6e022eE439C8aB8B7a7dBb0576f8090319CDc6", // Another checksummed address
80
+ ];
81
+
82
+ for (const address of checksummedAddresses) {
83
+ expect(context.validateAddress(address)).toBe(true);
84
+ }
85
+ });
86
+ });
package/src/index.ts CHANGED
@@ -192,6 +192,7 @@ export async function getToolbox<T extends keyof Toolboxes>(
192
192
  Chain.Dogecoin,
193
193
  Chain.BitcoinCash,
194
194
  Chain.Bitcoin,
195
+ Chain.Zcash,
195
196
  async () => {
196
197
  const { getUtxoToolbox } = await import("@swapkit/toolboxes/utxo");
197
198
  const utxoToolbox = await getUtxoToolbox(
@@ -0,0 +1,80 @@
1
+ import { beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
+ import { AssetValue, Chain, SKConfig } from "@swapkit/helpers";
3
+ import { providers } from "near-api-js";
4
+ import { getFullAccessPublicKey } from "../helpers/core";
5
+ import { getNearToolbox } from "../toolbox";
6
+
7
+ const accountId = "ea03292d08136cca439513a33c76af083e5204eceb4ce720320fff84071a447f";
8
+
9
+ const context: {
10
+ provider: providers.JsonRpcProvider;
11
+ toolbox: Awaited<ReturnType<typeof getNearToolbox>>;
12
+ } = {} as any;
13
+
14
+ beforeAll(() => {
15
+ context.provider = new providers.JsonRpcProvider({
16
+ url: SKConfig.get("rpcUrls")[Chain.Near],
17
+ });
18
+ });
19
+
20
+ beforeEach(async () => {
21
+ context.toolbox = await getNearToolbox();
22
+ });
23
+
24
+ describe("NEAR createTransaction", () => {
25
+ test("should retrieve full access public key for valid account", async () => {
26
+ const toolbox = context.toolbox;
27
+
28
+ const transaction = await toolbox.createTransaction({
29
+ recipient: accountId, // Self transfer
30
+ assetValue: AssetValue.from({
31
+ chain: Chain.Near,
32
+ value: "0.001", // Small amount
33
+ }),
34
+ sender: accountId,
35
+ feeRate: 300000000000000, // 300 TGas
36
+ });
37
+
38
+ expect(transaction).toBeDefined();
39
+ expect(transaction.publicKey).toBeDefined();
40
+ expect(transaction.serialized).toBeDefined();
41
+ }, 30000);
42
+
43
+ test("should throw error for invalid account", async () => {
44
+ const provider = context.provider;
45
+ const invalidAccountId = "non-existent-account-12345.testnet";
46
+
47
+ await expect(async () => {
48
+ await getFullAccessPublicKey(provider, invalidAccountId);
49
+ }).toThrow();
50
+ }, 10000);
51
+
52
+ test("should handle network errors gracefully", async () => {
53
+ const invalidProvider = new providers.JsonRpcProvider({ url: "https://invalid-rpc-url.test" });
54
+
55
+ await expect(async () => {
56
+ await getFullAccessPublicKey(invalidProvider, "any-account.testnet");
57
+ }).toThrow();
58
+ }, 10000);
59
+
60
+ test("should work with contract function call transaction", async () => {
61
+ const toolbox = context.toolbox;
62
+ const provider = context.provider;
63
+
64
+ const contractTransaction = await toolbox.createContractFunctionCall({
65
+ sender: accountId,
66
+ contractId: "wrap.testnet", // Known testnet contract
67
+ methodName: "storage_deposit",
68
+ args: { account_id: accountId },
69
+ gas: "300000000000000", // 300 TGas
70
+ attachedDeposit: "1250000000000000000000", // 0.00125 NEAR for storage
71
+ });
72
+
73
+ expect(contractTransaction).toBeDefined();
74
+ expect(contractTransaction.publicKey).toBeDefined();
75
+
76
+ // Verify the public key can be retrieved directly
77
+ const { publicKey } = await getFullAccessPublicKey(provider, accountId);
78
+ expect(publicKey.toString()).toBe(contractTransaction.publicKey);
79
+ }, 30000);
80
+ });
@@ -82,5 +82,8 @@ export async function getFullAccessPublicKey(provider: Provider, accountId: stri
82
82
  }
83
83
 
84
84
  const { utils } = await import("near-api-js");
85
- return utils.PublicKey.fromString(fullAccessKey.public_key);
85
+ const publicKey = utils.PublicKey.fromString(fullAccessKey.public_key);
86
+ const nonce = (fullAccessKey.access_key.nonce as number) || 0;
87
+
88
+ return { publicKey, nonce };
86
89
  }
@@ -50,9 +50,6 @@ export async function getNearToolbox(toolboxParams?: NearToolboxParams) {
50
50
  const provider = new providers.JsonRpcProvider({ url });
51
51
 
52
52
  async function getAccount(address?: string) {
53
- if (!signer) {
54
- throw new SwapKitError("toolbox_near_no_signer");
55
- }
56
53
  const { Account } = await import("near-api-js");
57
54
 
58
55
  const _address = address || (await getAddress());
@@ -118,19 +115,31 @@ export async function getNearToolbox(toolboxParams?: NearToolboxParams) {
118
115
  }
119
116
 
120
117
  async function createTransaction(params: NearCreateTransactionParams) {
121
- const { recipient, assetValue, memo, feeRate: gas, attachedDeposit, sender } = params;
118
+ const { recipient, assetValue, memo, feeRate: gas, attachedDeposit, sender: signerId } = params;
122
119
 
123
- const signerId = sender || (await getAddress());
124
- const publicKey = await getFullAccessPublicKey(provider, signerId);
125
-
126
- const accessKey = await provider.query({
127
- request_type: "view_access_key",
128
- finality: "final",
129
- account_id: signerId,
130
- });
120
+ // Handle NEP-141 token transfers
121
+ if (!assetValue.isGasAsset) {
122
+ const contractId = assetValue.address;
123
+ if (!contractId) {
124
+ throw new SwapKitError("toolbox_near_missing_contract_address");
125
+ }
131
126
 
132
- const nonce = (accessKey as any).nonce + 1;
127
+ return createContractFunctionCall({
128
+ sender: signerId,
129
+ contractId,
130
+ methodName: "ft_transfer",
131
+ args: {
132
+ receiver_id: recipient,
133
+ amount: assetValue.getBaseValue("string"),
134
+ memo: memo || null,
135
+ },
136
+ gas: gas.toString() || "100000000000000", // 100 TGas default
137
+ attachedDeposit: "1", // 1 yoctoNEAR required for NEP-141 transfers
138
+ });
139
+ }
133
140
 
141
+ // Native NEAR transfer
142
+ const { publicKey, nonce } = await getFullAccessPublicKey(provider, signerId);
134
143
  const baseAmount = assetValue.getBaseValue("bigint");
135
144
 
136
145
  const { SCHEMA } = await import("near-api-js/lib/transaction");
@@ -163,7 +172,7 @@ export async function getNearToolbox(toolboxParams?: NearToolboxParams) {
163
172
  serialized: serializedBase64,
164
173
  publicKey: publicKey.toString(),
165
174
  details: {
166
- signerId: await getAddress(),
175
+ signerId,
167
176
  nonce: nonce,
168
177
  blockHash: utils.serialize.base_encode(blockHash),
169
178
  },
@@ -171,24 +180,16 @@ export async function getNearToolbox(toolboxParams?: NearToolboxParams) {
171
180
  }
172
181
 
173
182
  async function createContractFunctionCall(params: {
174
- accountId: string;
183
+ sender: string;
175
184
  contractId: string;
176
185
  methodName: string;
177
186
  args: any;
178
187
  gas: string;
179
188
  attachedDeposit: string;
180
189
  }) {
181
- const { accountId } = params;
190
+ const { sender: accountId } = params;
182
191
 
183
- const publicKey = await getFullAccessPublicKey(provider, accountId);
184
-
185
- const accessKey = await provider.query({
186
- request_type: "view_access_key",
187
- finality: "final",
188
- account_id: accountId,
189
- public_key: publicKey.toString(),
190
- });
191
- const nonce = (accessKey as any).nonce + 1;
192
+ const { publicKey, nonce } = await getFullAccessPublicKey(provider, accountId);
192
193
 
193
194
  const { SCHEMA } = await import("near-api-js/lib/transaction");
194
195
  const { transactions, utils } = await import("near-api-js");
@@ -0,0 +1,92 @@
1
+ import type { ApiPromise } from "@polkadot/api";
2
+ import { AssetValue, Chain, SwapKitNumber } from "@swapkit/helpers";
3
+
4
+ /**
5
+ * Get balance for standard Substrate chains (Polkadot, etc.)
6
+ * Uses api.query.system.account to query free and reserved balances
7
+ */
8
+ export async function getSubstrateBalance(
9
+ api: ApiPromise,
10
+ gasAsset: AssetValue,
11
+ address: string,
12
+ ): Promise<AssetValue[]> {
13
+ try {
14
+ const account = await api.query.system?.account?.(address);
15
+
16
+ if (!account) {
17
+ return [gasAsset.set(0)];
18
+ }
19
+
20
+ const {
21
+ // @ts-expect-error
22
+ data: { free },
23
+ } = account;
24
+
25
+ // Convert the free balance to string using SwapKitNumber for proper decimal handling
26
+ const freeBalance = SwapKitNumber.fromBigInt(
27
+ BigInt(free.toString()),
28
+ gasAsset.decimal,
29
+ ).getValue("string");
30
+
31
+ return [gasAsset.set(freeBalance)];
32
+ } catch (error) {
33
+ console.error("Error fetching substrate balance:", error);
34
+ return [gasAsset.set(0)];
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get balance for Chainflip chain
40
+ * Uses api.query.flip.account to query FLIP balances
41
+ */
42
+ export async function getChainflipBalance(
43
+ api: ApiPromise,
44
+ gasAsset: AssetValue,
45
+ address: string,
46
+ ): Promise<AssetValue[]> {
47
+ try {
48
+ // Chainflip uses a custom flip pallet for account balances
49
+ const flipAccount = await api.query.flip?.account?.(address);
50
+
51
+ if (!flipAccount) {
52
+ return [gasAsset.set(0)];
53
+ }
54
+
55
+ // Extract balance from the flip account structure
56
+ // The structure has a balance field directly
57
+ //@ts-expect-error
58
+ const balance = flipAccount.balance || flipAccount.data?.balance;
59
+
60
+ if (!balance || balance.isEmpty) {
61
+ return [gasAsset.set(0)];
62
+ }
63
+
64
+ // Convert balance to string using SwapKitNumber
65
+ const balanceStr = SwapKitNumber.fromBigInt(
66
+ BigInt(balance.toString()),
67
+ gasAsset.decimal,
68
+ ).getValue("string");
69
+
70
+ return [gasAsset.set(balanceStr)];
71
+ } catch (error) {
72
+ console.error("Error fetching chainflip balance:", error);
73
+ return [gasAsset.set(0)];
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Factory function to create chain-specific balance getter
79
+ */
80
+ export function createBalanceGetter(chain: Chain, api: ApiPromise) {
81
+ return function getBalance(address: string): Promise<AssetValue[]> {
82
+ const gasAsset = AssetValue.from({ chain });
83
+
84
+ switch (chain) {
85
+ case Chain.Chainflip:
86
+ return getChainflipBalance(api, gasAsset, address);
87
+
88
+ default:
89
+ return getSubstrateBalance(api, gasAsset, address);
90
+ }
91
+ };
92
+ }
@@ -19,7 +19,7 @@ import {
19
19
  } from "@swapkit/helpers";
20
20
 
21
21
  import { P, match } from "ts-pattern";
22
- import { getBalance } from "../utils";
22
+ import { createBalanceGetter } from "./balance";
23
23
  import { SubstrateNetwork, type SubstrateTransferParams } from "./types";
24
24
 
25
25
  export const PolkadotToolbox = ({ generic = false, ...signerParams }: ToolboxParams = {}) => {
@@ -36,7 +36,7 @@ export const ChainflipToolbox = async ({
36
36
  ...signerParams,
37
37
  });
38
38
 
39
- return { ...toolbox, getBalance: getBalance(Chain.Chainflip) };
39
+ return { ...toolbox };
40
40
  };
41
41
 
42
42
  export type SubstrateToolboxes = {
@@ -208,11 +208,13 @@ export const BaseSubstrateToolbox = ({
208
208
  network,
209
209
  gasAsset,
210
210
  signer,
211
+ chain,
211
212
  }: {
212
213
  api: ApiPromise;
213
214
  network: SubstrateNetwork;
214
215
  gasAsset: AssetValue;
215
216
  signer?: IKeyringPair | Signer;
217
+ chain?: SubstrateChain;
216
218
  }) => ({
217
219
  api,
218
220
  network,
@@ -220,7 +222,7 @@ export const BaseSubstrateToolbox = ({
220
222
  decodeAddress,
221
223
  encodeAddress,
222
224
  convertAddress,
223
- getBalance: getBalance(Chain.Polkadot),
225
+ getBalance: createBalanceGetter(chain || Chain.Polkadot, api),
224
226
  createKeyring: (phrase: string) => createKeyring(phrase, network.prefix),
225
227
  getAddress: (keyring?: IKeyringPair | Signer) => {
226
228
  const keyringPair = keyring || signer;
@@ -301,7 +303,7 @@ export async function createSubstrateToolbox({
301
303
  .with({ signer: P.any }, ({ signer }) => signer)
302
304
  .otherwise(() => undefined);
303
305
 
304
- return BaseSubstrateToolbox({ api, signer, gasAsset, network });
306
+ return BaseSubstrateToolbox({ api, signer, gasAsset, network, chain });
305
307
  }
306
308
 
307
309
  export type ToolboxParams = {
@@ -0,0 +1,123 @@
1
+ import { beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
+ import { AssetValue, Chain, SKConfig } from "@swapkit/helpers";
3
+ import { createTronToolbox, getTronAddressValidator } from "../toolbox";
4
+
5
+ // Test mnemonic for consistent testing
6
+ const TEST_PHRASE =
7
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
8
+
9
+ const context: {
10
+ toolbox: Awaited<ReturnType<typeof createTronToolbox>>;
11
+ validateAddress: (address: string) => boolean;
12
+ } = {} as any;
13
+
14
+ beforeAll(async () => {
15
+ // Set up TRON mainnet configuration
16
+ SKConfig.set({
17
+ rpcUrls: {
18
+ [Chain.Tron]: "https://api.trongrid.io",
19
+ },
20
+ });
21
+
22
+ // Get the address validator
23
+ context.validateAddress = await getTronAddressValidator();
24
+ });
25
+
26
+ beforeEach(async () => {
27
+ context.toolbox = await createTronToolbox({ phrase: TEST_PHRASE });
28
+ });
29
+
30
+ describe("TRON Address Validation", () => {
31
+ test("should validate valid TRON addresses", () => {
32
+ const validAddresses = [
33
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // USDT contract
34
+ "TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7", // Mainnet address
35
+ "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", // Another valid address
36
+ ];
37
+
38
+ for (const address of validAddresses) {
39
+ expect(context.validateAddress(address)).toBe(true);
40
+ expect(context.toolbox.validateAddress(address)).toBe(true);
41
+ }
42
+ });
43
+
44
+ test("should reject invalid TRON addresses", () => {
45
+ const invalidAddresses = [
46
+ "", // Empty string
47
+ "invalid", // Random string
48
+ "0x742d35Cc6648C532F5e7c3d2a7a8E1e1e5b7c8D3", // Ethereum address
49
+ "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", // Bitcoin address
50
+ "cosmos1abc123", // Cosmos address
51
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6", // Too short
52
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6tt", // Too long
53
+ "XR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", // Wrong prefix
54
+ "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6O", // Invalid checksum
55
+ ];
56
+
57
+ for (const address of invalidAddresses) {
58
+ expect(context.validateAddress(address)).toBe(false);
59
+ expect(context.toolbox.validateAddress(address)).toBe(false);
60
+ }
61
+ });
62
+
63
+ test("should validate address from generated account", async () => {
64
+ const toolbox = context.toolbox;
65
+
66
+ // Get address from the toolbox
67
+ const address = await toolbox.getAddress();
68
+
69
+ expect(address).toBeDefined();
70
+ expect(typeof address).toBe("string");
71
+ expect(address.length).toBeGreaterThan(0);
72
+
73
+ // The generated address should be valid
74
+ expect(context.validateAddress(address)).toBe(true);
75
+ expect(toolbox.validateAddress(address)).toBe(true);
76
+
77
+ // Address should start with 'T'
78
+ expect(address.startsWith("T")).toBe(true);
79
+ });
80
+
81
+ test("should create transaction with valid addresses", async () => {
82
+ const toolbox = context.toolbox;
83
+ const fromAddress = await toolbox.getAddress();
84
+ const toAddress = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; // Valid TRON address
85
+
86
+ // Both addresses should be valid
87
+ expect(toolbox.validateAddress(fromAddress)).toBe(true);
88
+ expect(toolbox.validateAddress(toAddress)).toBe(true);
89
+
90
+ // Create a transaction
91
+ const transaction = await toolbox.createTransaction({
92
+ recipient: toAddress,
93
+ assetValue: AssetValue.from({
94
+ chain: Chain.Tron,
95
+ value: "1", // 1 TRX
96
+ }),
97
+ });
98
+
99
+ expect(transaction).toBeDefined();
100
+ expect(transaction.raw_data_hex).toBeDefined();
101
+ });
102
+
103
+ test("should handle case sensitivity in addresses", () => {
104
+ const address = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
105
+ const lowerCase = address.toLowerCase();
106
+ const upperCase = address.toUpperCase();
107
+
108
+ // TRON addresses are case sensitive - only the original should be valid
109
+ expect(context.validateAddress(address)).toBe(true);
110
+ expect(context.validateAddress(lowerCase)).toBe(false);
111
+ expect(context.validateAddress(upperCase)).toBe(false);
112
+ });
113
+
114
+ test("should handle edge cases", () => {
115
+ const edgeCases = [null, undefined, 123, {}, [], true, false];
116
+
117
+ for (const testCase of edgeCases) {
118
+ // Should not throw but return false for invalid inputs
119
+ expect(context.validateAddress(testCase as any)).toBe(false);
120
+ expect(context.toolbox.validateAddress(testCase as any)).toBe(false);
121
+ }
122
+ });
123
+ });
@@ -8,7 +8,6 @@ import {
8
8
  updateDerivationPath,
9
9
  warnOnce,
10
10
  } from "@swapkit/helpers";
11
- import { TronWeb } from "tronweb";
12
11
  import { P, match } from "ts-pattern";
13
12
 
14
13
  import { trc20ABI } from "./helpers/trc20.abi.js";
@@ -21,8 +20,11 @@ import type {
21
20
  } from "./types.js";
22
21
 
23
22
  export async function getTronAddressValidator() {
24
- const { TronWeb } = await import("tronweb");
25
- return (address: string) => TronWeb.isAddress(address);
23
+ const { TronWeb } = require("tronweb");
24
+
25
+ return (address: string) => {
26
+ return TronWeb.isAddress(address);
27
+ };
26
28
  }
27
29
 
28
30
  export async function getTronPrivateKeyFromMnemonic({
@@ -63,6 +65,7 @@ async function createKeysForPath({
63
65
  }) {
64
66
  const { HDKey } = await import("@scure/bip32");
65
67
  const { mnemonicToSeedSync } = await import("@scure/bip39");
68
+ const { TronWeb } = require("tronweb");
66
69
 
67
70
  const seed = mnemonicToSeedSync(phrase);
68
71
  const hdKey = HDKey.fromMasterSeed(seed);
@@ -93,6 +96,7 @@ async function createKeysForPath({
93
96
  }
94
97
 
95
98
  export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
99
+ const { TronWeb } = await import("tronweb");
96
100
  // Always get configuration from SKConfig
97
101
  const rpcUrl = SKConfig.get("rpcUrls")[Chain.Tron];
98
102
  // Note: TRON API key support can be added to SKConfig apiKeys when needed
@@ -122,10 +126,6 @@ export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
122
126
  return await signer.getAddress();
123
127
  };
124
128
 
125
- const validateAddress = (address: string) => {
126
- return tronWeb.isAddress(address);
127
- };
128
-
129
129
  const calculateFeeLimit = () => {
130
130
  return 100_000_000; // 100 TRX in SUN
131
131
  };
@@ -267,7 +267,7 @@ export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
267
267
  return tronWeb.transactionBuilder.addUpdateData(transaction, memo, "utf8");
268
268
  }
269
269
 
270
- return transaction.raw_data_hex;
270
+ return transaction;
271
271
  }
272
272
 
273
273
  // For TRC20, we would need to build the transaction manually
@@ -294,7 +294,7 @@ export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
294
294
  from,
295
295
  );
296
296
 
297
- return result.transaction.raw_data_hex;
297
+ return result.transaction;
298
298
  };
299
299
 
300
300
  const signTransaction = async (transaction: TronTransaction) => {
@@ -310,7 +310,7 @@ export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
310
310
  return {
311
311
  tronWeb,
312
312
  getAddress,
313
- validateAddress,
313
+ validateAddress: await getTronAddressValidator(),
314
314
  getBalance,
315
315
  transfer,
316
316
  estimateTransactionFee,