Skip to content

Commit eeb014f

Browse files
onmaxsisou
authored andcommitted
chore(swap crc): implemented swap logic
1 parent cdb265c commit eeb014f

12 files changed

+282
-220
lines changed

src/components/DualCurrencyInput.vue

+6-35
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
<div class="dual-currency-input">
33
<section :class="{ orange: !!invalidReason }">
44
<div class="flex-row primary-amount">
5-
<AmountInput v-model="_cryptoAmount" :decimals="cryptoCurrencyDecimals">
5+
<AmountInput :value="cryptoAmount" @input="$emit('update:cryptoAmount', $event)"
6+
:decimals="cryptoCurrencyDecimals">
67
<div class="amount-menu ticker" slot="suffix">
78
<button class="reset button flex-row" @click.stop="currencySelectorOpen = !currencySelectorOpen">
89
{{ cryptoCurrency.toUpperCase() }}
@@ -29,7 +30,8 @@
2930
<path d="M23.75 15.25l6.5 6.5-6.5 6.5" stroke-linejoin="round" />
3031
</g>
3132
</svg>
32-
<AmountInput v-model="_fiatAmount" :decimals="fiatCurrencyDecimals" placeholder="0.00">
33+
<AmountInput :value="fiatAmount" @input="$emit('update:fiatAmount', $event)"
34+
:decimals="fiatCurrencyDecimals" placeholder="0.00">
3335
<span slot="suffix" class="ticker">{{ fiatCurrency.toUpperCase() }}</span>
3436
</AmountInput>
3537
</span>
@@ -38,7 +40,7 @@
3840
</template>
3941

4042
<script lang="ts">
41-
import { computed, defineComponent, ref, watch } from '@vue/composition-api';
43+
import { defineComponent, ref } from '@vue/composition-api';
4244
import { CryptoCurrency, FiatCurrency } from '@/lib/Constants';
4345
import AmountInput from './AmountInput.vue';
4446
@@ -76,43 +78,12 @@ export default defineComponent({
7678
},
7779
invalidReason: String,
7880
},
79-
setup(props, context) {
81+
setup() {
8082
const currencySelectorOpen = ref(false);
8183
82-
const _fiatAmount = computed({
83-
get: () => props.fiatAmount,
84-
set: (newValue) => context.emit('update:fiatAmount', newValue),
85-
});
86-
const _cryptoAmount = computed({
87-
get: () => props.cryptoAmount,
88-
set: (newValue) => { context.emit('update:cryptoAmount', newValue); },
89-
});
90-
91-
const syncing = ref(false);
92-
93-
watch(_fiatAmount, (newVal) => {
94-
if (syncing.value) return;
95-
syncing.value = true;
96-
const newCryptoValue = Number(((newVal / props.exchangeRate) * 10 ** props.cryptoCurrencyDecimals)
97-
.toFixed(props.cryptoCurrencyDecimals));
98-
context.emit('update:cryptoAmount', newCryptoValue);
99-
setTimeout(() => syncing.value = false, 100);
100-
}, { lazy: true });
101-
102-
watch(_cryptoAmount, (newVal) => {
103-
if (syncing.value) return;
104-
syncing.value = true;
105-
const newFiatValue = Number(((newVal / 10 ** props.cryptoCurrencyDecimals) * props.exchangeRate)
106-
.toFixed(props.fiatCurrencyDecimals));
107-
context.emit('update:fiatAmount', newFiatValue);
108-
setTimeout(() => syncing.value = false, 100);
109-
}, { lazy: true });
110-
11184
return {
11285
currencySelectorOpen,
11386
CryptoCurrency,
114-
_fiatAmount,
115-
_cryptoAmount,
11687
};
11788
},
11889
components: {

src/components/modals/AssetTransferModal.vue

+134-31
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
@close="closeModal"
77
@close-overlay="() => (p.addressListOpened = false)"
88
>
9-
<div v-if="!p">Loading fees</div>
9+
<div v-if="!p" class="loading-wrapper">
10+
<div class="loading-container">
11+
<CircleSpinner />
12+
<p>
13+
{{ $t("Loading...") }}
14+
</p>
15+
</div>
16+
</div>
1017
<div v-else class="asset-transfer-input">
1118
<PageHeader>{{ p.modalTitle }}</PageHeader>
1219
<PageBody class="flex-column">
@@ -55,8 +62,6 @@
5562
</template>
5663
</i18n>
5764
</SwapFeesTooltip>
58-
{{p.limits}}
59-
current: {{p.currentLimitFiat}}
6065
<Tooltip
6166
:styles="{ width: '28.75rem' }"
6267
preferredPosition="bottom left"
@@ -69,7 +74,7 @@
6974
v-if="p.limits"
7075
:amount="p.currentLimitFiat"
7176
:currency="p.currencyFiatFallback.toLocaleLowerCase()"
72-
hideDecimals
77+
hide-decimals
7378
/>
7479
<CircleSpinner v-else />
7580
</div>
@@ -118,14 +123,12 @@
118123
</section>
119124

120125
<DualCurrencyInput
121-
:fiatAmount.sync="p.fiatAmount"
122-
:cryptoAmount.sync="p.cryptoAmount"
126+
:fiatAmount.sync="fiatAmount"
127+
:cryptoAmount.sync="cryptoAmount"
123128
:fiatCurrency="p.currencyFiatFallback.toLocaleLowerCase()"
124129
:cryptoCurrency="p.currencyCrypto.toLocaleLowerCase()"
125130
:fiatCurrencyDecimals="p.decimalsFiat"
126131
:cryptoCurrencyDecimals="p.decimalsCrypto"
127-
:maxCrypto="p.currentLimitCrypto"
128-
:maxFiat="p.currentLimitFiat"
129132
:exchangeRate="p.exchangeRate"
130133
:invalid-reason="p.invalidReason"
131134
@set-max="setMax()"
@@ -147,16 +150,16 @@
147150
{{ $t("Insufficient balance.") }}
148151
<a @click="() => setMax()">{{ $t("Sell max") }}</a>
149152
</template>
150-
<template v-else-if="p.estimateError">
151-
{{ p.estimateError }}
153+
<template v-else-if="estimateError">
154+
{{ estimateError }}
152155
</template>
153156
</MessageTransition>
154157
</PageBody>
155158
<PageFooter>
156159
<button
157160
class="nq-button light-blue"
158161
@mousedown.prevent
159-
:disabled="!p.canSign || p.invalid || !p.fiatAmount || !p.cryptoAmount"
162+
:disabled="!p.canSign || p.invalid || !fiatAmount || !cryptoAmount"
160163
@click="p.sign()"
161164
>
162165
{{ $t("Confirm") }}
@@ -245,6 +248,7 @@ import {
245248
defineComponent,
246249
onMounted,
247250
ref,
251+
watch,
248252
} from '@vue/composition-api';
249253
import {
250254
AssetTransferMethod,
@@ -264,9 +268,10 @@ import {
264268
import { ENV_MAIN } from '@/lib/Constants';
265269
import { useAddressStore } from '@/stores/Address';
266270
import { useBtcAddressStore } from '@/stores/BtcAddress';
267-
import { SwapState, useSwapsStore } from '@/stores/Swaps';
271+
import { isFiatAsset, SwapState, useSwapsStore } from '@/stores/Swaps';
268272
import { SwapAsset } from '@nimiq/libswap';
269273
import { useConfig } from '@/composables/useConfig';
274+
import { capDecimals, useSwapEstimate } from '@/lib/swap/utils/CommonUtils';
270275
import AddressList from '../AddressList.vue';
271276
import DualCurrencyInput from '../DualCurrencyInput.vue';
272277
import FiatConvertedAmount from '../FiatConvertedAmount.vue';
@@ -293,13 +298,50 @@ export default defineComponent({
293298
},
294299
setup(props, context) {
295300
const p /* params */ = ref<AssetTransferParams>(null);
296-
const cryptoAmount = computed(
297-
() => (p.value?.cryptoAmount as unknown as number) || 0,
298-
);
301+
302+
const { estimate } = useSwapEstimate();
303+
304+
const _fiatAmount = ref(0);
305+
const fiatAmount = computed({
306+
get: () => {
307+
if (_fiatAmount.value !== 0) return _fiatAmount.value;
308+
if (!estimate?.value) return 0;
309+
if (!isFiatAsset(estimate.value.to.asset)) return 0;
310+
return estimate.value.to.amount - estimate.value.to.fee;
311+
},
312+
set: (value: number) => {
313+
_cryptoAmount.value = 0;
314+
_fiatAmount.value = value;
315+
onInput(value);
316+
},
317+
});
318+
319+
const _cryptoAmount = ref(0);
320+
const cryptoAmount = computed({
321+
get: () => {
322+
if (_cryptoAmount.value !== 0) return _cryptoAmount.value;
323+
if (!estimate.value || !p.value?.currencyCrypto) return 0;
324+
if (estimate.value.from.asset !== p.value.currencyCrypto) return 0;
325+
return capDecimals(estimate.value.from.amount + estimate.value.from.fee, estimate.value.from.asset);
326+
},
327+
set: (value: number) => {
328+
_fiatAmount.value = 0;
329+
_cryptoAmount.value = value;
330+
onInput(value);
331+
},
332+
});
333+
334+
const fetchingEstimate = ref<boolean>(false);
299335
300336
onMounted(async () => {
301337
const options: AssetTransferOptions = {
302338
pair: props.pair,
339+
cryptoAmount,
340+
fiatAmount,
341+
estimate,
342+
updateEstimate,
343+
estimateError,
344+
fetchingEstimate,
303345
};
304346
305347
switch (props.method) {
@@ -315,43 +357,81 @@ export default defineComponent({
315357
}
316358
});
317359
360+
let updateEstimateFn: (args: any) => Promise<void>;
361+
const estimateError = ref<string>(null);
362+
let timeoutId: number | undefined;
363+
364+
async function updateEstimate() {
365+
if (!p.value) return;
366+
clearTimeout(timeoutId);
367+
368+
fetchingEstimate.value = true;
369+
370+
if (!updateEstimateFn) {
371+
updateEstimateFn = p.value?.isSelling
372+
? (await import('@/lib/swap/utils/SellUtils')).updateSellEstimate
373+
: (await import('@/lib/swap/utils/BuyUtils')).updateBuyEstimate;
374+
}
375+
376+
const args = _fiatAmount.value
377+
? { fiatAmount: fiatAmount.value, fiatAsset: p.value.currencyFiatFallback }
378+
: { cryptoAmount: cryptoAmount.value, fiatAsset: p.value.currencyFiatFallback };
379+
await updateEstimateFn(args).then(() => {
380+
estimateError.value = null;
381+
}).catch((error) => {
382+
console.warn(error); // eslint-disable-line no-console
383+
estimateError.value = error.message;
384+
});
385+
386+
fetchingEstimate.value = false;
387+
}
388+
389+
function onInput(val: number) {
390+
clearTimeout(timeoutId);
391+
392+
if (!val) {
393+
estimate.value = null;
394+
estimateError.value = null;
395+
return;
396+
}
397+
398+
timeoutId = window.setTimeout(updateEstimate, 500);
399+
fetchingEstimate.value = true;
400+
}
401+
318402
const { activeAddressInfo } = useAddressStore();
319403
const { accountBalance: accountBtcBalance } = useBtcAddressStore();
320404
321405
function setMax() {
322406
if (!p.value?.isSelling) return;
407+
const currentLimitCrypto = p.value.currentLimitCrypto.value;
323408
324409
switch (p.value.currencyCrypto) {
325410
case SwapAsset.NIM: {
326-
if (!p.value.currentLimitCrypto.value) {
327-
p.value.updateCryptoAmount(activeAddressInfo.value?.balance || 0);
328-
} else if (
329-
p.value.currentLimitCrypto.value
330-
< (activeAddressInfo.value?.balance || 0)
331-
) {
332-
p.value.updateCryptoAmount(p.value.currentLimitCrypto.value);
411+
if (!currentLimitCrypto) {
412+
cryptoAmount.value = activeAddressInfo.value?.balance || 0;
413+
} else if (currentLimitCrypto < (activeAddressInfo.value?.balance || 0)) {
414+
cryptoAmount.value = currentLimitCrypto;
333415
} else {
334-
p.value.updateCryptoAmount(activeAddressInfo.value?.balance || 0);
416+
cryptoAmount.value = activeAddressInfo.value?.balance || 0;
335417
}
336418
break;
337419
}
338420
case SwapAsset.BTC: {
339-
if (!p.value.currentLimitCrypto.value) {
340-
p.value.updateCryptoAmount(accountBtcBalance.value);
341-
} else if (
342-
p.value.currentLimitCrypto.value < accountBtcBalance.value
343-
) {
344-
p.value.updateCryptoAmount(p.value.currentLimitCrypto.value);
421+
if (!currentLimitCrypto) {
422+
cryptoAmount.value = accountBtcBalance.value;
423+
} else if (currentLimitCrypto < accountBtcBalance.value) {
424+
cryptoAmount.value = currentLimitCrypto;
345425
} else {
346-
p.value.updateCryptoAmount(accountBtcBalance.value);
426+
cryptoAmount.value = accountBtcBalance.value;
347427
}
348428
break;
349429
}
350430
default:
351431
throw new Error('Invalid currency');
352432
}
353433
354-
p.value.updateFiatAmount(cryptoAmount.value * p.value.exchangeRate.value);
434+
fiatAmount.value = cryptoAmount.value * p.value.exchangeRate.value;
355435
}
356436
357437
function finishSwap() {
@@ -368,6 +448,13 @@ export default defineComponent({
368448
}
369449
}
370450
451+
const { activeSwap } = useSwapsStore();
452+
watch(activeSwap, () => {
453+
if (activeSwap.value) {
454+
closeModal();
455+
}
456+
});
457+
371458
const { config } = useConfig();
372459
const isMainnet = config.environment === ENV_MAIN;
373460
@@ -380,6 +467,9 @@ export default defineComponent({
380467
SwapState,
381468
SwapAsset,
382469
activeAddressInfo,
470+
cryptoAmount,
471+
fiatAmount,
472+
estimateError,
383473
};
384474
},
385475
components: {
@@ -406,6 +496,19 @@ export default defineComponent({
406496

407497
<style scoped lang="scss">
408498
.asset-transfer-modal {
499+
.loading-wrapper {
500+
display: grid;
501+
place-content: center;
502+
height: 100%;
503+
504+
.loading-container {
505+
display: flex;
506+
justify-content: center;
507+
align-items: center;
508+
gap: 2rem;
509+
}
510+
}
511+
409512
.asset-transfer-input {
410513
position: relative;
411514
height: 100%;

0 commit comments

Comments
 (0)