UNPKG

@shogun-sdk/money-legos

Version:

Shogun Money Legos: clients and types for quotes, memes, prices, balances, fees, validations, etc.

162 lines (142 loc) 5.12 kB
import { CHAIN_MAP, SOLANA_CHAIN_ID } from '../config/chains.js'; import type { TokenBalanceResults } from '../types/index.js'; import { NATIVE_TOKEN, SOL_NATIVE } from '../config/addresses.js'; import { buildInsufficientFundsWebAppError } from '../utils/errors.js'; import { fromWei } from '../utils/eth.js'; import { AffiliateFeeService } from './AffiliateFeeService.js'; export interface InsufficientFundsErrorBuilderProps { nativeTotalBalance?: number; sourceNetwork: number; destinationNetwork: number; isTransfer: boolean; balances: TokenBalanceResults[]; tokenInAddress: string; tokenOutAddress: string; amountInput: string; systemFeePercent: number; errorBuilder?: (network: string, tokenSymbol?: string, minUsdValue?: number) => string; isEoaAccount: boolean; additionalAffiliateFee: number; externalServiceTxCostUsd: number; } export class InsufficientFundsErrorBuilder { private props: InsufficientFundsErrorBuilderProps; constructor(props: InsufficientFundsErrorBuilderProps) { this.props = props; } build(): string { const { balances, nativeTotalBalance = 0, sourceNetwork, isTransfer, amountInput, tokenInAddress, destinationNetwork, systemFeePercent, isEoaAccount, additionalAffiliateFee, externalServiceTxCostUsd, errorBuilder = buildInsufficientFundsWebAppError, } = this.props; if (!nativeTotalBalance || nativeTotalBalance === 0) { return errorBuilder(CHAIN_MAP[sourceNetwork]?.name ?? ''); } // it is swap const excludeFee = false /*excludeAffiliateFee({ srcToken: tokenInAddress, destToken: tokenOutAddress, srcChainId: sourceNetwork, destChainId: destinationNetwork, });*/ const sourceNativeUsd = (sourceNetwork === SOLANA_CHAIN_ID ? balances.find((x) => x.tokenAddress === NATIVE_TOKEN.SOL && x.network === sourceNetwork)?.usdValue || balances.find((x) => x.tokenAddress === SOL_NATIVE && x.network === sourceNetwork)?.usdValue : balances.find((x) => x.tokenAddress === NATIVE_TOKEN.ETH && x.network === sourceNetwork)?.usdValue) || 0; const matchingToken = balances.find((x) => x.tokenAddress === tokenInAddress && x.network === sourceNetwork); const sourceUsdValue = matchingToken ? Number(matchingToken?.balanceFormatted) * matchingToken?.usdPrice || 0 : 0; const minUsdValue = AffiliateFeeService.calculateMinAllowedUsdValue( systemFeePercent, sourceNetwork, excludeFee, isEoaAccount, externalServiceTxCostUsd, isTransfer, additionalAffiliateFee, ); // if source and dest networks are the same, we need to check: if (sourceNetwork === destinationNetwork) { if (sourceNativeUsd < minUsdValue) { return errorBuilder(CHAIN_MAP[sourceNetwork]?.name ?? ''); } } else if (sourceUsdValue < minUsdValue) { return errorBuilder(CHAIN_MAP[sourceNetwork]?.name ?? ''); } return InsufficientFundsErrorBuilder.buildAffiliateFeeErrorMsg( amountInput, balances, excludeFee, tokenInAddress, sourceNetwork, isTransfer, systemFeePercent, isEoaAccount, externalServiceTxCostUsd, additionalAffiliateFee, errorBuilder, ); } static buildAffiliateFeeErrorMsg( amountInWei: string, balances: TokenBalanceResults[], excludeFee: boolean, tokenInAddress: string, sourceNetwork: number, isTransfer: boolean, systemFeePercent: number, isEoaAccount: boolean, externalServiceTxCostUsd: number, additionalAffiliateFee: number, errorBuilder: (network: string, tokenSymbol?: string, minAmountValue?: number, minUsdValue?: number) => string, ): string { // We are not checking affiliate fee for transfers if (isTransfer) { return ''; } const { tokenUsdPrice, decimals = 18, symbol, } = AffiliateFeeService.getAffiliateFeeTokenForQuote(balances, tokenInAddress, sourceNetwork) || {}; if (!tokenUsdPrice || !amountInWei || amountInWei === '0') { return ''; } const minUsdValue = AffiliateFeeService.calculateMinAllowedUsdValue( systemFeePercent, sourceNetwork, excludeFee, isEoaAccount, externalServiceTxCostUsd, isTransfer, additionalAffiliateFee, ); const amount = fromWei(amountInWei, decimals); const amountUsd = +amount * tokenUsdPrice; // Check for valid non-zero amount if (amountUsd > 0 && amountUsd <= minUsdValue) { // Prevent division by zero by checking amountUsd const roundedAmountUsd = roundToBillion(amountUsd); if (roundedAmountUsd === 0) { return ''; } const minAmountValue = roundedAmountUsd > (Number(amount) * roundToBillion(minUsdValue)) / roundedAmountUsd ? 0 : amount; return errorBuilder(CHAIN_MAP[sourceNetwork]?.name ?? '', symbol, Number(minAmountValue), minUsdValue); } return ''; } } const roundToBillion = (value: number) => { return Math.round(value * 1000000000); };