mirror of
https://github.com/tornadocash/tornado-classic-ui
synced 2024-02-02 15:04:09 +01:00
452 lines
13 KiB
JavaScript
452 lines
13 KiB
JavaScript
/* eslint-disable no-console */
|
|
import { hexToNumber } from 'web3-utils'
|
|
|
|
import txStatus from './txStatus'
|
|
|
|
import { eventsType } from '@/constants'
|
|
import { createChainIdState, parseNote } from '@/utils'
|
|
|
|
const initialState = createChainIdState({
|
|
txs: {},
|
|
govTxs: {},
|
|
encryptedTxs: {}
|
|
})
|
|
|
|
export const state = () => {
|
|
return initialState
|
|
}
|
|
|
|
export const getters = {
|
|
txExplorerUrl: (state, getters, rootState, rootGetters) => (txHash) => {
|
|
const { explorerUrl } = rootGetters['metamask/networkConfig']
|
|
return explorerUrl.tx + txHash
|
|
},
|
|
addressExplorerUrl: (state, getters, rootState, rootGetters) => (address) => {
|
|
const { explorerUrl } = rootGetters['metamask/networkConfig']
|
|
return explorerUrl.address + address
|
|
},
|
|
blockExplorerUrl: (state, getters, rootState, rootGetters) => (block) => {
|
|
const { explorerUrl } = rootGetters['metamask/networkConfig']
|
|
return explorerUrl.block + block
|
|
},
|
|
encryptedTxs: (state, getters, rootState, rootGetters) => {
|
|
const netId = rootGetters['metamask/netId']
|
|
|
|
const txsToRender = Object.entries(state[`netId${netId}`].encryptedTxs)
|
|
.reverse()
|
|
.map(([txHash, tx]) => {
|
|
return {
|
|
isEncrypted: true,
|
|
txHash,
|
|
status,
|
|
...tx
|
|
}
|
|
})
|
|
return txsToRender
|
|
},
|
|
txs: (state, getters, rootState, rootGetters) => {
|
|
const netId = rootGetters['metamask/netId']
|
|
const txsToRender = Object.entries(state[`netId${netId}`].txs)
|
|
.reverse()
|
|
.map(([txHash, tx]) => {
|
|
return {
|
|
txHash,
|
|
status,
|
|
...tx
|
|
}
|
|
})
|
|
return txsToRender
|
|
},
|
|
allTxs: (state, getters) => {
|
|
const { txs, encryptedTxs } = getters
|
|
return txs.concat(encryptedTxs)
|
|
},
|
|
allTxsHash: (state, getters, rootState, rootGetters) => {
|
|
const netId = rootGetters['metamask/netId']
|
|
return Object.entries(state[`netId${netId}`].txs)
|
|
.reverse()
|
|
.map(([txHash]) => txHash)
|
|
},
|
|
txStatusClass: () => (status) => {
|
|
let cssClass
|
|
switch (status) {
|
|
case txStatus.waitingForReciept:
|
|
cssClass = 'is-loading'
|
|
break
|
|
case txStatus.success:
|
|
cssClass = 'is-success'
|
|
break
|
|
case txStatus.fail:
|
|
cssClass = 'is-danger'
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
return cssClass
|
|
}
|
|
}
|
|
|
|
export const mutations = {
|
|
SAVE_TX_HASH(state, { storeType = 'txs', amount = '0', note = null, netId, txHash, status, ...rest }) {
|
|
this._vm.$set(state[`netId${netId}`][storeType], [txHash], {
|
|
...rest,
|
|
status: status || txStatus.waitingForReciept,
|
|
note,
|
|
amount
|
|
})
|
|
},
|
|
CHANGE_TX_STATUS(state, { storeType = 'txs', txHash, status, blockNumber, netId }) {
|
|
this._vm.$set(state[`netId${netId}`][storeType][txHash], 'status', status)
|
|
this._vm.$set(state[`netId${netId}`][storeType][txHash], 'blockNumber', blockNumber)
|
|
},
|
|
SET_SPENT(state, { netId, storeType = 'txs', txHash }) {
|
|
this._vm.$set(state[`netId${netId}`][storeType][txHash], 'isSpent', true)
|
|
},
|
|
DELETE_TX(state, { storeType = 'txs', txHash }) {
|
|
const netId = this._vm['metamask/netId']
|
|
this._vm.$delete(state[`netId${netId}`][storeType], txHash)
|
|
},
|
|
UPDATE_DEPOSIT(state, { storeType = 'txs', txHash, netId, withdrawTxHash, timestamp, status, ...rest }) {
|
|
const tx = state[`netId${netId}`][storeType][txHash]
|
|
this._vm.$delete(state[`netId${netId}`][storeType], txHash)
|
|
|
|
this._vm.$set(state[`netId${netId}`][storeType], withdrawTxHash, {
|
|
...tx,
|
|
timestamp,
|
|
depositTxHash: txHash,
|
|
txHash: withdrawTxHash,
|
|
depositBlock: tx.blockNumber,
|
|
status: status || txStatus.waitingForReciept,
|
|
...rest
|
|
})
|
|
}
|
|
}
|
|
|
|
export const actions = {
|
|
async getInstances({ rootGetters }, { txs }) {
|
|
const eventsInterface = rootGetters['application/eventsInterface']
|
|
|
|
const instances = txs.reduce((acc, curr) => {
|
|
const [, currency, amount, netId] = curr.prefix.split('-')
|
|
|
|
const name = `${amount}${currency}`
|
|
if (!acc[name]) {
|
|
const service = eventsInterface.getService({ netId, amount, currency })
|
|
acc[name] = { currency, amount, netId, service }
|
|
}
|
|
return acc
|
|
}, {})
|
|
|
|
await Promise.all(
|
|
[].concat(
|
|
Object.values(instances).map((instance) => instance.service.updateEvents(eventsType.DEPOSIT)),
|
|
Object.values(instances).map((instance) => instance.service.updateEvents(eventsType.WITHDRAWAL))
|
|
)
|
|
)
|
|
|
|
return instances
|
|
},
|
|
async checkPendingEncryptedTransaction({ dispatch, getters }) {
|
|
const transactions = getters.encryptedTxs
|
|
|
|
const pendingTxs = transactions.filter((tx) => tx.status === 1)
|
|
|
|
const instances = await dispatch('getInstances', { txs: pendingTxs })
|
|
|
|
for (const tx of pendingTxs) {
|
|
const [, currency, amount, netId] = tx.prefix.split('-')
|
|
|
|
dispatch('checkSpeedUpEncryptedTx', {
|
|
netId,
|
|
txHash: tx.txHash,
|
|
type: eventsType.DEPOSIT,
|
|
commitment: tx.commitmentHex,
|
|
service: instances[`${amount}${currency}`]
|
|
})
|
|
}
|
|
},
|
|
async checkSpeedUpEncryptedTx({ commit, dispatch }, { txHash, commitment, netId, service, type }) {
|
|
try {
|
|
const result = await this.$provider.web3.eth.getTransactionReceipt(txHash)
|
|
|
|
if (result) {
|
|
const status = result.status === '0x0' ? txStatus.fail : txStatus.success
|
|
|
|
commit('CHANGE_TX_STATUS', {
|
|
netId,
|
|
txHash,
|
|
status,
|
|
storeType: 'encryptedTxs',
|
|
blockNumber: hexToNumber(result.blockNumber)
|
|
})
|
|
} else {
|
|
const foundEvent = await service.findEvent({ eventName: 'commitment', eventToFind: commitment, type })
|
|
|
|
if (foundEvent) {
|
|
commit('UPDATE_DEPOSIT', {
|
|
netId,
|
|
txHash,
|
|
storeType: 'encryptedTxs',
|
|
status: txStatus.success,
|
|
timestamp: foundEvent.timestamp,
|
|
withdrawTxHash: foundEvent.txHash,
|
|
blockNumber: foundEvent.depositBlock
|
|
})
|
|
}
|
|
}
|
|
} catch (err) {
|
|
throw new Error(`Method checkSpeedUpEncryptedTx has error ${err.message}`)
|
|
}
|
|
},
|
|
async cleanEncryptedTxs({ getters, commit, dispatch }) {
|
|
getters.encryptedTxs.forEach(({ status, txHash, type }) => {
|
|
if (status === txStatus.fail) {
|
|
commit('DELETE_TX', { txHash, storeType: 'encryptedTxs' })
|
|
}
|
|
})
|
|
|
|
const instances = await dispatch('getInstances', {
|
|
txs: getters.encryptedTxs
|
|
})
|
|
|
|
for (const tx of getters.encryptedTxs) {
|
|
if (!tx.isSpent) {
|
|
const { currency, amount, netId, nullifierHex } = parseNote(`${tx.prefix}-${tx.note}`)
|
|
|
|
const isSpent = await instances[`${amount}${currency}`].service.findEvent({
|
|
eventName: 'nullifierHash',
|
|
eventToFind: nullifierHex,
|
|
type: eventsType.WITHDRAWAL
|
|
})
|
|
|
|
if (isSpent) {
|
|
commit('SET_SPENT', { txHash: tx.txHash, storeType: 'encryptedTxs', netId })
|
|
}
|
|
}
|
|
}
|
|
},
|
|
checkPendingTransaction({ getters, dispatch }) {
|
|
const transactions = getters.txs
|
|
|
|
const pendingTxs = transactions.filter((tx) => tx.status === 1)
|
|
|
|
for (const tx of pendingTxs) {
|
|
const [, , , netId] = tx.prefix.split('-')
|
|
|
|
dispatch('checkPendingSpeedUpTx', { txHash: tx.txHash, note: `${tx.prefix}-${tx.note}`, netId })
|
|
}
|
|
},
|
|
async checkPendingSpeedUpTx({ commit, dispatch }, { txHash, netId, note }) {
|
|
try {
|
|
const result = await this.$provider.web3.eth.getTransactionReceipt(txHash)
|
|
|
|
if (result) {
|
|
const status = result.status === '0x0' ? txStatus.fail : txStatus.success
|
|
|
|
commit('CHANGE_TX_STATUS', {
|
|
storeType: 'txs',
|
|
txHash,
|
|
blockNumber: hexToNumber(result.blockNumber),
|
|
status,
|
|
netId
|
|
})
|
|
} else {
|
|
const response = await dispatch(
|
|
'application/loadDepositEvent',
|
|
{ withdrawNote: note },
|
|
{ root: true }
|
|
)
|
|
|
|
if (response) {
|
|
commit('UPDATE_DEPOSIT', {
|
|
netId,
|
|
txHash,
|
|
storeType: 'txs',
|
|
status: txStatus.success,
|
|
timestamp: response.timestamp,
|
|
withdrawTxHash: response.txHash,
|
|
blockNumber: response.depositBlock
|
|
})
|
|
}
|
|
}
|
|
} catch (err) {
|
|
throw new Error(`Method checkPendingSpeedUpTx has error ${err.message}`)
|
|
}
|
|
},
|
|
async runTxWatcher({ commit, dispatch }, { storeType = 'txs', txHash, netId, isSaving = true }) {
|
|
console.log('runTxWatcher storeType txHash, netId', storeType, txHash, netId)
|
|
try {
|
|
// eslint-disable-next-line prefer-const
|
|
let { status, blockNumber } = await this.$provider.waitForTxReceipt({ txHash })
|
|
status = status === '0x0' ? txStatus.fail : txStatus.success
|
|
|
|
if (isSaving) {
|
|
commit('CHANGE_TX_STATUS', {
|
|
storeType,
|
|
txHash,
|
|
blockNumber: hexToNumber(blockNumber),
|
|
status,
|
|
netId
|
|
})
|
|
}
|
|
|
|
return status === txStatus.success
|
|
} catch (e) {
|
|
console.error('runTxWatcher', e)
|
|
return false
|
|
}
|
|
},
|
|
async runTxWatcherWithNotifications(
|
|
{ dispatch },
|
|
{ title, successTitle, storeType = 'txs', txHash, netId, onSuccess, isSaving }
|
|
) {
|
|
try {
|
|
const noticeId = await dispatch(
|
|
'notice/addNotice',
|
|
{
|
|
notice: {
|
|
title,
|
|
type: 'loading',
|
|
txHash
|
|
}
|
|
},
|
|
{ root: true }
|
|
)
|
|
const success = await dispatch('runTxWatcher', { storeType, txHash, netId, isSaving })
|
|
if (success) {
|
|
dispatch(
|
|
'notice/updateNotice',
|
|
{
|
|
id: noticeId,
|
|
notice: {
|
|
title: successTitle,
|
|
type: 'success'
|
|
},
|
|
interval: 10000
|
|
},
|
|
{ root: true }
|
|
)
|
|
if (typeof onSuccess === 'function') {
|
|
onSuccess(txHash)
|
|
}
|
|
} else {
|
|
dispatch(
|
|
'notice/updateNotice',
|
|
{
|
|
id: noticeId,
|
|
notice: {
|
|
title: 'transactionFailed',
|
|
type: 'danger'
|
|
}
|
|
},
|
|
{ root: true }
|
|
)
|
|
}
|
|
return success
|
|
} catch (e) {
|
|
console.error('runTxWatcherWithNotifications', e)
|
|
return false
|
|
}
|
|
},
|
|
async cleanTxs({ getters, commit, dispatch }) {
|
|
// isSpentArray
|
|
getters.txs.forEach(({ status, txHash, type }) => {
|
|
if (status === txStatus.fail) {
|
|
commit('DELETE_TX', { txHash })
|
|
}
|
|
})
|
|
|
|
const instances = await dispatch('getInstances', {
|
|
txs: getters.txs
|
|
})
|
|
|
|
for (const tx of getters.txs) {
|
|
if (tx && !tx.isSpent) {
|
|
const { currency, amount, netId, nullifierHex } = parseNote(`${tx.prefix}-${tx.note}`)
|
|
|
|
const isSpent = await instances[`${amount}${currency}`].service.findEvent({
|
|
eventName: 'nullifierHash',
|
|
eventToFind: nullifierHex,
|
|
type: eventsType.WITHDRAWAL
|
|
})
|
|
|
|
if (isSpent) {
|
|
commit('SET_SPENT', { txHash: tx.txHash, netId })
|
|
}
|
|
}
|
|
}
|
|
},
|
|
async updateDeposit(
|
|
{ getters, commit, dispatch, rootGetters },
|
|
{ netId, type = 'tornado', action = 'Withdraw', note, txHash, fee, amount, currency }
|
|
) {
|
|
const timestamp = Math.round(new Date().getTime() / 1000)
|
|
|
|
let txMutation = 'SAVE_TX_HASH'
|
|
const tx = {
|
|
txHash,
|
|
type: action,
|
|
amount,
|
|
currency,
|
|
fee,
|
|
netId,
|
|
timestamp,
|
|
status: 2
|
|
}
|
|
|
|
if (type === 'tornado') {
|
|
const [tornado, , , , hexNote] = note.split('-')
|
|
const { commitmentHex } = parseNote(note)
|
|
tx.prefix = `${tornado}-${currency}-${amount}-${netId}`
|
|
tx.isSpent = true
|
|
|
|
const encryptedTxs = getters.encryptedTxs
|
|
const encrypted = encryptedTxs.find((tx) => {
|
|
return tx.commitmentHex === commitmentHex
|
|
})
|
|
|
|
tx.storeType = encrypted ? 'encryptedTxs' : 'txs'
|
|
|
|
const deposit =
|
|
encrypted ||
|
|
getters.txs.find(({ note }) => {
|
|
return note === hexNote
|
|
})
|
|
|
|
tx.note = encrypted ? encrypted.note : hexNote
|
|
|
|
const blockNumber = await dispatch('getBlockNumber', { txHash })
|
|
tx.blockNumber = blockNumber
|
|
|
|
if (deposit && deposit.txHash) {
|
|
txMutation = 'UPDATE_DEPOSIT'
|
|
tx.txHash = deposit.txHash
|
|
tx.withdrawTxHash = txHash
|
|
} else {
|
|
const events = await dispatch('application/loadDepositEvent', { withdrawNote: note }, { root: true })
|
|
|
|
tx.withdrawTxHash = txHash
|
|
tx.txHash = events.txHash
|
|
tx.depositBlock = events.depositBlock
|
|
tx.index = events.leafIndex
|
|
}
|
|
}
|
|
|
|
commit(txMutation, tx)
|
|
},
|
|
async getBlockNumber({ rootState }, { txHash }) {
|
|
try {
|
|
const { netId } = rootState.metamask
|
|
const { url } = rootState.settings[`netId${netId}`].rpc
|
|
|
|
const web3 = this.$provider.getWeb3(url)
|
|
|
|
const { blockNumber } = await web3.eth.getTransaction(txHash)
|
|
|
|
return blockNumber
|
|
} catch (err) {
|
|
console.log('getBlockNumber has error:', err.message)
|
|
}
|
|
}
|
|
}
|