@codegame.dev/wallet-cli
Version:
A CLI tool for managing wallets across multiple blockchains, supporting operations like wallet creation, balance checking, transfers, and fee estimation.
259 lines (215 loc) • 8.12 kB
text/typescript
import { ethers, formatUnits } from "ethers";
import { TronWeb } from 'tronweb'
import { EstimateFeeParams, EstimateFeeResponse, GetBalanceParams, GetBalanceResponse, TransferParams, TransferResponse } from ".";
import { sleep } from "../utils/index";
const trc20Abi = [
{
"constant": true,
"inputs": [{ "name": "owner", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "to", "type": "address" },
{ "name": "amount", "type": "uint256" }
],
"name": "transfer",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint8" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "from", "type": "address" },
{ "indexed": true, "name": "to", "type": "address" },
{ "indexed": false, "name": "value", "type": "uint256" }
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "owner", "type": "address" },
{ "indexed": true, "name": "spender", "type": "address" },
{ "indexed": false, "name": "value", "type": "uint256" }
],
"name": "Approval",
"type": "event"
}
]
export const getBalance = async (params: GetBalanceParams): Promise<GetBalanceResponse> => {
const tronWeb = new TronWeb({
fullHost: params.jsonRpcProvider,
});
tronWeb.setAddress(params.address)
if (params.tokenAddress) {
const tokenContract = tronWeb.contract(trc20Abi, params.tokenAddress);
const decimals = await tokenContract.decimals().call()
await sleep(params.delay)
const tokenBalance = formatUnits(await tokenContract.balanceOf(params.address).call(), decimals);
return {
success: true,
token_balance: tokenBalance,
}
} else {
const coinBalance = formatUnits(await tronWeb.trx.getBalance(params.address), params.chainDecimals);
return {
success: true,
coin_balance: coinBalance,
}
}
}
export const transfer = async (params: TransferParams): Promise<TransferResponse> => {
const tronWeb = new TronWeb({
fullHost: params.jsonRpcProvider,
});
tronWeb.setPrivateKey(Buffer.from(params.secretKey).toString('hex'))
tronWeb.setAddress(params.fromAddress)
if (params.tokenAddress) {
const tokenContract = tronWeb.contract(trc20Abi, params.tokenAddress);
const decimals = Number(await tokenContract.decimals().call())
await sleep(params.delay)
const amount = Number(params.amount).toFixed(decimals)
const amountInSun = ethers.parseUnits(amount, decimals);
const transaction = await tokenContract.transfer(params.toAddress, amountInSun).send()
const hash = transaction
const fee = "0"
return {
success: true,
hash,
fee,
}
} else {
const amount = Number(params.amount).toFixed(params.chainDecimals)
const amountInSun = Number(ethers.parseUnits(amount, params.chainDecimals));
const transaction: any = await tronWeb.trx.sendTransaction(params.toAddress, amountInSun);
const hash = transaction.txid
const fee = "0"
return {
success: true,
hash,
fee,
}
}
}
export const estimateFee = async (params: EstimateFeeParams): Promise<EstimateFeeResponse> => {
const tronWeb = new TronWeb({
fullHost: params.jsonRpcProvider,
});
tronWeb.setAddress(params.fromAddress)
if (params.tokenAddress) {
const tokenContract = tronWeb.contract(trc20Abi, params.tokenAddress);
const decimals = await tokenContract.decimals().call()
await sleep(params.delay)
const amountInSun = ethers.parseUnits("0", decimals);
const result = await tronWeb.transactionBuilder.triggerConstantContract(
params.tokenAddress,
'transfer(address,uint256)',
{},
[
{ type: 'address', value: params.toAddress },
{ type: 'uint256', value: amountInSun }
],
params.fromAddress,
);
await sleep(params.delay)
const energyLimit = (result.energy_used || result.energy_required || 0) + (result.energy_penalty || 0)
const currentTimestamp = Date.now()
const energyPrices = (await tronWeb.trx.getEnergyPrices()).split(",").map(part => part.split(':').map(v => Number(v)))
energyPrices.reverse()
await sleep(params.delay)
const energyPrice = (energyPrices.find(part => currentTimestamp <= part[0]) || energyPrices[0])[1]
if (!energyPrice) {
return {
success: false,
code: 'empty-gas-price',
message: "Error in fetching energy price",
}
}
const energyCostInSum = energyLimit * energyPrice;
const energyCost = formatUnits(energyCostInSum, params.chainDecimals);
const bandwidthLimit = 400
const bandwidthPrices = (await tronWeb.trx.getBandwidthPrices()).split(",").map(part => part.split(':').map(v => Number(v)))
bandwidthPrices.reverse()
const bandwidthPrice = (bandwidthPrices.find(part => currentTimestamp <= part[0]) || bandwidthPrices[0])[1]
if (!bandwidthPrice) {
return {
success: false,
code: 'empty-gas-price',
message: "Error in fetching bandwidth price",
}
}
const bandwidthCostInSum = bandwidthLimit * bandwidthPrice;
const bandwidthCost = formatUnits(bandwidthCostInSum, params.chainDecimals);
const gasCost = Number(bandwidthCost) + Number(energyCost)
return {
success: true,
fee: String(gasCost || 0),
}
} else {
const bandwidthLimit = 400
const bandwidthPrices = (await tronWeb.trx.getBandwidthPrices()).split(",").map(part => part.split(':').map(v => Number(v)))
bandwidthPrices.reverse()
const currentTimestamp = Date.now()
const bandwidthPrice = (bandwidthPrices.find(part => currentTimestamp <= part[0]) || bandwidthPrices[0])[1]
if (!bandwidthPrice) {
return {
success: false,
code: 'empty-gas-price',
message: "Error in fetching bandwidth price"
}
}
const bandwidthCostInSum = bandwidthLimit * bandwidthPrice;
const bandwidthCost = formatUnits(bandwidthCostInSum, params.chainDecimals);
const gasCost = bandwidthCost
return {
success: true,
fee: gasCost,
}
}
}
export default { getBalance, transfer, estimateFee }