@ledgerhq/coin-algorand
Version:
Ledger Algorand Coin integration
125 lines • 5.48 kB
JavaScript
import { isValidAddress } from "algosdk";
import { BigNumber } from "bignumber.js";
import invariant from "invariant";
import { AmountRequired, FeeNotLoaded, FeeTooHigh, InvalidAddress, InvalidAddressBecauseDestinationIsAlsoSource, NotEnoughBalance, NotEnoughBalanceBecauseDestinationNotCreated, NotEnoughBalanceInParentAccount, RecipientRequired, } from "@ledgerhq/errors";
import { ClaimRewardsFeesWarning } from "@ledgerhq/errors";
import { AlgorandASANotOptInInRecipient } from "./errors";
import { ALGORAND_MAX_MEMO_SIZE, computeAlgoMaxSpendable, isAmountValid, recipientHasAsset, } from "./logic";
import { extractTokenId } from "./tokens";
/*
* Here are the list of the differents things we check
* - Check if recipient is the same in case of send
* - Check if recipient is valid
* - Check if amounts are set
* - Check if fees are loaded
* - Check if is a send Max and set the amount
* - Check if Token is already optin at the recipient
* - Check if memo is too long
*/
export const getTransactionStatus = async (account, transaction) => {
const errors = {};
const warnings = {};
const tokenAccount = !transaction.subAccountId
? null
: account.subAccounts && account.subAccounts.find(ta => ta.id === transaction.subAccountId);
if (!transaction.recipient) {
errors.recipient = new RecipientRequired();
}
else if (!(await isValidAddress(transaction.recipient))) {
errors.recipient = new InvalidAddress("", {
currencyName: account.currency.name,
});
}
else if (transaction.mode === "send" && account.freshAddress === transaction.recipient) {
errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
}
const estimatedFees = transaction.fees || new BigNumber(0);
let amount = transaction.amount;
let totalSpent = estimatedFees;
invariant(account.algorandResources, "Algorand family expected");
const algorandResources = account.algorandResources;
const algoSpendableBalance = computeAlgoMaxSpendable({
accountBalance: account.balance,
nbAccountAssets: algorandResources.nbAssets,
mode: transaction.mode,
});
switch (transaction.mode) {
case "send": {
if (amount.lte(0) && !transaction.useAllAmount) {
errors.amount = new AmountRequired();
}
if (!transaction.fees || !transaction.fees.gt(0)) {
errors.fees = new FeeNotLoaded();
}
if (tokenAccount &&
tokenAccount.type === "TokenAccount" &&
!errors.recipient &&
!(await recipientHasAsset(transaction.recipient, extractTokenId(tokenAccount.token.id)))) {
errors.recipient = new AlgorandASANotOptInInRecipient();
}
amount = transaction.useAllAmount
? tokenAccount
? tokenAccount.balance
: algoSpendableBalance.minus(estimatedFees)
: amount;
if (amount.lt(0)) {
amount = new BigNumber(0);
}
totalSpent = tokenAccount ? amount : amount.plus(estimatedFees);
if (!errors.recipient && !(await isAmountValid(transaction.recipient, amount))) {
errors.amount = new NotEnoughBalanceBecauseDestinationNotCreated("", {
minimalAmount: "0.1 ALGO",
});
}
if (!tokenAccount && amount.gt(0) && estimatedFees.times(10).gt(amount)) {
warnings.feeTooHigh = new FeeTooHigh();
}
if ((amount.lte(0) && transaction.useAllAmount) || // if use all Amount sets an amount at 0
(!errors.recipient && !errors.amount && tokenAccount
? totalSpent.gt(tokenAccount.balance)
: totalSpent.gt(algoSpendableBalance)) // if spendable balance lower than total
) {
errors.amount = new NotEnoughBalance();
}
// if spendable balance lower than fees for token
if (!errors.amount && tokenAccount && algoSpendableBalance.lt(estimatedFees)) {
errors.amount = new NotEnoughBalanceInParentAccount();
}
break;
}
case "optIn": {
if (!transaction.fees || !transaction.fees.gt(0)) {
errors.fees = new FeeNotLoaded();
}
// This error doesn't need to be translate,
// it will use to block until the user choose an assetId
if (!transaction.assetId) {
errors.assetId = new Error("Asset Id is not set");
}
if (algoSpendableBalance.lt(estimatedFees)) {
errors.amount = new NotEnoughBalance();
}
break;
}
case "claimReward": {
if (algoSpendableBalance.lt(totalSpent)) {
errors.amount = new NotEnoughBalance();
}
if (estimatedFees.gt(algorandResources.rewards)) {
warnings.claimReward = new ClaimRewardsFeesWarning();
}
break;
}
}
if (transaction.memo && transaction.memo.length > ALGORAND_MAX_MEMO_SIZE) {
throw new Error("Memo is too long");
}
return {
errors,
warnings,
estimatedFees,
amount,
totalSpent,
};
};
//# sourceMappingURL=getTransactionStatus.js.map