init
This commit is contained in:
commit
44f31f8b9f
402 changed files with 47865 additions and 0 deletions
36
components/withdraw/RelayerTotal.vue
Normal file
36
components/withdraw/RelayerTotal.vue
Normal file
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
{{ $t('relayerTotal.label') }}
|
||||
</div>
|
||||
<div class="withdraw-data">
|
||||
<div class="withdraw-data-item">
|
||||
{{ $t('relayerTotal.name') }}
|
||||
<span>{{ relayerName }}</span>
|
||||
</div>
|
||||
<div class="withdraw-data-item">
|
||||
{{ $t('relayerTotal.fee') }}
|
||||
<span>{{ selectedRelayer.tornadoServiceFee }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState('relayer', ['selectedRelayer']),
|
||||
relayerName() {
|
||||
const { name, url } = this.selectedRelayer
|
||||
|
||||
if (name === 'custom') {
|
||||
return url.replace(/http(s)?|:|\//g, '')
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
526
components/withdraw/Withdraw.vue
Normal file
526
components/withdraw/Withdraw.vue
Normal file
|
@ -0,0 +1,526 @@
|
|||
<template>
|
||||
<b-tab-item :label="$t('withdraw')">
|
||||
<div class="field">
|
||||
<div class="label-with-buttons">
|
||||
<div class="label">
|
||||
{{ $t('note') }}
|
||||
<b-tooltip :label="$t('noteTooltip')" size="is-small" position="is-right" multilined>
|
||||
<button class="button is-primary has-icon">
|
||||
<span class="icon icon-info"></span>
|
||||
</button>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
<a
|
||||
v-show="!hasErrorNote && depositTxHash"
|
||||
:href="txExplorerUrl(depositTxHash)"
|
||||
target="_blank"
|
||||
class="button is-icon"
|
||||
>
|
||||
<b-tooltip
|
||||
:label="$t('depositTransactionOnEtherscan')"
|
||||
size="is-small"
|
||||
position="is-left"
|
||||
multilined
|
||||
>
|
||||
<LinkIcon />
|
||||
</b-tooltip>
|
||||
</a>
|
||||
<button v-show="shouldSettingsShow" class="button is-icon" @click="onSettings">
|
||||
<b-tooltip :label="$t('withdrawalSettings')" size="is-small" position="is-right" multilined>
|
||||
<SettingsIcon />
|
||||
</b-tooltip>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<b-input
|
||||
v-model="withdrawNote"
|
||||
:placeholder="$t('pleaseEnterYourNote')"
|
||||
:custom-class="hasErrorNote ? hasErrorNote.type : 'is-primary'"
|
||||
></b-input>
|
||||
<div v-if="hasErrorNote" class="help" :class="hasErrorNote.type">
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<p v-html="hasErrorNote.msg"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!hasErrorNote && depositTxHash" class="field field-withdraw">
|
||||
<div class="withdraw-data">
|
||||
<div class="withdraw-data-item">
|
||||
{{ $t('amount') }} <span>{{ selectedAmount }} {{ selectedStatisticCurrency }}</span>
|
||||
</div>
|
||||
<div class="withdraw-data-item">
|
||||
{{ $t('timePassed') }}
|
||||
<b-tooltip
|
||||
:active="notEnoughPassedTime"
|
||||
:label="$t('timePassedTooltip')"
|
||||
position="is-left"
|
||||
multilined
|
||||
size="is-large"
|
||||
:class="{ 'has-low-anonymity': notEnoughPassedTime }"
|
||||
>
|
||||
<span>{{ timePassed }}</span>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
<div class="withdraw-data-item">
|
||||
{{ $t('subsequentDeposits') }}
|
||||
<b-tooltip
|
||||
:active="notEnoughDeposits"
|
||||
:label="$t('subsequentDepositsTooltip')"
|
||||
position="is-left"
|
||||
multilined
|
||||
size="is-large"
|
||||
:class="{ 'has-low-anonymity': notEnoughDeposits }"
|
||||
>
|
||||
<span>{{ this.$tc('userDeposit', depositsPast) }}</span>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset>
|
||||
<div class="field withdraw-address">
|
||||
<div class="label-with-buttons">
|
||||
<div class="label">
|
||||
<span class="name">
|
||||
{{ $t('recipientAddress') }}
|
||||
</span>
|
||||
</div>
|
||||
<button class="button is-primary-text" @click="insertDonate">
|
||||
{{ $t('donate') }}
|
||||
</button>
|
||||
</div>
|
||||
<b-input
|
||||
v-model="withdrawAddress"
|
||||
:placeholder="$t('pleasePasteAddressHere')"
|
||||
:size="!withdrawAddress ? '' : isValidAddress ? 'is-primary' : 'is-warning'"
|
||||
></b-input>
|
||||
<p class="help">
|
||||
<span class="has-text-warning">{{ error.type }}</span> {{ error.message }}
|
||||
</p>
|
||||
</div>
|
||||
<RelayerTotal v-show="shouldShowRelayerTotal" />
|
||||
<WithdrawTotal
|
||||
v-show="shouldShowTotal"
|
||||
:currency="selectedStatisticCurrency"
|
||||
:withdraw-type="withdrawType"
|
||||
:eth-to-receive="ethToReceive"
|
||||
/>
|
||||
<b-tooltip
|
||||
class="is-block"
|
||||
:label="`${$t(tooltipText)}`"
|
||||
position="is-top"
|
||||
:active="shouldTooltipShow"
|
||||
multilined
|
||||
>
|
||||
<b-button
|
||||
type="is-primary is-fullwidth"
|
||||
class="slide-animation"
|
||||
:outlined="isLoading"
|
||||
:expanded="isLoading"
|
||||
:class="{ 'slide-animation-active': isLoading }"
|
||||
:disabled="isWithdrawalButtonDisable"
|
||||
:loading="isLoadingRelayers || isLoading"
|
||||
@click="onWithdraw"
|
||||
>
|
||||
{{ $t('withdrawButton') }}
|
||||
</b-button>
|
||||
</b-tooltip>
|
||||
</fieldset>
|
||||
</b-tab-item>
|
||||
</template>
|
||||
<script>
|
||||
/* eslint-disable no-console */
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { getTornadoKeys } from '@/store/snark'
|
||||
|
||||
import { parseNote } from '@/utils'
|
||||
import { DONATIONS_ADDRESS } from '@/constants'
|
||||
|
||||
import { LinkIcon, SettingsIcon } from '@/components/icons'
|
||||
import RelayerTotal from '@/components/withdraw/RelayerTotal'
|
||||
import WithdrawTotal from '@/components/withdraw/WithdrawTotal'
|
||||
import WithdrawModalBox from '@/components/withdraw/WithdrawModalBox'
|
||||
import SettingsModalBox from '@/components/settings/SettingsModalBox'
|
||||
|
||||
const { toChecksumAddress, isHexStrict, isAddress } = require('web3-utils')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LinkIcon,
|
||||
RelayerTotal,
|
||||
SettingsIcon,
|
||||
WithdrawTotal
|
||||
},
|
||||
props: {
|
||||
activeTab: {
|
||||
required: true,
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
withdrawAddress: '',
|
||||
withdrawNote: '',
|
||||
depositsPast: null,
|
||||
depositTxHash: null,
|
||||
depositTimestamp: null,
|
||||
isSpent: false,
|
||||
isLoading: false,
|
||||
isFileError: false,
|
||||
error: {
|
||||
type: null,
|
||||
message: ''
|
||||
},
|
||||
timePassed: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('application', ['note', 'errors', 'withdrawType', 'ethToReceive']),
|
||||
...mapState('relayer', ['isLoadingRelayers']),
|
||||
...mapGetters('txHashKeeper', ['txExplorerUrl']),
|
||||
...mapGetters('application', ['isNotEnoughTokens', 'selectedStatisticCurrency']),
|
||||
...mapGetters('metamask', ['networkConfig', 'netId', 'isLoggedIn', 'nativeCurrency']),
|
||||
notEnoughDeposits() {
|
||||
if (this.depositsPast < 5) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
shouldSettingsShow() {
|
||||
return !this.hasErrorNote && !this.error.message
|
||||
},
|
||||
hasErrorNote() {
|
||||
const note = this.withdrawNote.split('-')[4]
|
||||
if (typeof WebAssembly === 'undefined') {
|
||||
return {
|
||||
type: 'is-warning',
|
||||
msg: this.$t('turnOnWasm')
|
||||
}
|
||||
}
|
||||
if (!this.withdrawNote) {
|
||||
return { type: '', msg: '' }
|
||||
}
|
||||
if (!note || note.length < 126 || !isHexStrict(note)) {
|
||||
return { type: 'is-warning', msg: this.$t('noteIsInvalid') }
|
||||
}
|
||||
if (this.isSpent) {
|
||||
return { type: 'is-warning', msg: this.$t('noteHasBeenSpent') }
|
||||
}
|
||||
return false
|
||||
},
|
||||
withDisconnectedWallet() {
|
||||
return this.withdrawType === 'wallet' && !this.isLoggedIn
|
||||
},
|
||||
shouldTooltipShow() {
|
||||
return (!this.isWithdrawDisabled && this.isNotEnoughTokens) || this.withDisconnectedWallet
|
||||
},
|
||||
tooltipText() {
|
||||
if (this.withDisconnectedWallet) {
|
||||
return 'withDisconnectedWallet'
|
||||
}
|
||||
return 'notEnoughTokens'
|
||||
},
|
||||
isValidAddress() {
|
||||
return isAddress(this.withdrawAddress)
|
||||
},
|
||||
notEnoughPassedTime() {
|
||||
return this.$moment().unix() - Number(this.depositTimestamp) < 86400 // less than 24 hours
|
||||
},
|
||||
hasWarning() {
|
||||
return this.depositsPast < 5 || this.notEnoughPassedTime
|
||||
},
|
||||
isWithdrawDisabled() {
|
||||
return (
|
||||
this.isLoading ||
|
||||
!!this.error.type ||
|
||||
this.hasErrorNote ||
|
||||
(this.withdrawType === 'relayer' && !this.selectedRelayer) ||
|
||||
!this.isValidAddress
|
||||
)
|
||||
},
|
||||
isWithdrawalButtonDisable() {
|
||||
return (
|
||||
this.isWithdrawDisabled || this.isNotEnoughTokens || this.isFileError || this.withDisconnectedWallet
|
||||
)
|
||||
},
|
||||
selectedRelayer() {
|
||||
return this.$store.state.relayer.selectedRelayer.name
|
||||
},
|
||||
selectedAmount() {
|
||||
return this.$store.state.application.selectedStatistic.amount
|
||||
},
|
||||
tokens() {
|
||||
return this.networkConfig.tokens
|
||||
},
|
||||
shouldShowTotal() {
|
||||
return this.isValidAddress && !this.isWithdrawDisabled
|
||||
},
|
||||
shouldShowRelayerTotal() {
|
||||
return this.withdrawType === 'relayer' && this.shouldShowTotal
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
netId(netId, oldNetId) {
|
||||
if (netId !== oldNetId) {
|
||||
const [, , , noteNetId] = this.withdrawNote.split('-')
|
||||
|
||||
if (Number(noteNetId) !== netId && noteNetId) {
|
||||
this.error = {
|
||||
type: this.$t('error'),
|
||||
message: this.$t('changeNetworkNote')
|
||||
}
|
||||
} else {
|
||||
this.error = {
|
||||
type: '',
|
||||
message: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
errors: {
|
||||
handler(errors) {
|
||||
console.log('error', errors)
|
||||
this.error = {
|
||||
type: errors.length ? this.$t('error') : null,
|
||||
message: errors[errors.length - 1]
|
||||
}
|
||||
if (this.error.message) {
|
||||
this.$store.dispatch('notice/addNoticeWithInterval', {
|
||||
notice: {
|
||||
untranslatedTitle: this.error.message,
|
||||
type: 'warning'
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
withdrawNote: {
|
||||
async handler(withdrawNote) {
|
||||
this.error = {
|
||||
type: '',
|
||||
message: ''
|
||||
}
|
||||
|
||||
try {
|
||||
this.$store.dispatch('loading/enable', { message: this.$t('gettingTheNoteData') })
|
||||
this.isSpent = false
|
||||
this.depositsPast = null
|
||||
this.depositTxHash = null
|
||||
this.depositTimestamp = null
|
||||
|
||||
if (!this.hasErrorNote) {
|
||||
const [tornadoPrefix, currency, amount, noteNetId, note] = this.withdrawNote.split('-')
|
||||
if (tornadoPrefix !== 'tornado') {
|
||||
this.$store.dispatch('loading/disable')
|
||||
this.withdrawNote = `tornado-${currency}-${amount}-${noteNetId}-${note}`
|
||||
return
|
||||
}
|
||||
this.getLogs(withdrawNote)
|
||||
this.$store.commit('application/SET_WITHDRAW_NOTE', withdrawNote)
|
||||
const netIdMissmatch = Number(noteNetId) !== Number(this.netId)
|
||||
|
||||
if (netIdMissmatch) {
|
||||
throw new Error(this.$t('changeNetworkNote'))
|
||||
}
|
||||
|
||||
const event = await this.$store.dispatch('application/loadDepositEvent', { withdrawNote })
|
||||
|
||||
if (!event) {
|
||||
throw new Error(this.$t('thereIsNoRelatedDeposit'))
|
||||
}
|
||||
|
||||
const { timestamp, txHash, isSpent, depositsPast = 0 } = event
|
||||
|
||||
if (isSpent) {
|
||||
this.$store.dispatch('notice/addNoticeWithInterval', {
|
||||
notice: {
|
||||
title: 'noteWasAlreadySpent',
|
||||
type: 'warning'
|
||||
},
|
||||
interval: 5000
|
||||
})
|
||||
}
|
||||
this.$store.dispatch('application/setAndUpdateStatistic', { currency, amount: Number(amount) })
|
||||
if (currency !== this.nativeCurrency) {
|
||||
this.$store.dispatch('application/setDefaultEthToReceive', { currency })
|
||||
}
|
||||
this.depositsPast = Number(depositsPast) <= 0 ? 0 : depositsPast
|
||||
this.depositTxHash = txHash
|
||||
this.depositTimestamp = timestamp
|
||||
this.isSpent = isSpent
|
||||
}
|
||||
} catch (err) {
|
||||
this.error = {
|
||||
type: this.$t('error'),
|
||||
message: err.message
|
||||
}
|
||||
} finally {
|
||||
this.$store.dispatch('loading/disable')
|
||||
}
|
||||
}
|
||||
},
|
||||
'$i18n.locale'() {
|
||||
this.timePastToRender()
|
||||
},
|
||||
depositTimestamp() {
|
||||
this.timePastToRender()
|
||||
},
|
||||
activeTab(newTab, oldTab) {
|
||||
if (newTab !== oldTab && newTab === 1) {
|
||||
this.error = {
|
||||
type: '',
|
||||
message: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$emit('get-key', this.getKeys)
|
||||
},
|
||||
mounted() {
|
||||
if (this.$route.query.note) {
|
||||
this.withdrawNote = this.$route.query.note
|
||||
}
|
||||
this.$root.$on('resetWithdraw', () => {
|
||||
this.withdrawAddress = ''
|
||||
this.withdrawNote = ''
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getKeys() {
|
||||
try {
|
||||
this.isFileError = false
|
||||
this.isLoading = true
|
||||
this.getProgress(0)
|
||||
await getTornadoKeys(this.getProgress)
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('getKeys has error:', err.message)
|
||||
|
||||
this.$store.dispatch(
|
||||
'notice/addNoticeWithInterval',
|
||||
{
|
||||
notice: {
|
||||
title: 'fetchFile',
|
||||
type: 'warning'
|
||||
}
|
||||
},
|
||||
{ root: true }
|
||||
)
|
||||
this.error = {
|
||||
type: this.$t('downloadError'),
|
||||
message: this.$t('fetchFile')
|
||||
}
|
||||
this.isFileError = true
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
getProgress(value) {
|
||||
document.documentElement.style.setProperty('--width-animation', `${value}%`)
|
||||
},
|
||||
getLogs(note) {
|
||||
try {
|
||||
if (!note) {
|
||||
return
|
||||
}
|
||||
|
||||
const { commitmentHex, nullifierHex } = parseNote(note)
|
||||
|
||||
console.log('\n\nYOUR NOTE DATA:')
|
||||
console.log('note:', note)
|
||||
|
||||
console.log('commitment:', commitmentHex)
|
||||
console.log('nullifierHash:', nullifierHex)
|
||||
} catch (err) {
|
||||
console.log(`Get logs: ${err.message}`)
|
||||
}
|
||||
},
|
||||
onWithdraw() {
|
||||
const note = this.withdrawNote.split('-')[4]
|
||||
if (note.length !== 126) {
|
||||
this.error = {
|
||||
type: this.$t('validationError'),
|
||||
message: this.$t('noteIsInvalid')
|
||||
}
|
||||
return
|
||||
}
|
||||
try {
|
||||
this.withdrawAddress = toChecksumAddress(this.withdrawAddress)
|
||||
this.$store.dispatch('application/prepareWithdraw', {
|
||||
note: this.withdrawNote,
|
||||
recipient: this.withdrawAddress
|
||||
})
|
||||
this.error.type = null
|
||||
this.currentModal = this.$buefy.modal.open({
|
||||
parent: this,
|
||||
component: WithdrawModalBox,
|
||||
hasModalCard: true,
|
||||
width: 440,
|
||||
props: {
|
||||
note: this.withdrawNote,
|
||||
withdrawType: this.withdrawType
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
this.error = {
|
||||
type: this.$t('validationError'),
|
||||
message: this.$t('recipientAddressIsInvalid')
|
||||
}
|
||||
console.error('error', e)
|
||||
}
|
||||
},
|
||||
onSettings() {
|
||||
this.$buefy.modal.open({
|
||||
parent: this,
|
||||
component: SettingsModalBox,
|
||||
hasModalCard: true,
|
||||
width: 440,
|
||||
props: {
|
||||
currency: this.selectedStatisticCurrency
|
||||
},
|
||||
customClass: 'is-pinned'
|
||||
})
|
||||
},
|
||||
timePastToRender() {
|
||||
this.timePassed = this.$moment.unix(this.depositTimestamp).fromNow(true)
|
||||
},
|
||||
insertDonate() {
|
||||
this.withdrawAddress = DONATIONS_ADDRESS
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:root {
|
||||
--width-animation: 0;
|
||||
}
|
||||
|
||||
.slide-animation {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--width-animation);
|
||||
height: 100%;
|
||||
background-color: #94febf;
|
||||
animation-fill-mode: backwards;
|
||||
}
|
||||
|
||||
::v-deep span {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&-active {
|
||||
::v-deep span,
|
||||
&:after {
|
||||
filter: invert(0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
88
components/withdraw/WithdrawModalBox.vue
Normal file
88
components/withdraw/WithdrawModalBox.vue
Normal file
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<div class="modal-card box box-modal">
|
||||
<header class="box-modal-header is-spaced">
|
||||
<div class="box-modal-title">{{ $t('withdrawalConfirmation') }}</div>
|
||||
<button type="button" class="delete" @click="$emit('close')" />
|
||||
</header>
|
||||
<div class="note">
|
||||
{{ message }}
|
||||
</div>
|
||||
<b-button type="is-primary is-fullwidth" @click="_sendWithdraw">
|
||||
{{ $t('confirm') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
/* eslint-disable no-console */
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
note: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
withdrawType: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
message: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('application', ['notes', 'errors']),
|
||||
withdrawalMethod() {
|
||||
if (this.withdrawType === 'wallet') {
|
||||
return 'application/withdraw'
|
||||
}
|
||||
|
||||
return 'relayer/relayTornadoWithdraw'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
notes(newNotes) {
|
||||
if (newNotes[this.note]) {
|
||||
this.$store.dispatch('loading/disable')
|
||||
this.message = this.$t('yourZkSnarkProofHasBeenSuccesfullyGenerated')
|
||||
}
|
||||
},
|
||||
errors: {
|
||||
handler(type) {
|
||||
this.$store.dispatch('loading/disable')
|
||||
this.$parent.close()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('loading/enable', { message: this.$t('generatingProof') })
|
||||
},
|
||||
methods: {
|
||||
async _sendWithdraw() {
|
||||
this.$store.dispatch('loading/enable', { message: this.$t('preparingTransactionData') })
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(this.withdrawalMethod, {
|
||||
note: this.note
|
||||
})
|
||||
this.$root.$emit('resetWithdraw')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.$store.dispatch('notice/addNoticeWithInterval', {
|
||||
notice: {
|
||||
untranslatedTitle: e.message,
|
||||
type: 'danger'
|
||||
},
|
||||
interval: 3000
|
||||
})
|
||||
} finally {
|
||||
this.$store.dispatch('loading/disable')
|
||||
this.$parent.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
139
components/withdraw/WithdrawTotal.vue
Normal file
139
components/withdraw/WithdrawTotal.vue
Normal file
|
@ -0,0 +1,139 @@
|
|||
<template>
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
{{ $t('total') }}
|
||||
</div>
|
||||
<div class="withdraw-data">
|
||||
<div v-if="isTokenSelected" class="withdraw-data-item">
|
||||
{{ $t('noteBalance') }}
|
||||
<span>
|
||||
{{ selectedStatistic.amount }}
|
||||
{{ currency }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="withdrawType === 'relayer'" class="withdraw-data-item">
|
||||
{{ $t('gasPrice') }}
|
||||
<span>{{ gasPrices.fast }} Gwei</span>
|
||||
</div>
|
||||
<div v-if="withdrawType === 'relayer'" class="withdraw-data-item">
|
||||
{{ $t('networkFee') }}
|
||||
<span>{{ networkFeeInEth }} {{ networkCurrency }}</span>
|
||||
</div>
|
||||
<div v-if="withdrawType === 'relayer'" class="withdraw-data-item">
|
||||
{{ $t('relayerFee') }}
|
||||
<span>{{ toDecimals(relayerFee, null, 6) }} {{ currency }}</span>
|
||||
</div>
|
||||
<div v-if="withdrawType === 'relayer'" class="withdraw-data-item">
|
||||
{{ $t('totalFee') }}
|
||||
<span>{{ toDecimals(totalRelayerFee, null, 6) }} {{ currency }}</span>
|
||||
</div>
|
||||
<div v-if="isTokenSelected" class="withdraw-data-item">
|
||||
{{ $t('ethPurchase', { currency: networkCurrency }) }}
|
||||
<span>{{ toDecimals(ethToReceiveInToken, null, 6) }} {{ currency }}</span>
|
||||
</div>
|
||||
<hr v-if="withdrawType === 'relayer'" />
|
||||
<div class="withdraw-data-item">
|
||||
{{ $t('tokensToReceive') }}
|
||||
<span>{{ total }} {{ currency }}</span>
|
||||
</div>
|
||||
<div v-if="isTokenSelected" class="withdraw-data-item">
|
||||
<span class="is-alone">{{ ethToReceiveFromWei }} {{ networkCurrency }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { decimalPlaces } from '@/utils'
|
||||
const { fromWei, toBN } = require('web3-utils')
|
||||
|
||||
export default {
|
||||
props: {
|
||||
currency: {
|
||||
type: String,
|
||||
default: 'ETH'
|
||||
},
|
||||
withdrawType: {
|
||||
type: String,
|
||||
default: 'wallet'
|
||||
},
|
||||
ethToReceive: {
|
||||
type: String,
|
||||
default: '20000000000000000'
|
||||
},
|
||||
serviceFee: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('application', ['selectedStatistic']),
|
||||
...mapGetters('metamask', ['networkConfig', 'nativeCurrency']),
|
||||
...mapGetters('metamask', {
|
||||
networkCurrency: 'currency'
|
||||
}),
|
||||
...mapGetters('gasPrices', ['gasPrices']),
|
||||
...mapGetters('token', ['toDecimals', 'fromDecimals']),
|
||||
...mapGetters('application', ['networkFee']),
|
||||
...mapGetters('price', ['tokenRate']),
|
||||
networkFeeInEth() {
|
||||
return fromWei(this.networkFee)
|
||||
},
|
||||
relayerFee() {
|
||||
const { amount } = this.selectedStatistic
|
||||
const total = toBN(this.fromDecimals(amount.toString()))
|
||||
const fee = this.serviceFee || this.$store.state.relayer.selectedRelayer.tornadoServiceFee
|
||||
const decimalsPoint = decimalPlaces(fee)
|
||||
const roundDecimal = 10 ** decimalsPoint
|
||||
const aroundFee = toBN(parseInt(fee * roundDecimal, 10))
|
||||
const tornadoServiceFee = total.mul(toBN(aroundFee)).div(toBN(roundDecimal * 100))
|
||||
return tornadoServiceFee
|
||||
},
|
||||
totalRelayerFee() {
|
||||
const tornadoServiceFee = this.relayerFee
|
||||
const { currency } = this.selectedStatistic
|
||||
const { decimals } = this.networkConfig.tokens[currency]
|
||||
const ethFee = this.networkFee
|
||||
if (currency === this.nativeCurrency) {
|
||||
return ethFee.add(tornadoServiceFee)
|
||||
}
|
||||
const tokenFee = ethFee.mul(toBN(10 ** decimals)).div(toBN(this.tokenRate))
|
||||
return tokenFee.add(tornadoServiceFee)
|
||||
},
|
||||
isTokenSelected() {
|
||||
return (
|
||||
this.withdrawType === 'relayer' &&
|
||||
this.selectedStatistic.currency !== this.nativeCurrency &&
|
||||
this.currency !== 'TORN'
|
||||
)
|
||||
},
|
||||
ethToReceiveInToken() {
|
||||
const { currency } = this.selectedStatistic
|
||||
const { decimals } = this.networkConfig.tokens[currency]
|
||||
const price = this.tokenRate
|
||||
return toBN(this.ethToReceive)
|
||||
.mul(toBN(10 ** decimals))
|
||||
.div(toBN(price))
|
||||
},
|
||||
ethToReceiveFromWei() {
|
||||
return fromWei(this.ethToReceive)
|
||||
},
|
||||
total() {
|
||||
const { amount, currency } = this.selectedStatistic
|
||||
let total = toBN(this.fromDecimals(amount.toString()))
|
||||
|
||||
if (this.withdrawType === 'relayer') {
|
||||
const relayerFee = this.totalRelayerFee
|
||||
|
||||
if (currency === this.nativeCurrency) {
|
||||
total = total.sub(relayerFee)
|
||||
} else {
|
||||
total = total.sub(relayerFee).sub(this.ethToReceiveInToken)
|
||||
}
|
||||
}
|
||||
|
||||
return this.toDecimals(total, null, 6)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue