Skip to content

Commit 271afda

Browse files
committed
implemented Tron helper methods
1 parent 51349d4 commit 271afda

File tree

2 files changed

+332
-11
lines changed

2 files changed

+332
-11
lines changed

src/common/helpers/tronHelper.ts

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
import { TronWeb, utils } from 'tronweb';
2+
import { successResponse } from '../utils';
3+
import erc20Abi from '../../abis/erc20.json';
4+
import {
5+
BalancePayload,
6+
GetTransactionPayload,
7+
IGetTokenInfoPayload,
8+
ISmartContractCallPayload,
9+
TransferPayload,
10+
} from '../utils/types';
11+
12+
interface GetContract {
13+
rpcUrl?: string;
14+
apiKey?: string;
15+
privateKey?: string;
16+
contractAddress?: string;
17+
abi?: any[];
18+
}
19+
20+
const getContract = async ({
21+
contractAddress,
22+
rpcUrl,
23+
apiKey,
24+
privateKey,
25+
abi,
26+
}: GetContract) => {
27+
if (!rpcUrl) {
28+
throw new Error('RPC URL is required');
29+
}
30+
31+
let tronWeb = new TronWeb({
32+
fullHost: rpcUrl,
33+
headers: apiKey ? { 'TRON-PRO-API-KEY': apiKey } : undefined,
34+
});
35+
36+
if (privateKey) {
37+
tronWeb.setPrivateKey(privateKey);
38+
}
39+
40+
let contract;
41+
if (contractAddress) {
42+
if (privateKey) {
43+
const wallet = TronWeb.address.fromPrivateKey(privateKey);
44+
tronWeb.setAddress(wallet as string);
45+
} else {
46+
tronWeb.setAddress(contractAddress);
47+
}
48+
49+
contract = tronWeb.contract(abi || erc20Abi, contractAddress);
50+
}
51+
52+
return {
53+
contract,
54+
tronWeb,
55+
};
56+
};
57+
58+
const createWallet = (derivationPath?: string) => {
59+
const path = derivationPath || "m/44'/195'/0'/0/0";
60+
const account = TronWeb.createRandom(undefined, path);
61+
62+
return successResponse({
63+
address: account.address,
64+
privateKey: account.privateKey,
65+
mnemonic: account.mnemonic?.phrase,
66+
});
67+
};
68+
69+
const getAddressFromPrivateKey = (privateKey: string) => {
70+
const account = TronWeb.address.fromPrivateKey(privateKey);
71+
72+
return successResponse({
73+
address: account,
74+
});
75+
};
76+
77+
const generateWalletFromMnemonic = (
78+
mnemonic: string,
79+
derivationPath?: string
80+
) => {
81+
const path = derivationPath || "m/44'/195'/0'/0/0";
82+
const account = TronWeb.fromMnemonic(mnemonic, path);
83+
84+
return successResponse({
85+
address: account.address,
86+
privateKey: account.privateKey,
87+
mnemonic: mnemonic,
88+
});
89+
};
90+
91+
const getBalance = async ({
92+
rpcUrl,
93+
tokenAddress,
94+
address,
95+
}: BalancePayload) => {
96+
const { tronWeb, contract } = await getContract({
97+
rpcUrl,
98+
contractAddress: tokenAddress,
99+
});
100+
101+
try {
102+
if (contract && tokenAddress) {
103+
const balance = await contract.balanceOf(address).call();
104+
105+
return successResponse({
106+
balance: TronWeb.fromSun(Number(balance)),
107+
});
108+
}
109+
110+
const balance = await tronWeb.trx.getBalance(address);
111+
112+
return successResponse({
113+
balance: TronWeb.fromSun(balance).toString(),
114+
});
115+
} catch (error) {
116+
throw error;
117+
}
118+
};
119+
120+
const transfer = async ({
121+
privateKey,
122+
tokenAddress,
123+
rpcUrl,
124+
recipientAddress,
125+
amount,
126+
feeLimit,
127+
}: TransferPayload) => {
128+
const { tronWeb, contract } = await getContract({
129+
rpcUrl,
130+
privateKey,
131+
contractAddress: tokenAddress,
132+
});
133+
134+
try {
135+
let tx;
136+
137+
if (contract && tokenAddress) {
138+
const amountInSun = TronWeb.toSun(amount);
139+
const functionSelector = 'transfer(address,uint256)';
140+
const parameter = [
141+
{ type: 'address', value: recipientAddress },
142+
{ type: 'uint256', value: amountInSun.toString() },
143+
];
144+
145+
const txRaw = await tronWeb.transactionBuilder.triggerSmartContract(
146+
tokenAddress,
147+
functionSelector,
148+
feeLimit ? { feeLimit } : {},
149+
parameter
150+
);
151+
const signedTx = await tronWeb.trx.sign(txRaw.transaction);
152+
const result = await tronWeb.trx.sendRawTransaction(signedTx);
153+
154+
tx = result;
155+
} else {
156+
const amountInSun = TronWeb.toSun(amount);
157+
tx = await tronWeb.trx.sendTransaction(
158+
recipientAddress,
159+
Number(amountInSun.toString())
160+
);
161+
}
162+
163+
return successResponse({
164+
...tx,
165+
});
166+
} catch (error) {
167+
throw error;
168+
}
169+
};
170+
171+
const getTransaction = async ({ hash, rpcUrl }: GetTransactionPayload) => {
172+
const { tronWeb } = await getContract({ rpcUrl });
173+
174+
try {
175+
const tx = await tronWeb.trx.getTransactionInfo(hash);
176+
return successResponse({
177+
...tx,
178+
});
179+
} catch (error) {
180+
throw error;
181+
}
182+
};
183+
184+
const getTokenInfo = async ({
185+
address,
186+
rpcUrl,
187+
apiKey,
188+
}: IGetTokenInfoPayload) => {
189+
const { contract } = await getContract({
190+
contractAddress: address,
191+
rpcUrl,
192+
apiKey,
193+
});
194+
195+
if (contract) {
196+
try {
197+
const [name, symbol, decimals, totalSupply] = await Promise.all([
198+
contract.name().call(),
199+
contract.symbol().call(),
200+
contract.decimals().call(),
201+
contract.totalSupply().call(),
202+
]);
203+
204+
const data = {
205+
name,
206+
symbol,
207+
decimals: Number(decimals),
208+
address,
209+
totalSupply: TronWeb.fromSun(Number(totalSupply)).toString(),
210+
};
211+
212+
return successResponse({ ...data });
213+
} catch (error) {
214+
throw error;
215+
}
216+
}
217+
218+
throw new Error('Contract not found');
219+
};
220+
221+
const smartContractCall = async ({
222+
rpcUrl,
223+
contractAddress,
224+
privateKey,
225+
method,
226+
params = [],
227+
methodType,
228+
feeLimit,
229+
contractAbi,
230+
}: ISmartContractCallPayload) => {
231+
const { tronWeb } = await getContract({
232+
rpcUrl,
233+
contractAddress,
234+
privateKey,
235+
});
236+
237+
try {
238+
if (!contractAbi) {
239+
throw new Error('Contract ABI is required');
240+
}
241+
242+
let result;
243+
244+
if (methodType === 'read') {
245+
const functionAbi = contractAbi.find(abi => abi.name === method);
246+
const functionSelector = `${functionAbi.name}(${functionAbi.inputs
247+
.map((input: { type: string }) => input.type)
248+
.join(',')})`;
249+
250+
result = await tronWeb.transactionBuilder.triggerConstantContract(
251+
contractAddress,
252+
functionSelector,
253+
{},
254+
params
255+
);
256+
257+
const prefixedHex = result.constant_result[0].startsWith('0x')
258+
? result.constant_result[0]
259+
: `0x${result.constant_result[0]}`;
260+
261+
const decoded = utils.abi.decodeParamsV2ByABI(functionAbi, prefixedHex);
262+
result = decoded.toString();
263+
} else if (methodType === 'write') {
264+
const txRaw = await tronWeb.transactionBuilder.triggerSmartContract(
265+
contractAddress,
266+
method,
267+
feeLimit ? { feeLimit } : {},
268+
params
269+
);
270+
const signedTx = await tronWeb.trx.sign(txRaw.transaction);
271+
result = await tronWeb.trx.sendRawTransaction(signedTx);
272+
}
273+
274+
return successResponse({
275+
data: result,
276+
});
277+
} catch (error) {
278+
throw error;
279+
}
280+
};
281+
282+
export default {
283+
getBalance,
284+
createWallet,
285+
getAddressFromPrivateKey,
286+
generateWalletFromMnemonic,
287+
transfer,
288+
getTransaction,
289+
getTokenInfo,
290+
smartContractCall,
291+
};

0 commit comments

Comments
 (0)