diff --git a/packages/extension/src/libs/cache-fetch/index.ts b/packages/extension/src/libs/cache-fetch/index.ts index f85d49bf9..4b08885c0 100644 --- a/packages/extension/src/libs/cache-fetch/index.ts +++ b/packages/extension/src/libs/cache-fetch/index.ts @@ -42,7 +42,7 @@ const cacheFetch = async ( .then(json => { const jsondata = options.postProcess ? options.postProcess(json) : json; const jsonstring = JSON.stringify(jsondata); - if (!jsonstring.includes('error')) { + if (!json.error) { const store: StoredData = { timestamp: new Date().getTime(), data: jsonstring, diff --git a/packages/extension/src/providers/solana/libs/api.ts b/packages/extension/src/providers/solana/libs/api.ts index e7d404a93..9931e7526 100644 --- a/packages/extension/src/providers/solana/libs/api.ts +++ b/packages/extension/src/providers/solana/libs/api.ts @@ -64,7 +64,7 @@ class API implements ProviderAPIInterface { } const allTokensResponse = await cacheFetch( { - url: 'https://utl.solcast.dev/solana-tokenlist.json', + url: 'https://raw.githubusercontent.com/solflare-wallet/token-list/refs/heads/master/solana-tokenlist.json', postProcess: (data: any) => { const allTokens = data.tokens as TokenDetails[]; const tObj: Record = {}; @@ -74,7 +74,7 @@ class API implements ProviderAPIInterface { return tObj; }, }, - 60 * 60 * 1000, + 6 * 60 * 60 * 1000, ); const allTokens = allTokensResponse as Record; let decimals = 9; diff --git a/packages/extension/src/providers/solana/ui/send-transaction/components/send-alert.vue b/packages/extension/src/providers/solana/ui/send-transaction/components/send-alert.vue new file mode 100644 index 000000000..046f53424 --- /dev/null +++ b/packages/extension/src/providers/solana/ui/send-transaction/components/send-alert.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/packages/extension/src/providers/solana/ui/send-transaction/index.vue b/packages/extension/src/providers/solana/ui/send-transaction/index.vue index 12b74be95..1f59aeda9 100644 --- a/packages/extension/src/providers/solana/ui/send-transaction/index.vue +++ b/packages/extension/src/providers/solana/ui/send-transaction/index.vue @@ -93,18 +93,7 @@ :fee="gasCostValues[selectedFee]" /> - +
@@ -133,7 +122,7 @@ import SendContactsList from '@/providers/common/ui/send-transaction/send-contac import AssetsSelectList from '@action/views/assets-select-list/index.vue'; import NftSelectList from '@/providers/common/ui/send-transaction/nft-select-list/index.vue'; import SendTokenSelect from './components/send-token-select.vue'; -import SendAlert from '@/providers/common/ui/send-transaction/send-alert.vue'; +import SendAlert from './components/send-alert.vue'; import SendNftSelect from '@/providers/common/ui/send-transaction/send-nft-select.vue'; import SendInputAmount from '@/providers/common/ui/send-transaction/send-input-amount.vue'; import SendFeeSelect from './components/send-fee-select.vue'; @@ -148,7 +137,10 @@ import BigNumber from 'bignumber.js'; import { defaultGasCostVals } from '@/providers/common/libs/default-vals'; import { fromBase, toBase, isValidDecimals } from '@enkryptcom/utils'; import { VerifyTransactionParams, SendTransactionDataType } from '../types'; -import { formatFloatingPointValue } from '@/libs/utils/number-formatter'; +import { + formatFloatingPointValue, + formatFiatValue, +} from '@/libs/utils/number-formatter'; import { routes as RouterNames } from '@/ui/action/router'; import getUiPath from '@/libs/utils/get-ui-path'; import Browser from 'webextension-polyfill'; @@ -328,6 +320,50 @@ const nativeBalanceAfterTransaction = computed(() => { return toBN(0); }); +const nativeValue = computed(() => { + return fromBase( + nativeBalanceAfterTransaction.value.toString(), + props.network.decimals, + ); +}); + +const nativePrice = computed(() => { + return accountAssets.value[0]?.price || '0'; +}); + +const priceDifference = computed(() => { + return new BigNumber(nativeValue.value) + .times(nativePrice.value ?? '0') + .toFixed(); +}); + +const errorMsg = computed(() => { + if (hasEnoughBalance.value && nativeBalanceAfterTransaction.value.isNeg()) { + return `Not enough funds. You are + ~${formatFloatingPointValue(nativeValue.value).value} + ${props.network.currencyName} ($ ${ + formatFiatValue(priceDifference.value).value + }) short.`; + } + if ( + !props.network.isAddress(getAddress(addressTo.value)) && + addressTo.value !== '' + ) + return `Invalid to address.`; + if ( + isSendToken.value && + !isValidDecimals(sendAmount.value, selectedAsset.value.decimals!) + ) { + return `Invalid decimals for ${selectedAsset.value.symbol}.`; + } + if (!isSendToken.value && !selectedNft.value.id) { + return `Invalid NFT selected.`; + } + if (new BigNumber(sendAmount.value).gt(assetMaxValue.value)) + return `Amount exceeds maximum value.`; + return ''; +}); + const setTransactionFees = (tx: SolTransaction) => { return tx .getEstimatedFee(solConnection.value!.web3) @@ -421,6 +457,11 @@ const updateTransactionFees = async () => { }), ); if (isSendToken.value && TxInfo.value.contract === NATIVE_TOKEN_ADDRESS) { + const toBalance = await solConnection.value!.web3.getBalance(to); + const rentExempt = + await solConnection.value!.web3.getMinimumBalanceForRentExemption( + ACCOUNT_SIZE, + ); transaction.add( SystemProgram.transfer({ fromPubkey: from, @@ -428,6 +469,16 @@ const updateTransactionFees = async () => { lamports: toBN(TxInfo.value.value).toNumber(), }), ); + if (toBN(toBalance).lt(toBN(rentExempt))) { + storageFee.value = rentExempt - toBalance; + transaction.add( + SystemProgram.transfer({ + fromPubkey: from, + toPubkey: to, + lamports: storageFee.value, + }), + ); + } } else if ( isSendToken.value || (!isSendToken.value && selectedNft.value.type === NFTType.SolanaToken) diff --git a/packages/extension/src/ui/action/views/swap/index.vue b/packages/extension/src/ui/action/views/swap/index.vue index 9681d940a..a3caa71c0 100644 --- a/packages/extension/src/ui/action/views/swap/index.vue +++ b/packages/extension/src/ui/action/views/swap/index.vue @@ -4,7 +4,7 @@

Swap

- +
@@ -68,7 +68,7 @@
@@ -528,6 +528,7 @@ const pickBestQuote = (fromAmountBN: BN, quotes: ProviderQuoteResponse[]) => { highestMaximum = q.minMax.maximumFrom; } // Minimum briding fees / rent fees / etc + // set smallest fee to q.additionalNativeFees if it's smaller than the current smallest or if smallest is 0 if ( !q.additionalNativeFees.eqn(0) && q.additionalNativeFees.lt(smallestNativeFees) @@ -535,34 +536,25 @@ const pickBestQuote = (fromAmountBN: BN, quotes: ProviderQuoteResponse[]) => { smallestNativeFees = q.additionalNativeFees; } }); - - // Decide what message to show - if (fromAmountBN.lt(lowestMinimum)) { - // Swapping too few tokens - errors.value.inputAmount = `Minimum amount: ${fromT.toReadable( - lowestMinimum, - )}`; + if (fromAmountBN.gt(fromT.getBalanceRaw())) { + errors.value.inputAmount = 'Insufficient funds'; + } else if (fromAmountBN.lt(lowestMinimum)) { + errors.value.inputAmount = `Amount too low`; } else if (fromAmountBN.gt(highestMaximum)) { // Swapping too many tokens errors.value.inputAmount = `Maximum amount: ${fromT.toReadable( highestMaximum, - )}`; + )} ${nativeSwapToken.value!.token.symbol}`; } else if (smallestNativeFees.gt(remainingBalance)) { // Can't afford the fees - errors.value.inputAmount = `Insufficient Bridging fees: ~${nativeSwapToken - .value!.toReadable(smallestNativeFees) - .substring(0, 6)} ${nativeSwapToken.value!.token.symbol} required`; + errors.value.inputAmount = `Insufficient Bridging fees: ~${fromT + .toReadable(smallestNativeFees) + .substring(0, 10)} ${nativeSwapToken.value!.token.symbol} required`; } return; } - // There exist quotes that fit the users swap amount - - if (fromT.getBalanceRaw().lt(fromAmountBN)) { - errors.value.inputAmount = 'Insufficient funds'; - } - // Sort remaining quotes descending by the amount of the dest asset to be received // i.e. best deal first filteredQuotes.sort((a, b) => (b.toTokenAmount.gt(a.toTokenAmount) ? 1 : -1)); @@ -742,6 +734,7 @@ const selectTokenTo = (token: TokenTypeTo | TokenType) => { const inputAmountFrom = async (newVal: string) => { fromAmount.value = newVal; swapMax.value = false; + errors.value.inputAmount = ''; }; const selectAccount = (account: string) => { address.value = account; diff --git a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue index 83090a272..dfb84bb60 100644 --- a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue +++ b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue @@ -477,7 +477,6 @@ const selectFee = (option: GasPriceTypes) => { }; const selectTrade = (trade: ProviderSwapResponse) => { - console.log(trade.provider); pickedTrade.value = trade; setTransactionFees(); }; diff --git a/packages/swap/src/providers/changelly/index.ts b/packages/swap/src/providers/changelly/index.ts index 16cd5fcc6..b10fb64f5 100644 --- a/packages/swap/src/providers/changelly/index.ts +++ b/packages/swap/src/providers/changelly/index.ts @@ -628,7 +628,6 @@ class Changelly extends ProviderClass { ` options.toToken.address=${options.toToken.address}`, ); } - const providerQuoteResponse: ProviderQuoteResponse = { fromTokenAmount: quoteRequestAmount, additionalNativeFees: toBN(0),