@ledgerhq/coin-tron
Version:
Ledger Tron Coin integration
222 lines • 10.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const currencies_1 = require("@ledgerhq/coin-module-framework/currencies");
const errors_1 = require("@ledgerhq/errors");
const account_1 = require("@ledgerhq/ledger-wallet-framework/account");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const sumBy_1 = __importDefault(require("lodash/sumBy"));
const constants_1 = require("../logic/constants");
const network_1 = require("../network");
const errors_2 = require("../types/errors");
const getEstimateFees_1 = __importDefault(require("./getEstimateFees"));
const getTransactionStatus = async (acc, transaction) => {
const errors = {};
const warnings = {};
const { family, mode, recipient, resource, votes, useAllAmount = false } = transaction;
const tokenAccount = !transaction.subAccountId
? null
: acc.subAccounts && acc.subAccounts.find(ta => ta.id === transaction.subAccountId);
const account = tokenAccount || acc;
const isContractInteraction = (await (0, network_1.fetchTronContract)(tokenAccount ? tokenAccount.token.contractAddress : recipient)) !==
undefined;
if (mode === "send" && !recipient) {
errors.recipient = new errors_1.RecipientRequired();
}
if (["send", "unDelegateResource", "legacyUnfreeze"].includes(mode)) {
if (recipient === acc.freshAddress) {
errors.recipient = new errors_1.InvalidAddressBecauseDestinationIsAlsoSource();
}
else if (recipient && !(await (0, network_1.validateAddress)(recipient))) {
errors.recipient = new errors_1.InvalidAddress(undefined, {
currencyName: acc.currency.name,
});
}
else if (recipient &&
mode === "send" &&
account.type === "TokenAccount" &&
account.token.tokenType === "trc20" &&
!isContractInteraction && // send trc20 to a smart contract is allowed
(await (0, network_1.fetchTronAccount)(recipient)).length === 0) {
// send trc20 to a new account is forbidden by us (because it will not activate the account)
errors.recipient = new errors_2.TronSendTrc20ToNewAccountForbidden();
}
}
if (mode === "unfreeze") {
const { bandwidth, energy } = acc.tronResources.frozen;
if (resource === "BANDWIDTH" && transaction.amount.gt(bandwidth?.amount || new bignumber_js_1.default(0))) {
errors.resource = new errors_2.TronNoFrozenForBandwidth();
}
else if (resource === "ENERGY" && transaction.amount.gt(energy?.amount || new bignumber_js_1.default(0))) {
errors.resource = new errors_2.TronNoFrozenForEnergy();
}
}
if (mode === "legacyUnfreeze") {
const now = new Date();
const expirationDate = resource === "ENERGY"
? acc.tronResources.legacyFrozen.energy?.expiredAt
: acc.tronResources.legacyFrozen.bandwidth?.expiredAt;
if (!expirationDate) {
if (resource === "BANDWIDTH") {
errors.resource = new errors_2.TronNoFrozenForBandwidth();
}
else {
errors.resource = new errors_2.TronNoFrozenForEnergy();
}
}
else if (now.getTime() < expirationDate.getTime()) {
errors.resource = new errors_2.TronLegacyUnfreezeNotExpired();
}
}
if (mode === "withdrawExpireUnfreeze") {
const now = new Date();
if ((!acc.tronResources.unFrozen.bandwidth ||
acc.tronResources.unFrozen.bandwidth.length === 0) &&
(!acc.tronResources.unFrozen.energy || acc.tronResources.unFrozen.energy.length === 0)) {
errors.resource = new errors_2.TronNoUnfrozenResource();
}
else {
const unfreezingResources = [
...(acc.tronResources.unFrozen.bandwidth ?? []),
...(acc.tronResources.unFrozen.energy ?? []),
];
const hasNoExpiredResource = !unfreezingResources.some(unfrozen => unfrozen.expireTime.getTime() <= now.getTime());
if (hasNoExpiredResource) {
const closestExpireTime = unfreezingResources.reduce((closest, current) => {
if (!closest) {
return current;
}
const closestTimeDifference = Math.abs(closest.expireTime.getTime() - now.getTime());
const currentTimeDifference = Math.abs(current.expireTime.getTime() - now.getTime());
return currentTimeDifference < closestTimeDifference ? current : closest;
});
errors.resource = new errors_2.TronUnfreezeNotExpired(undefined, {
time: closestExpireTime.expireTime.toISOString(),
});
}
}
}
if (mode === "unDelegateResource" && resource && acc.tronResources) {
const delegatedResourceAmount = await (0, network_1.getDelegatedResource)(acc, transaction, resource);
if (delegatedResourceAmount.lt(transaction.amount)) {
errors.resource = new errors_2.TronInvalidUnDelegateResourceAmount();
}
}
if (mode === "vote") {
if (votes.length === 0) {
errors.vote = new errors_2.TronVoteRequired();
}
else {
const superRepresentatives = await (0, network_1.getTronSuperRepresentatives)();
const isValidVoteCounts = votes.every(v => v.voteCount > 0);
const isValidAddresses = votes.every(v => superRepresentatives.some(s => s.address === v.address));
if (!isValidAddresses) {
errors.vote = new errors_1.InvalidAddress("", {
currencyName: acc.currency.name,
});
}
else if (!isValidVoteCounts) {
errors.vote = new errors_2.TronInvalidVoteCount();
}
else {
const totalVoteCount = (0, sumBy_1.default)(votes, "voteCount");
const tronPower = (acc.tronResources && acc.tronResources.tronPower) || 0;
if (totalVoteCount > tronPower) {
errors.vote = new errors_2.TronNotEnoughTronPower();
}
}
}
}
if (mode === "claimReward") {
const lastRewardOp = account.operations.find(o => o.type === "REWARD");
const claimableRewardDate = lastRewardOp
? new Date(lastRewardOp.date.getTime() + 24 * 60 * 60 * 1000) // date + 24 hours
: new Date();
if (acc.tronResources && acc.tronResources.unwithdrawnReward.eq(0)) {
errors.reward = new errors_2.TronNoReward();
}
else if (lastRewardOp && claimableRewardDate.valueOf() > new Date().valueOf()) {
errors.reward = new errors_2.TronRewardNotAvailable("Reward is not claimable", {
until: claimableRewardDate.toISOString(),
});
}
}
const estimatedFees = Object.entries(errors).length > 0
? new bignumber_js_1.default(0)
: await (0, getEstimateFees_1.default)(acc, transaction, tokenAccount);
const balance = account.type === "Account"
? bignumber_js_1.default.max(0, account.spendableBalance.minus(estimatedFees))
: account.balance;
const amount = useAllAmount ? balance : transaction.amount;
const amountSpent = ["send", "freeze", "undelegateResource"].includes(mode)
? amount
: new bignumber_js_1.default(0);
if (mode === "freeze" && amount.lt(constants_1.ONE_TRX)) {
errors.amount = new errors_2.TronInvalidFreezeAmount();
}
// fees are applied in the parent only (TRX)
const totalSpent = account.type === "Account" ? amountSpent.plus(estimatedFees) : amountSpent;
if (["send", "freeze"].includes(mode)) {
if (amount.eq(0)) {
errors.amount = new errors_1.AmountRequired();
}
if (amountSpent.eq(0)) {
errors.amount = useAllAmount ? new errors_1.NotEnoughBalance() : new errors_1.AmountRequired();
}
else if (amount.gt(balance)) {
errors.amount = new errors_1.NotEnoughBalance();
}
const energy = (acc.tronResources && acc.tronResources.energy) || new bignumber_js_1.default(0);
// For the moment, we rely on this rule:
// Add a 'TronNotEnoughEnergy' warning only if the account sastifies theses 3 conditions:
// - no energy
// - balance is lower than 1 TRX
// - contract consumes user energy (ie: user's ratio > 0%)
if (account.type === "TokenAccount" &&
account.token.tokenType === "trc20" &&
energy.lt(47619) // temporary value corresponding to usdt trc20 energy
) {
const contractUserEnergyConsumption = await (0, network_1.getContractUserEnergyRatioConsumption)(account.token.contractAddress);
if (contractUserEnergyConsumption > 0) {
warnings.amount = new errors_2.TronNotEnoughEnergy();
}
}
}
if (!errors.recipient && estimatedFees.gt(0)) {
const fees = (0, currencies_1.formatCurrencyUnit)((0, account_1.getAccountCurrency)(acc).units[0], estimatedFees, {
showCode: true,
disableRounding: true,
});
warnings.fee = new errors_2.TronUnexpectedFees("Estimated fees", {
fees,
});
}
const parentAccountBalance = account.type === "Account" ? account.balance : acc.spendableBalance;
//
// Not enough gas check
// PTX swap uses this to support deeplink to buy additional currency
//
if (parentAccountBalance.lt(estimatedFees) || parentAccountBalance.isZero()) {
const query = new URLSearchParams({
...(acc?.id ? { account: acc.id } : {}),
});
errors.gasLimit = new errors_1.NotEnoughGas(undefined, {
fees: (0, currencies_1.formatCurrencyUnit)((0, account_1.getFeesUnit)(acc.currency), estimatedFees),
ticker: acc.currency.ticker,
cryptoName: acc.currency.name,
links: [`ledgerlive://buy?${query.toString()}`],
});
}
return Promise.resolve({
errors,
warnings,
amount: amountSpent,
estimatedFees,
totalSpent,
family,
});
};
exports.default = getTransactionStatus;
//# sourceMappingURL=getTransactionStatus.js.map