UNPKG

@ledgerhq/coin-algorand

Version:
140 lines 5.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateIntent = validateIntent; const errors_1 = require("@ledgerhq/errors"); const algosdk_1 = require("algosdk"); const errors_2 = require("../errors"); const network_1 = require("../network"); const common_1 = require("./common"); const validateMemo_1 = require("./validateMemo"); /** * Validate a transaction intent for Algorand * @param intent - The transaction intent * @param balances - Current account balances * @param customFees - Optional custom fees * @returns Validation result with errors, warnings, and amounts */ async function validateIntent(intent, balances, customFees) { const errors = {}; const warnings = {}; const fees = customFees?.value ?? 0n; let amount = intent.amount; // Validate recipient if (!intent.recipient) { errors.recipient = new errors_1.RecipientRequired(); } else if (!(0, algosdk_1.isValidAddress)(intent.recipient)) { errors.recipient = new errors_1.InvalidAddress(); } else if (intent.sender === intent.recipient) { errors.recipient = new errors_1.InvalidAddressBecauseDestinationIsAlsoSource(); } // Get native balance const nativeBalance = balances.find(b => b.asset.type === "native"); const balance = nativeBalance?.value ?? 0n; const locked = nativeBalance?.locked ?? 0n; // Check for token transfer const isTokenTransfer = intent.asset.type !== "native"; let tokenBalance; if (isTokenTransfer) { const intentAssetRef = intent.asset.assetReference; tokenBalance = balances.find(b => { if (b.asset.type !== "asa") return false; const balanceAssetRef = b.asset.assetReference; return balanceAssetRef === intentAssetRef; }); if (!tokenBalance) { errors.amount = new errors_1.NotEnoughBalance(); } } // Validate amount if (amount <= 0n && !intent.useAllAmount) { errors.amount = new errors_1.AmountRequired(); } // Handle useAllAmount if (intent.useAllAmount) { if (isTokenTransfer && tokenBalance) { amount = tokenBalance.value; } else { const spendable = balance - locked - fees; amount = spendable > 0n ? spendable : 0n; } } // Calculate total spent const totalSpent = isTokenTransfer ? amount : amount + fees; // Check balance if (!errors.amount) { if (isTokenTransfer) { // Check token balance if (tokenBalance && amount > tokenBalance.value) { errors.amount = new errors_1.NotEnoughBalance(); } // Check native balance for fees if (fees > balance - locked) { errors.amount = new errors_1.NotEnoughBalanceInParentAccount(); } } else { // Check native balance const spendable = balance - locked; if (totalSpent > spendable) { errors.amount = new errors_1.NotEnoughBalance(); } } } // Validate recipient account (fetch once for both ASA opt-in and native minimum balance checks) if (!errors.recipient && intent.recipient) { try { const recipientAccount = await (0, network_1.getAccount)(intent.recipient); if (isTokenTransfer) { // Check if recipient has opted in to the ASA token const intentAssetRef = intent.asset.assetReference; if (intentAssetRef) { const hasOptedIn = recipientAccount.assets.map(a => a.assetId).includes(intentAssetRef); if (!hasOptedIn) { errors.recipient = new errors_2.AlgorandASANotOptInInRecipient(); } } } else if (amount > 0n) { // Check minimum balance requirement for native transfers const recipientBalance = BigInt(recipientAccount.balance.toString()); if (recipientBalance === 0n && amount < common_1.ALGORAND_MIN_ACCOUNT_BALANCE) { errors.amount = new errors_1.NotEnoughBalanceBecauseDestinationNotCreated("", { minimalAmount: "0.1 ALGO", }); } } } catch { // Handle account fetch error if (isTokenTransfer) { // If we can't fetch the account, assume it doesn't exist and hasn't opted in errors.recipient = new errors_2.AlgorandASANotOptInInRecipient(); } else if (amount > 0n) { // Account doesn't exist yet, need minimum balance for native transfer if (amount < common_1.ALGORAND_MIN_ACCOUNT_BALANCE) { errors.amount = new errors_1.NotEnoughBalanceBecauseDestinationNotCreated("", { minimalAmount: "0.1 ALGO", }); } } } } // Validate memo const memoValue = intent.memo?.type === "string" ? intent.memo.value : undefined; if (memoValue && !(0, validateMemo_1.validateMemo)(memoValue)) { errors.transaction = new errors_2.AlgorandMemoExceededSizeError(); } return { errors, warnings, estimatedFees: fees, amount, totalSpent, }; } //# sourceMappingURL=validateIntent.js.map