@ledgerhq/coin-hedera
Version:
Ledger Hedera Coin integration
263 lines • 11.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTransactionStatus = void 0;
const errors_1 = require("@ledgerhq/errors");
const account_1 = require("@ledgerhq/ledger-wallet-framework/account");
const live_env_1 = require("@ledgerhq/live-env");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const invariant_1 = __importDefault(require("invariant"));
const constants_1 = require("../constants");
const errors_2 = require("../errors");
const estimateFees_1 = require("../logic/estimateFees");
const utils_1 = require("../logic/utils");
const validateMemo_1 = require("../logic/validateMemo");
const preload_data_1 = require("../preload-data");
const utils_2 = require("./utils");
async function validateRecipient(account, recipient) {
if (!recipient || recipient.length === 0) {
return new errors_1.RecipientRequired();
}
const [parsingError, parsingResult] = await (0, utils_1.safeParseAccountId)(recipient);
if (parsingError) {
return parsingError;
}
const recipientWithoutChecksum = parsingResult.accountId;
if (account.freshAddress === recipientWithoutChecksum) {
return new errors_1.InvalidAddressBecauseDestinationIsAlsoSource();
}
return null;
}
async function handleTokenAssociateTransaction(account, transaction) {
const errors = {};
const warnings = {};
const [usdRate, estimatedFees] = await Promise.all([
(0, utils_1.getCurrencyToUSDRate)(account.currency),
(0, estimateFees_1.estimateFees)({
currency: account.currency,
operationType: constants_1.HEDERA_OPERATION_TYPES.TokenAssociate,
}),
]);
const amount = (0, bignumber_js_1.default)(0);
const totalSpent = amount.plus(estimatedFees.tinybars);
const isAssociationFlow = (0, utils_1.isTokenAssociationRequired)(account, transaction.properties.token);
if (!(0, validateMemo_1.validateMemo)(transaction.memo)) {
errors.transaction = new errors_2.HederaMemoExceededSizeError();
}
if (isAssociationFlow) {
const hbarBalance = account.balance.dividedBy(10 ** account.currency.units[0].magnitude);
const currentWorthInUSD = usdRate ? hbarBalance.multipliedBy(usdRate) : new bignumber_js_1.default(0);
const requiredWorthInUSD = (0, live_env_1.getEnv)("HEDERA_TOKEN_ASSOCIATION_MIN_USD");
if (currentWorthInUSD.isLessThan(requiredWorthInUSD)) {
errors.insufficientAssociateBalance = new errors_2.HederaInsufficientFundsForAssociation("", {
requiredWorthInUSD,
});
}
}
return {
amount,
totalSpent,
estimatedFees: estimatedFees.tinybars,
errors,
warnings,
};
}
async function handleHTSTokenTransaction(account, subAccount, transaction) {
const errors = {};
const warnings = {};
const [calculatedAmount, estimatedFees] = await Promise.all([
(0, utils_2.calculateAmount)({ transaction, account }),
(0, estimateFees_1.estimateFees)({
currency: account.currency,
operationType: constants_1.HEDERA_OPERATION_TYPES.TokenTransfer,
}),
]);
const recipientError = await validateRecipient(account, transaction.recipient);
if (recipientError) {
errors.recipient = recipientError;
}
if (!(0, validateMemo_1.validateMemo)(transaction.memo)) {
errors.transaction = new errors_2.HederaMemoExceededSizeError();
}
if (!errors.recipient) {
try {
const hasRecipientTokenAssociated = await (0, utils_1.checkAccountTokenAssociationStatus)(transaction.recipient, subAccount.token);
if (!hasRecipientTokenAssociated) {
warnings.missingAssociation = new errors_2.HederaRecipientTokenAssociationRequired();
}
}
catch {
warnings.unverifiedAssociation = new errors_2.HederaRecipientTokenAssociationUnverified();
}
}
if (transaction.amount.eq(0)) {
errors.amount = new errors_1.AmountRequired();
}
if (subAccount.balance.isLessThan(calculatedAmount.totalSpent)) {
errors.amount = new errors_1.NotEnoughBalance();
}
if (account.balance.isLessThan(estimatedFees.tinybars)) {
errors.amount = new errors_1.NotEnoughBalance();
}
return {
amount: calculatedAmount.amount,
totalSpent: calculatedAmount.totalSpent,
estimatedFees: estimatedFees.tinybars,
errors,
warnings,
};
}
async function handleERC20TokenTransaction(account, subAccount, transaction) {
const errors = {};
const warnings = {
unverifiedEvmAddress: new errors_2.HederaRecipientEvmAddressVerificationRequired(),
};
const [calculatedAmount, estimatedFees] = await Promise.all([
(0, utils_2.calculateAmount)({ transaction, account }),
(0, estimateFees_1.estimateFees)({
operationType: constants_1.HEDERA_OPERATION_TYPES.ContractCall,
txIntent: {
intentType: "transaction",
type: constants_1.HEDERA_TRANSACTION_MODES.Send,
asset: {
type: "erc20",
assetReference: subAccount.token.contractAddress,
assetOwner: account.freshAddress,
},
amount: BigInt(transaction.amount.toString()),
sender: account.freshAddress,
recipient: transaction.recipient,
},
}),
]);
const recipientError = await validateRecipient(account, transaction.recipient);
if (recipientError) {
errors.recipient = recipientError;
}
if (!(0, validateMemo_1.validateMemo)(transaction.memo)) {
errors.transaction = new errors_2.HederaMemoExceededSizeError();
}
if (transaction.amount.eq(0)) {
errors.amount = new errors_1.AmountRequired();
}
if (subAccount.balance.isLessThan(calculatedAmount.totalSpent)) {
errors.amount = new errors_1.NotEnoughBalance();
}
if (account.balance.isLessThan(estimatedFees.tinybars)) {
errors.amount = new errors_1.NotEnoughBalance();
}
return {
amount: calculatedAmount.amount,
totalSpent: calculatedAmount.totalSpent,
estimatedFees: estimatedFees.tinybars,
errors,
warnings,
};
}
async function handleCoinTransaction(account, transaction) {
const errors = {};
const warnings = {};
const [calculatedAmount, estimatedFees] = await Promise.all([
(0, utils_2.calculateAmount)({ transaction, account }),
(0, estimateFees_1.estimateFees)({
currency: account.currency,
operationType: constants_1.HEDERA_OPERATION_TYPES.CryptoTransfer,
}),
]);
const recipientError = await validateRecipient(account, transaction.recipient);
if (recipientError) {
errors.recipient = recipientError;
}
if (transaction.amount.eq(0) && !transaction.useAllAmount) {
errors.amount = new errors_1.AmountRequired();
}
if (account.balance.isLessThan(calculatedAmount.totalSpent)) {
errors.amount = new errors_1.NotEnoughBalance("");
}
if (!(0, validateMemo_1.validateMemo)(transaction.memo)) {
errors.transaction = new errors_2.HederaMemoExceededSizeError();
}
return {
amount: calculatedAmount.amount,
totalSpent: calculatedAmount.totalSpent,
estimatedFees: estimatedFees.tinybars,
errors,
warnings,
};
}
async function handleStakingTransaction(account, transaction) {
(0, invariant_1.default)((0, utils_1.isStakingTransaction)(transaction), "invalid transaction properties");
const errors = {};
const warnings = {};
const { validators } = (0, preload_data_1.getCurrentHederaPreloadData)(account.currency);
const estimatedFees = await (0, estimateFees_1.estimateFees)({
operationType: constants_1.HEDERA_OPERATION_TYPES.CryptoUpdate,
currency: account.currency,
});
const amount = (0, bignumber_js_1.default)(0);
const totalSpent = amount.plus(estimatedFees.tinybars);
if (!(0, validateMemo_1.validateMemo)(transaction.memo)) {
errors.transaction = new errors_2.HederaMemoExceededSizeError();
}
if (transaction.mode === constants_1.HEDERA_TRANSACTION_MODES.Delegate ||
transaction.mode === constants_1.HEDERA_TRANSACTION_MODES.Redelegate) {
if (typeof transaction.properties?.stakingNodeId === "number") {
const isValid = validators.some(validator => {
return validator.nodeId === transaction.properties?.stakingNodeId;
});
if (!isValid) {
errors.stakingNodeId = new errors_2.HederaInvalidStakingNodeIdError();
}
}
else {
errors.missingStakingNodeId = new errors_2.HederaInvalidStakingNodeIdError("Validator must be set");
}
if (account.hederaResources?.delegation?.nodeId === transaction.properties?.stakingNodeId) {
errors.stakingNodeId = new errors_2.HederaRedundantStakingNodeIdError();
}
}
if (transaction.mode === constants_1.HEDERA_TRANSACTION_MODES.ClaimRewards) {
const rewardsToClaim = account.hederaResources?.delegation?.pendingReward || new bignumber_js_1.default(0);
const transactionFee = transaction.maxFee ?? new bignumber_js_1.default(0);
if (rewardsToClaim.lte(0)) {
errors.noRewardsToClaim = new errors_2.HederaNoStakingRewardsError();
}
if (transactionFee.gt(rewardsToClaim)) {
warnings.claimRewardsFee = new errors_1.ClaimRewardsFeesWarning();
}
}
if (account.balance.isLessThan(totalSpent)) {
errors.fee = new errors_1.NotEnoughBalance("");
}
return {
amount: new bignumber_js_1.default(0),
estimatedFees: estimatedFees.tinybars,
totalSpent,
errors,
warnings,
};
}
const getTransactionStatus = async (account, transaction) => {
const subAccount = (0, account_1.findSubAccountById)(account, transaction?.subAccountId || "");
const isHTSTokenTransaction = transaction.mode === constants_1.HEDERA_TRANSACTION_MODES.Send && subAccount?.token.tokenType === "hts";
const isERC20TokenTransaction = transaction.mode === constants_1.HEDERA_TRANSACTION_MODES.Send && subAccount?.token.tokenType === "erc20";
if ((0, utils_1.isTokenAssociateTransaction)(transaction)) {
return handleTokenAssociateTransaction(account, transaction);
}
else if (isHTSTokenTransaction) {
return handleHTSTokenTransaction(account, subAccount, transaction);
}
else if (isERC20TokenTransaction) {
return handleERC20TokenTransaction(account, subAccount, transaction);
}
else if ((0, utils_1.isStakingTransaction)(transaction)) {
return handleStakingTransaction(account, transaction);
}
else {
return handleCoinTransaction(account, transaction);
}
};
exports.getTransactionStatus = getTransactionStatus;
//# sourceMappingURL=getTransactionStatus.js.map