Skip to content

Commit d3eab40

Browse files
authored
Merge pull request #561 from enkryptcom/fix/error-msg-for-send-and-swap
fix: error message for send when address has no balance for rent
2 parents 89fb017 + 3716d40 commit d3eab40

7 files changed

Lines changed: 133 additions & 38 deletions

File tree

packages/extension/src/libs/cache-fetch/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const cacheFetch = async (
4242
.then(json => {
4343
const jsondata = options.postProcess ? options.postProcess(json) : json;
4444
const jsonstring = JSON.stringify(jsondata);
45-
if (!jsonstring.includes('error')) {
45+
if (!json.error) {
4646
const store: StoredData = {
4747
timestamp: new Date().getTime(),
4848
data: jsonstring,

packages/extension/src/providers/solana/libs/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class API implements ProviderAPIInterface {
6464
}
6565
const allTokensResponse = await cacheFetch(
6666
{
67-
url: 'https://utl.solcast.dev/solana-tokenlist.json',
67+
url: 'https://raw.githubusercontent.com/solflare-wallet/token-list/refs/heads/master/solana-tokenlist.json',
6868
postProcess: (data: any) => {
6969
const allTokens = data.tokens as TokenDetails[];
7070
const tObj: Record<string, TokenDetails> = {};
@@ -74,7 +74,7 @@ class API implements ProviderAPIInterface {
7474
return tObj;
7575
},
7676
},
77-
60 * 60 * 1000,
77+
6 * 60 * 60 * 1000,
7878
);
7979
const allTokens = allTokensResponse as Record<string, TokenDetails>;
8080
let decimals = 9;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<template>
2+
<div class="send-alert" role="alert" aria-live="polite">
3+
<alert-icon />
4+
<p aria-label="Error message">{{ errorMsg }}</p>
5+
</div>
6+
</template>
7+
8+
<script setup lang="ts">
9+
import AlertIcon from '@action/icons/send/alert-icon.vue';
10+
11+
interface IProps {
12+
errorMsg: string;
13+
}
14+
15+
defineProps<IProps>();
16+
</script>
17+
18+
<style lang="less">
19+
@import '@action/styles/theme.less';
20+
21+
.send-alert {
22+
margin: 0 32px 8px 32px;
23+
background: @error01;
24+
border-radius: 10px;
25+
padding: 12px 16px 12px 57px;
26+
position: relative;
27+
box-sizing: border-box;
28+
29+
svg {
30+
position: absolute;
31+
left: 16px;
32+
top: 50%;
33+
margin-top: -12px;
34+
}
35+
36+
p {
37+
font-weight: 400;
38+
font-size: 14px;
39+
line-height: 20px;
40+
letter-spacing: 0.25px;
41+
color: @error;
42+
margin: 0;
43+
44+
a {
45+
color: @error;
46+
47+
&:hover {
48+
text-decoration: none;
49+
}
50+
}
51+
}
52+
}
53+
</style>

packages/extension/src/providers/solana/ui/send-transaction/index.vue

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,7 @@
9393
:fee="gasCostValues[selectedFee]"
9494
/>
9595

96-
<send-alert
97-
v-show="hasEnoughBalance && nativeBalanceAfterTransaction.isNeg()"
98-
:native-symbol="network.currencyName"
99-
:price="accountAssets[0]?.price || '0'"
100-
:native-value="
101-
fromBase(
102-
nativeBalanceAfterTransaction.abs().toString(),
103-
network.decimals,
104-
)
105-
"
106-
:decimals="network.decimals"
107-
/>
96+
<send-alert v-show="errorMsg" :error-msg="errorMsg" />
10897

10998
<div class="send-transaction__buttons">
11099
<div class="send-transaction__buttons-cancel">
@@ -133,7 +122,7 @@ import SendContactsList from '@/providers/common/ui/send-transaction/send-contac
133122
import AssetsSelectList from '@action/views/assets-select-list/index.vue';
134123
import NftSelectList from '@/providers/common/ui/send-transaction/nft-select-list/index.vue';
135124
import SendTokenSelect from './components/send-token-select.vue';
136-
import SendAlert from '@/providers/common/ui/send-transaction/send-alert.vue';
125+
import SendAlert from './components/send-alert.vue';
137126
import SendNftSelect from '@/providers/common/ui/send-transaction/send-nft-select.vue';
138127
import SendInputAmount from '@/providers/common/ui/send-transaction/send-input-amount.vue';
139128
import SendFeeSelect from './components/send-fee-select.vue';
@@ -148,7 +137,10 @@ import BigNumber from 'bignumber.js';
148137
import { defaultGasCostVals } from '@/providers/common/libs/default-vals';
149138
import { fromBase, toBase, isValidDecimals } from '@enkryptcom/utils';
150139
import { VerifyTransactionParams, SendTransactionDataType } from '../types';
151-
import { formatFloatingPointValue } from '@/libs/utils/number-formatter';
140+
import {
141+
formatFloatingPointValue,
142+
formatFiatValue,
143+
} from '@/libs/utils/number-formatter';
152144
import { routes as RouterNames } from '@/ui/action/router';
153145
import getUiPath from '@/libs/utils/get-ui-path';
154146
import Browser from 'webextension-polyfill';
@@ -328,6 +320,50 @@ const nativeBalanceAfterTransaction = computed(() => {
328320
return toBN(0);
329321
});
330322
323+
const nativeValue = computed(() => {
324+
return fromBase(
325+
nativeBalanceAfterTransaction.value.toString(),
326+
props.network.decimals,
327+
);
328+
});
329+
330+
const nativePrice = computed(() => {
331+
return accountAssets.value[0]?.price || '0';
332+
});
333+
334+
const priceDifference = computed(() => {
335+
return new BigNumber(nativeValue.value)
336+
.times(nativePrice.value ?? '0')
337+
.toFixed();
338+
});
339+
340+
const errorMsg = computed(() => {
341+
if (hasEnoughBalance.value && nativeBalanceAfterTransaction.value.isNeg()) {
342+
return `Not enough funds. You are
343+
~${formatFloatingPointValue(nativeValue.value).value}
344+
${props.network.currencyName} ($ ${
345+
formatFiatValue(priceDifference.value).value
346+
}) short.`;
347+
}
348+
if (
349+
!props.network.isAddress(getAddress(addressTo.value)) &&
350+
addressTo.value !== ''
351+
)
352+
return `Invalid to address.`;
353+
if (
354+
isSendToken.value &&
355+
!isValidDecimals(sendAmount.value, selectedAsset.value.decimals!)
356+
) {
357+
return `Invalid decimals for ${selectedAsset.value.symbol}.`;
358+
}
359+
if (!isSendToken.value && !selectedNft.value.id) {
360+
return `Invalid NFT selected.`;
361+
}
362+
if (new BigNumber(sendAmount.value).gt(assetMaxValue.value))
363+
return `Amount exceeds maximum value.`;
364+
return '';
365+
});
366+
331367
const setTransactionFees = (tx: SolTransaction) => {
332368
return tx
333369
.getEstimatedFee(solConnection.value!.web3)
@@ -421,13 +457,28 @@ const updateTransactionFees = async () => {
421457
}),
422458
);
423459
if (isSendToken.value && TxInfo.value.contract === NATIVE_TOKEN_ADDRESS) {
460+
const toBalance = await solConnection.value!.web3.getBalance(to);
461+
const rentExempt =
462+
await solConnection.value!.web3.getMinimumBalanceForRentExemption(
463+
ACCOUNT_SIZE,
464+
);
424465
transaction.add(
425466
SystemProgram.transfer({
426467
fromPubkey: from,
427468
toPubkey: to,
428469
lamports: toBN(TxInfo.value.value).toNumber(),
429470
}),
430471
);
472+
if (toBN(toBalance).lt(toBN(rentExempt))) {
473+
storageFee.value = rentExempt - toBalance;
474+
transaction.add(
475+
SystemProgram.transfer({
476+
fromPubkey: from,
477+
toPubkey: to,
478+
lamports: storageFee.value,
479+
}),
480+
);
481+
}
431482
} else if (
432483
isSendToken.value ||
433484
(!isSendToken.value && selectedNft.value.type === NFTType.SolanaToken)

packages/extension/src/ui/action/views/swap/index.vue

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<div v-if="!!selected" class="swap">
55
<div class="swap__header">
66
<h3>Swap</h3>
7-
<a class="swap__close" @click="$router.go(-1)">
7+
<a class="swap__close" @click="router.go(-1)">
88
<close-icon />
99
</a>
1010
</div>
@@ -68,7 +68,7 @@
6868
<base-button
6969
title="Cancel"
7070
:no-background="true"
71-
@click="$router.go(-1)"
71+
@click="router.go(-1)"
7272
/>
7373
</div>
7474
<div class="swap__buttons-send">
@@ -528,41 +528,33 @@ const pickBestQuote = (fromAmountBN: BN, quotes: ProviderQuoteResponse[]) => {
528528
highestMaximum = q.minMax.maximumFrom;
529529
}
530530
// Minimum briding fees / rent fees / etc
531+
// set smallest fee to q.additionalNativeFees if it's smaller than the current smallest or if smallest is 0
531532
if (
532533
!q.additionalNativeFees.eqn(0) &&
533534
q.additionalNativeFees.lt(smallestNativeFees)
534535
) {
535536
smallestNativeFees = q.additionalNativeFees;
536537
}
537538
});
538-
539-
// Decide what message to show
540-
if (fromAmountBN.lt(lowestMinimum)) {
541-
// Swapping too few tokens
542-
errors.value.inputAmount = `Minimum amount: ${fromT.toReadable(
543-
lowestMinimum,
544-
)}`;
539+
if (fromAmountBN.gt(fromT.getBalanceRaw())) {
540+
errors.value.inputAmount = 'Insufficient funds';
541+
} else if (fromAmountBN.lt(lowestMinimum)) {
542+
errors.value.inputAmount = `Amount too low`;
545543
} else if (fromAmountBN.gt(highestMaximum)) {
546544
// Swapping too many tokens
547545
errors.value.inputAmount = `Maximum amount: ${fromT.toReadable(
548546
highestMaximum,
549-
)}`;
547+
)} ${nativeSwapToken.value!.token.symbol}`;
550548
} else if (smallestNativeFees.gt(remainingBalance)) {
551549
// Can't afford the fees
552-
errors.value.inputAmount = `Insufficient Bridging fees: ~${nativeSwapToken
553-
.value!.toReadable(smallestNativeFees)
554-
.substring(0, 6)} ${nativeSwapToken.value!.token.symbol} required`;
550+
errors.value.inputAmount = `Insufficient Bridging fees: ~${fromT
551+
.toReadable(smallestNativeFees)
552+
.substring(0, 10)} ${nativeSwapToken.value!.token.symbol} required`;
555553
}
556554
557555
return;
558556
}
559557
560-
// There exist quotes that fit the users swap amount
561-
562-
if (fromT.getBalanceRaw().lt(fromAmountBN)) {
563-
errors.value.inputAmount = 'Insufficient funds';
564-
}
565-
566558
// Sort remaining quotes descending by the amount of the dest asset to be received
567559
// i.e. best deal first
568560
filteredQuotes.sort((a, b) => (b.toTokenAmount.gt(a.toTokenAmount) ? 1 : -1));
@@ -742,6 +734,7 @@ const selectTokenTo = (token: TokenTypeTo | TokenType) => {
742734
const inputAmountFrom = async (newVal: string) => {
743735
fromAmount.value = newVal;
744736
swapMax.value = false;
737+
errors.value.inputAmount = '';
745738
};
746739
const selectAccount = (account: string) => {
747740
address.value = account;

packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,6 @@ const selectFee = (option: GasPriceTypes) => {
477477
};
478478
479479
const selectTrade = (trade: ProviderSwapResponse) => {
480-
console.log(trade.provider);
481480
pickedTrade.value = trade;
482481
setTransactionFees();
483482
};

packages/swap/src/providers/changelly/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,6 @@ class Changelly extends ProviderClass {
628628
` options.toToken.address=${options.toToken.address}`,
629629
);
630630
}
631-
632631
const providerQuoteResponse: ProviderQuoteResponse = {
633632
fromTokenAmount: quoteRequestAmount,
634633
additionalNativeFees: toBN(0),

0 commit comments

Comments
 (0)