@swapkit/toolboxes 1.0.0-beta.2 → 1.0.0-beta.21
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-12xtvbsp.js +4 -0
- package/dist/{chunk-0h4xdrwz.js.map → chunk-12xtvbsp.js.map} +2 -2
- 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-0f0249b1.js → chunk-9bqegm61.js} +1 -1
- package/dist/chunk-kbnwrc5b.js +4 -0
- package/dist/chunk-kbnwrc5b.js.map +10 -0
- 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/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 +11 -0
- package/dist/src/substrate/index.js +3 -0
- package/dist/src/substrate/index.js.map +11 -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 +3 -0
- package/dist/src/utxo/index.cjs.map +16 -0
- package/dist/src/utxo/index.js +3 -0
- package/dist/src/utxo/index.js.map +16 -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 +16 -11
- package/src/cosmos/toolbox/index.ts +2 -2
- package/src/cosmos/toolbox/thorchain.ts +11 -9
- package/src/cosmos/util.ts +13 -6
- 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 +117 -100
- package/src/near/helpers/contractFactory.ts +22 -0
- package/src/near/helpers/core.ts +86 -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 +497 -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 +75 -2
- package/src/substrate/substrate.ts +1 -1
- package/src/tron/helpers/trc20.abi.ts +40 -0
- package/src/tron/index.ts +16 -0
- package/src/tron/toolbox.ts +328 -0
- package/src/tron/types.ts +22 -0
- package/src/utxo/helpers/api.ts +30 -16
- package/src/utxo/helpers/bchaddrjs.ts +8 -8
- package/src/utxo/helpers/coinselect.ts +2 -2
- package/src/utxo/helpers/txSize.ts +4 -3
- package/src/utxo/toolbox/bitcoinCash.ts +22 -14
- package/src/utxo/toolbox/index.ts +2 -1
- package/src/utxo/toolbox/utxo.ts +35 -27
- 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
package/src/ripple/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
BaseDecimal,
|
|
4
4
|
Chain,
|
|
5
5
|
type ChainSigner,
|
|
6
|
-
type GenericCreateTransactionParams,
|
|
7
6
|
type GenericTransferParams,
|
|
8
7
|
SKConfig,
|
|
9
8
|
SwapKitError,
|
|
@@ -14,6 +13,12 @@ import { Client, type Payment, Wallet, isValidAddress, xrpToDrops } from "xrpl";
|
|
|
14
13
|
|
|
15
14
|
export type RippleWallet = Awaited<ReturnType<typeof getRippleToolbox>>;
|
|
16
15
|
|
|
16
|
+
export { hashes, type Transaction } from "xrpl";
|
|
17
|
+
|
|
18
|
+
const RIPPLE_ERROR_CODES = {
|
|
19
|
+
ACCOUNT_NOT_FOUND: 19,
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
17
22
|
// Note: Ripple seeds generate a single address, no derivation path/index support.
|
|
18
23
|
function createSigner(phrase: string): ChainSigner<Transaction, { tx_blob: string; hash: string }> {
|
|
19
24
|
const wallet = Wallet.fromMnemonic(phrase);
|
|
@@ -65,11 +70,7 @@ export const getRippleToolbox = async (params: RippleToolboxParams = {}) => {
|
|
|
65
70
|
const addr = address || (await getAddress());
|
|
66
71
|
|
|
67
72
|
try {
|
|
68
|
-
await client.
|
|
69
|
-
const accountInfo = await client.request({
|
|
70
|
-
command: "account_info",
|
|
71
|
-
account: addr,
|
|
72
|
-
});
|
|
73
|
+
const accountInfo = await client.request({ command: "account_info", account: addr });
|
|
73
74
|
|
|
74
75
|
const balance = accountInfo.result.account_data.Balance;
|
|
75
76
|
|
|
@@ -82,7 +83,7 @@ export const getRippleToolbox = async (params: RippleToolboxParams = {}) => {
|
|
|
82
83
|
];
|
|
83
84
|
} catch (error) {
|
|
84
85
|
// empty account
|
|
85
|
-
if ((error as any).data.error_code ===
|
|
86
|
+
if ((error as any).data.error_code === RIPPLE_ERROR_CODES.ACCOUNT_NOT_FOUND) {
|
|
86
87
|
return [
|
|
87
88
|
AssetValue.from({
|
|
88
89
|
chain: Chain.Ripple,
|
|
@@ -111,10 +112,7 @@ export const getRippleToolbox = async (params: RippleToolboxParams = {}) => {
|
|
|
111
112
|
recipient,
|
|
112
113
|
memo,
|
|
113
114
|
sender,
|
|
114
|
-
}:
|
|
115
|
-
if (!signer) {
|
|
116
|
-
throw new SwapKitError({ errorKey: "toolbox_ripple_signer_not_found" });
|
|
117
|
-
}
|
|
115
|
+
}: { assetValue: AssetValue; recipient: string; sender?: string; memo?: string }) => {
|
|
118
116
|
if (!rippleValidateAddress(recipient)) {
|
|
119
117
|
throw new SwapKitError({ errorKey: "core_transaction_invalid_recipient_address" });
|
|
120
118
|
}
|
|
@@ -152,22 +150,15 @@ export const getRippleToolbox = async (params: RippleToolboxParams = {}) => {
|
|
|
152
150
|
|
|
153
151
|
const broadcastTransaction = async (signedTxHex: string) => {
|
|
154
152
|
const submitResult = await client.submitAndWait(signedTxHex);
|
|
155
|
-
|
|
156
|
-
const result: any = submitResult.result;
|
|
153
|
+
const result = submitResult.result;
|
|
157
154
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Access hash from tx_json if available, otherwise fallback to result.hash
|
|
161
|
-
return result.tx_json?.hash || result.hash;
|
|
155
|
+
if (result.validated) {
|
|
156
|
+
return result.hash;
|
|
162
157
|
}
|
|
163
158
|
|
|
164
|
-
const message = result.engine_result_message || "Unknown error";
|
|
165
|
-
const code = result.engine_result || "Unknown code";
|
|
166
|
-
|
|
167
159
|
throw new SwapKitError({
|
|
168
160
|
errorKey: "toolbox_ripple_broadcast_error",
|
|
169
|
-
info: { chain: Chain.Ripple
|
|
170
|
-
// Remove explicit message when using object format
|
|
161
|
+
info: { chain: Chain.Ripple },
|
|
171
162
|
});
|
|
172
163
|
};
|
|
173
164
|
|
|
@@ -181,9 +172,7 @@ export const getRippleToolbox = async (params: RippleToolboxParams = {}) => {
|
|
|
181
172
|
return broadcastTransaction(signedTx.tx_blob);
|
|
182
173
|
};
|
|
183
174
|
|
|
184
|
-
|
|
185
|
-
// For now, let's assume connection is managed outside or persists.
|
|
186
|
-
// const disconnect = () => client.disconnect();
|
|
175
|
+
const disconnect = () => client.disconnect();
|
|
187
176
|
|
|
188
177
|
return {
|
|
189
178
|
// Signer related
|
|
@@ -198,6 +187,6 @@ export const getRippleToolbox = async (params: RippleToolboxParams = {}) => {
|
|
|
198
187
|
broadcastTransaction,
|
|
199
188
|
transfer,
|
|
200
189
|
estimateTransactionFee,
|
|
201
|
-
|
|
190
|
+
disconnect,
|
|
202
191
|
};
|
|
203
192
|
};
|
package/src/solana/toolbox.ts
CHANGED
|
@@ -22,10 +22,79 @@ import {
|
|
|
22
22
|
import { P } from "ts-pattern";
|
|
23
23
|
import { match } from "ts-pattern";
|
|
24
24
|
import type { SolanaCreateTransactionParams, SolanaProvider, SolanaTransferParams } from ".";
|
|
25
|
-
import { getBalance } from "../utils";
|
|
26
25
|
|
|
27
26
|
type SolanaSigner = SolanaProvider | Signer;
|
|
28
27
|
|
|
28
|
+
type TokenMetadata = {
|
|
29
|
+
name: string;
|
|
30
|
+
symbol: string;
|
|
31
|
+
decimals: number;
|
|
32
|
+
logoURI?: string;
|
|
33
|
+
tags?: string[];
|
|
34
|
+
daily_volume?: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
async function fetchTokenMetaData(mintAddress: string): Promise<TokenMetadata | null> {
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(`https://lite-api.jup.ag/tokens/v1/token/${mintAddress}`);
|
|
40
|
+
if (!response.ok) return null;
|
|
41
|
+
return await response.json();
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function getSolanaBalance(address: string) {
|
|
48
|
+
const connection = await getConnection();
|
|
49
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
50
|
+
const { TOKEN_PROGRAM_ID } = await import("@solana/spl-token");
|
|
51
|
+
const publicKey = new PublicKey(address);
|
|
52
|
+
|
|
53
|
+
const balances: AssetValue[] = [];
|
|
54
|
+
|
|
55
|
+
// Get SOL balance
|
|
56
|
+
const solBalance = await connection.getBalance(publicKey);
|
|
57
|
+
if (solBalance > 0) {
|
|
58
|
+
balances.push(
|
|
59
|
+
AssetValue.from({
|
|
60
|
+
chain: Chain.Solana,
|
|
61
|
+
value: solBalance,
|
|
62
|
+
fromBaseDecimal: BaseDecimal[Chain.Solana],
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Get token balances
|
|
68
|
+
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(publicKey, {
|
|
69
|
+
programId: TOKEN_PROGRAM_ID,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
for (const { account } of tokenAccounts.value) {
|
|
73
|
+
const tokenInfo = account.data.parsed.info;
|
|
74
|
+
const mintAddress = tokenInfo.mint;
|
|
75
|
+
const amount = tokenInfo.tokenAmount.amount;
|
|
76
|
+
|
|
77
|
+
if (Number(amount) === 0) continue;
|
|
78
|
+
|
|
79
|
+
// Fetch token metadata from Jupiter
|
|
80
|
+
const metadata = await fetchTokenMetaData(mintAddress);
|
|
81
|
+
const symbol = metadata?.symbol || "UNKNOWN";
|
|
82
|
+
const decimals = metadata?.decimals || tokenInfo.tokenAmount.decimals;
|
|
83
|
+
|
|
84
|
+
balances.push(
|
|
85
|
+
AssetValue.from({
|
|
86
|
+
chain: Chain.Solana,
|
|
87
|
+
symbol,
|
|
88
|
+
address: mintAddress,
|
|
89
|
+
value: amount,
|
|
90
|
+
fromBaseDecimal: decimals,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return balances;
|
|
96
|
+
}
|
|
97
|
+
|
|
29
98
|
export async function getSolanaAddressValidator() {
|
|
30
99
|
const { PublicKey } = await import("@solana/web3.js");
|
|
31
100
|
|
|
@@ -68,7 +137,11 @@ export async function getSolanaToolbox(
|
|
|
68
137
|
getPubkeyFromAddress,
|
|
69
138
|
createTransaction: createTransaction(getConnection),
|
|
70
139
|
createTransactionFromInstructions,
|
|
71
|
-
getBalance:
|
|
140
|
+
getBalance: (addressParam?: string) => {
|
|
141
|
+
const address = addressParam || getAddress();
|
|
142
|
+
if (!address) throw new SwapKitError("core_wallet_connection_not_found");
|
|
143
|
+
return getSolanaBalance(address);
|
|
144
|
+
},
|
|
72
145
|
transfer: transfer(getConnection, signer),
|
|
73
146
|
broadcastTransaction: broadcastTransaction(getConnection),
|
|
74
147
|
getAddressValidator: getSolanaAddressValidator,
|
|
@@ -53,7 +53,7 @@ export function getSubstrateToolbox<T extends SubstrateChain>(chain: T, params?:
|
|
|
53
53
|
return PolkadotToolbox(params);
|
|
54
54
|
}
|
|
55
55
|
default:
|
|
56
|
-
throw new
|
|
56
|
+
throw new SwapKitError("toolbox_substrate_not_supported", { chain });
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const trc20ABI = [
|
|
2
|
+
{
|
|
3
|
+
constant: true,
|
|
4
|
+
inputs: [{ name: "_owner", type: "address" }],
|
|
5
|
+
name: "balanceOf",
|
|
6
|
+
outputs: [{ name: "balance", type: "uint256" }],
|
|
7
|
+
type: "function",
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
constant: false,
|
|
11
|
+
inputs: [
|
|
12
|
+
{ name: "_to", type: "address" },
|
|
13
|
+
{ name: "_value", type: "uint256" },
|
|
14
|
+
],
|
|
15
|
+
name: "transfer",
|
|
16
|
+
outputs: [{ name: "success", type: "bool" }],
|
|
17
|
+
type: "function",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
constant: true,
|
|
21
|
+
inputs: [],
|
|
22
|
+
name: "decimals",
|
|
23
|
+
outputs: [{ name: "", type: "uint8" }],
|
|
24
|
+
type: "function",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
constant: true,
|
|
28
|
+
inputs: [],
|
|
29
|
+
name: "symbol",
|
|
30
|
+
outputs: [{ name: "", type: "string" }],
|
|
31
|
+
type: "function",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
constant: true,
|
|
35
|
+
inputs: [],
|
|
36
|
+
name: "name",
|
|
37
|
+
outputs: [{ name: "", type: "string" }],
|
|
38
|
+
type: "function",
|
|
39
|
+
},
|
|
40
|
+
] as const;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createTronToolbox,
|
|
3
|
+
getTronAddressValidator,
|
|
4
|
+
getTronPrivateKeyFromMnemonic,
|
|
5
|
+
} from "./toolbox";
|
|
6
|
+
export type {
|
|
7
|
+
TronSigner,
|
|
8
|
+
TronToolboxOptions,
|
|
9
|
+
TronTransferParams,
|
|
10
|
+
TronContract,
|
|
11
|
+
TronTransaction,
|
|
12
|
+
} from "./types";
|
|
13
|
+
export { trc20ABI } from "./helpers/trc20.abi";
|
|
14
|
+
|
|
15
|
+
import type { createTronToolbox } from "./toolbox.js";
|
|
16
|
+
export type TronWallet = Awaited<ReturnType<typeof createTronToolbox>>;
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AssetValue,
|
|
3
|
+
Chain,
|
|
4
|
+
NetworkDerivationPath,
|
|
5
|
+
SKConfig,
|
|
6
|
+
SwapKitError,
|
|
7
|
+
derivationPathToString,
|
|
8
|
+
updateDerivationPath,
|
|
9
|
+
warnOnce,
|
|
10
|
+
} from "@swapkit/helpers";
|
|
11
|
+
import { TronWeb } from "tronweb";
|
|
12
|
+
import { P, match } from "ts-pattern";
|
|
13
|
+
|
|
14
|
+
import { trc20ABI } from "./helpers/trc20.abi.js";
|
|
15
|
+
import type {
|
|
16
|
+
TronSignedTransaction,
|
|
17
|
+
TronSigner,
|
|
18
|
+
TronToolboxOptions,
|
|
19
|
+
TronTransaction,
|
|
20
|
+
TronTransferParams,
|
|
21
|
+
} from "./types.js";
|
|
22
|
+
|
|
23
|
+
export async function getTronAddressValidator() {
|
|
24
|
+
const { TronWeb } = await import("tronweb");
|
|
25
|
+
return (address: string) => TronWeb.isAddress(address);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function getTronPrivateKeyFromMnemonic({
|
|
29
|
+
phrase,
|
|
30
|
+
derivationPath: customPath,
|
|
31
|
+
index,
|
|
32
|
+
}: {
|
|
33
|
+
phrase: string;
|
|
34
|
+
derivationPath?: string;
|
|
35
|
+
index?: number;
|
|
36
|
+
}) {
|
|
37
|
+
const derivationPathToUse =
|
|
38
|
+
customPath ||
|
|
39
|
+
derivationPathToString(
|
|
40
|
+
updateDerivationPath(NetworkDerivationPath[Chain.Tron], { index: index || 0 }),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const { HDKey } = await import("@scure/bip32");
|
|
44
|
+
const { mnemonicToSeedSync } = await import("@scure/bip39");
|
|
45
|
+
|
|
46
|
+
const seed = mnemonicToSeedSync(phrase);
|
|
47
|
+
const hdKey = HDKey.fromMasterSeed(seed);
|
|
48
|
+
const derived = hdKey.derive(derivationPathToUse);
|
|
49
|
+
|
|
50
|
+
if (!derived.privateKey) {
|
|
51
|
+
throw new SwapKitError("toolbox_tron_no_signer");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Buffer.from(derived.privateKey).toString("hex");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function createKeysForPath({
|
|
58
|
+
phrase,
|
|
59
|
+
derivationPath,
|
|
60
|
+
}: {
|
|
61
|
+
phrase: string;
|
|
62
|
+
derivationPath: string;
|
|
63
|
+
}): Promise<TronSigner> {
|
|
64
|
+
const { HDKey } = await import("@scure/bip32");
|
|
65
|
+
const { mnemonicToSeedSync } = await import("@scure/bip39");
|
|
66
|
+
|
|
67
|
+
const seed = mnemonicToSeedSync(phrase);
|
|
68
|
+
const hdKey = HDKey.fromMasterSeed(seed);
|
|
69
|
+
const derived = hdKey.derive(derivationPath);
|
|
70
|
+
|
|
71
|
+
if (!derived.privateKey) {
|
|
72
|
+
throw new SwapKitError("toolbox_tron_no_signer");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Convert private key to hex string for TronWeb
|
|
76
|
+
const privateKeyHex = Buffer.from(derived.privateKey).toString("hex");
|
|
77
|
+
|
|
78
|
+
// Create TronWeb instance with the derived private key
|
|
79
|
+
const tronWebWithKey = new TronWeb({
|
|
80
|
+
fullHost: SKConfig.get("rpcUrls")[Chain.Tron],
|
|
81
|
+
privateKey: privateKeyHex,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const address = tronWebWithKey.address.fromPrivateKey(privateKeyHex);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
getAddress: () => Promise.resolve(typeof address === "string" ? address : ""),
|
|
88
|
+
signTransaction: async (transaction: TronTransaction) => {
|
|
89
|
+
const signedTx = await tronWebWithKey.trx.sign(transaction, privateKeyHex);
|
|
90
|
+
return signedTx;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
|
|
96
|
+
// Always get configuration from SKConfig
|
|
97
|
+
const rpcUrl = SKConfig.get("rpcUrls")[Chain.Tron];
|
|
98
|
+
// Note: TRON API key support can be added to SKConfig apiKeys when needed
|
|
99
|
+
const headers = undefined; // No API key needed for basic TronGrid access
|
|
100
|
+
|
|
101
|
+
const tronWeb = new TronWeb({
|
|
102
|
+
fullHost: rpcUrl,
|
|
103
|
+
headers,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Handle derivation path and index
|
|
107
|
+
const index = "index" in options ? options.index || 0 : 0;
|
|
108
|
+
const derivationPath = derivationPathToString(
|
|
109
|
+
"derivationPath" in options && options.derivationPath
|
|
110
|
+
? options.derivationPath
|
|
111
|
+
: updateDerivationPath(NetworkDerivationPath[Chain.Tron], { index }),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Create signer based on options using pattern matching
|
|
115
|
+
const signer: TronSigner | undefined = await match(options)
|
|
116
|
+
.with({ phrase: P.string }, async ({ phrase }) => createKeysForPath({ phrase, derivationPath }))
|
|
117
|
+
.with({ signer: P.any }, ({ signer }) => Promise.resolve(signer as TronSigner))
|
|
118
|
+
.otherwise(() => Promise.resolve(undefined));
|
|
119
|
+
|
|
120
|
+
const getAddress = async () => {
|
|
121
|
+
if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
|
|
122
|
+
return await signer.getAddress();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const validateAddress = (address: string) => {
|
|
126
|
+
return tronWeb.isAddress(address);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const getContractAddress = (assetValue: AssetValue) => {
|
|
130
|
+
// Use asset.address for TRC20 contracts instead of parsing string
|
|
131
|
+
return assetValue.address;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const calculateFeeLimit = () => {
|
|
135
|
+
return 100_000_000; // 100 TRX in SUN
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const getBalance = async (address: string, scamFilter = true) => {
|
|
139
|
+
const { getBalance: getBalanceFromApi } = await import("../utils.js");
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
// Use SwapKit API for comprehensive balance fetching (includes TRX + TRC20 tokens)
|
|
143
|
+
const apiBalances = await getBalanceFromApi(Chain.Tron)(address, scamFilter);
|
|
144
|
+
|
|
145
|
+
// If API returns balances, use those
|
|
146
|
+
if (apiBalances.length > 0) {
|
|
147
|
+
return apiBalances;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Fallback to on-chain TRX balance if API fails or returns empty
|
|
151
|
+
const trxBalanceInSun = await tronWeb.trx.getBalance(address);
|
|
152
|
+
return [
|
|
153
|
+
AssetValue.from({
|
|
154
|
+
chain: Chain.Tron,
|
|
155
|
+
value: trxBalanceInSun,
|
|
156
|
+
fromBaseDecimal: 6, // TRX has 6 decimals
|
|
157
|
+
}),
|
|
158
|
+
];
|
|
159
|
+
} catch (error) {
|
|
160
|
+
warnOnce(
|
|
161
|
+
true,
|
|
162
|
+
`Failed to get Tron balance for ${address}: ${error instanceof Error ? error.message : error}`,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Final fallback: try to get just the native TRX balance
|
|
166
|
+
try {
|
|
167
|
+
const trxBalanceInSun = await tronWeb.trx.getBalance(address);
|
|
168
|
+
return [
|
|
169
|
+
AssetValue.from({
|
|
170
|
+
chain: Chain.Tron,
|
|
171
|
+
value: trxBalanceInSun,
|
|
172
|
+
fromBaseDecimal: 6,
|
|
173
|
+
}),
|
|
174
|
+
];
|
|
175
|
+
} catch (fallbackError) {
|
|
176
|
+
warnOnce(
|
|
177
|
+
true,
|
|
178
|
+
`Failed to get native TRX balance for ${address}: ${fallbackError instanceof Error ? fallbackError.message : fallbackError}`,
|
|
179
|
+
);
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const transfer = async ({ recipient, assetValue, memo }: TronTransferParams) => {
|
|
186
|
+
if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
|
|
187
|
+
|
|
188
|
+
const from = await getAddress();
|
|
189
|
+
const isNative = assetValue.isGasAsset;
|
|
190
|
+
|
|
191
|
+
if (isNative) {
|
|
192
|
+
// Native TRX Transfer (amount in SUN - base units)
|
|
193
|
+
const transaction = await tronWeb.transactionBuilder.sendTrx(
|
|
194
|
+
recipient,
|
|
195
|
+
assetValue.getBaseValue("number"),
|
|
196
|
+
from,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Add memo if provided
|
|
200
|
+
if (memo) {
|
|
201
|
+
const transactionWithMemo = await tronWeb.transactionBuilder.addUpdateData(
|
|
202
|
+
transaction,
|
|
203
|
+
memo,
|
|
204
|
+
"utf8",
|
|
205
|
+
);
|
|
206
|
+
const signedTx = await signer.signTransaction(transactionWithMemo);
|
|
207
|
+
const { txid } = await tronWeb.trx.sendRawTransaction(signedTx);
|
|
208
|
+
return txid;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const signedTx = await signer.signTransaction(transaction);
|
|
212
|
+
const { txid } = await tronWeb.trx.sendRawTransaction(signedTx);
|
|
213
|
+
return txid;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// TRC20 Token Transfer
|
|
217
|
+
const contractAddress = getContractAddress(assetValue);
|
|
218
|
+
if (!contractAddress) {
|
|
219
|
+
throw new SwapKitError("toolbox_tron_invalid_token_identifier", {
|
|
220
|
+
identifier: assetValue.toString(),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const feeLimit = calculateFeeLimit();
|
|
225
|
+
const contract = await tronWeb.contract(trc20ABI, contractAddress);
|
|
226
|
+
|
|
227
|
+
if (!contract.methods?.transfer) {
|
|
228
|
+
throw new SwapKitError("toolbox_tron_token_transfer_failed");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const txid = await contract.methods
|
|
232
|
+
.transfer(recipient, assetValue.getBaseValue("string"))
|
|
233
|
+
.send({
|
|
234
|
+
from,
|
|
235
|
+
feeLimit,
|
|
236
|
+
callValue: 0,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!txid) {
|
|
240
|
+
throw new SwapKitError("toolbox_tron_token_transfer_failed");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return txid;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const estimateTransactionFee = ({ assetValue }: TronTransferParams) => {
|
|
247
|
+
const isNative = assetValue.isGasAsset;
|
|
248
|
+
|
|
249
|
+
if (isNative) {
|
|
250
|
+
// Native TRX transfers typically consume bandwidth, which is free up to daily limit
|
|
251
|
+
// Return a minimal fee estimation for bandwidth cost
|
|
252
|
+
return AssetValue.from({ chain: Chain.Tron, value: 1 }); // 1 TRX
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// TRC20 transfers consume energy, estimate higher fee
|
|
256
|
+
return AssetValue.from({ chain: Chain.Tron, value: 10 }); // 10 TRX
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const createTransaction = async (params: TronTransferParams) => {
|
|
260
|
+
if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
|
|
261
|
+
|
|
262
|
+
const { recipient, assetValue, memo } = params;
|
|
263
|
+
const from = await getAddress();
|
|
264
|
+
const isNative = assetValue.isGasAsset;
|
|
265
|
+
|
|
266
|
+
if (isNative) {
|
|
267
|
+
const transaction = await tronWeb.transactionBuilder.sendTrx(
|
|
268
|
+
recipient,
|
|
269
|
+
assetValue.getBaseValue("number"),
|
|
270
|
+
from,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
if (memo) {
|
|
274
|
+
return tronWeb.transactionBuilder.addUpdateData(transaction, memo, "utf8");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return transaction;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// For TRC20, we would need to build the transaction manually
|
|
281
|
+
// This is a simplified version - in practice, you'd build the contract call transaction
|
|
282
|
+
const contractAddress = getContractAddress(assetValue);
|
|
283
|
+
if (!contractAddress) {
|
|
284
|
+
throw new SwapKitError("toolbox_tron_invalid_token_identifier", {
|
|
285
|
+
identifier: assetValue.toString(),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Build TRC20 transfer transaction
|
|
290
|
+
const functionSelector = "transfer(address,uint256)";
|
|
291
|
+
const parameter = [
|
|
292
|
+
{ type: "address", value: recipient },
|
|
293
|
+
{ type: "uint256", value: assetValue.getBaseValue("string") },
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
const result = await tronWeb.transactionBuilder.triggerSmartContract(
|
|
297
|
+
contractAddress,
|
|
298
|
+
functionSelector,
|
|
299
|
+
{},
|
|
300
|
+
parameter,
|
|
301
|
+
from,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
return result.transaction;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const signTransaction = async (transaction: TronTransaction) => {
|
|
308
|
+
if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
|
|
309
|
+
return await signer.signTransaction(transaction);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const broadcastTransaction = async (signedTx: TronSignedTransaction) => {
|
|
313
|
+
const { txid } = await tronWeb.trx.sendRawTransaction(signedTx);
|
|
314
|
+
return txid;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
tronWeb,
|
|
319
|
+
getAddress,
|
|
320
|
+
validateAddress,
|
|
321
|
+
getBalance,
|
|
322
|
+
transfer,
|
|
323
|
+
estimateTransactionFee,
|
|
324
|
+
createTransaction,
|
|
325
|
+
signTransaction,
|
|
326
|
+
broadcastTransaction,
|
|
327
|
+
};
|
|
328
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DerivationPathArray, GenericTransferParams } from "@swapkit/helpers";
|
|
2
|
+
import type { Contract, Types } from "tronweb";
|
|
3
|
+
|
|
4
|
+
// Re-export TronWeb types for convenience
|
|
5
|
+
export type TronTransaction = Types.Transaction;
|
|
6
|
+
export type TronContract = Contract;
|
|
7
|
+
export type TronSignedTransaction = Types.SignedTransaction;
|
|
8
|
+
|
|
9
|
+
// Signer interface compatible with TronWeb and wallet implementations
|
|
10
|
+
export interface TronSigner {
|
|
11
|
+
getAddress(): Promise<string>;
|
|
12
|
+
signTransaction(transaction: TronTransaction): Promise<TronSignedTransaction>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type TronToolboxOptions =
|
|
16
|
+
| { signer?: TronSigner }
|
|
17
|
+
| { phrase?: string; derivationPath?: DerivationPathArray; index?: number }
|
|
18
|
+
| {};
|
|
19
|
+
|
|
20
|
+
export interface TronTransferParams extends GenericTransferParams {
|
|
21
|
+
// No additional fields needed - all inherited from GenericTransferParams
|
|
22
|
+
}
|