forked from tornadocash/classic-ui
220 lines
6.3 KiB
JavaScript
220 lines
6.3 KiB
JavaScript
/* eslint-disable no-console */
|
|
import Web3 from 'web3'
|
|
import { toBN } from 'web3-utils'
|
|
import tornABI from '../abis/ERC20.abi.json'
|
|
import PermitSigner from '../lib/Permit'
|
|
|
|
import { isWalletRejection } from '@/utils'
|
|
|
|
const { toWei, toHex, numberToHex } = require('web3-utils')
|
|
|
|
const state = () => {
|
|
return {
|
|
approvalAmount: 'unlimited',
|
|
allowance: '0',
|
|
balance: '',
|
|
signature: {
|
|
v: '',
|
|
r: '',
|
|
s: '',
|
|
amount: '',
|
|
deadline: 0
|
|
}
|
|
}
|
|
}
|
|
|
|
const getters = {
|
|
tokenContract: (state, getters, rootState, rootGetters) => {
|
|
const tornContract = rootGetters['metamask/networkConfig']['torn.contract.tornadocash.eth']
|
|
const { url } = rootGetters['settings/currentRpc']
|
|
const web3 = new Web3(url)
|
|
return new web3.eth.Contract(tornABI, tornContract)
|
|
}
|
|
}
|
|
|
|
const mutations = {
|
|
SET_APPROVAL_AMOUNT(state, { approvalAmount }) {
|
|
state.approvalAmount = approvalAmount
|
|
},
|
|
SAVE_ALLOWANCE(state, { allowance }) {
|
|
this._vm.$set(state, 'allowance', allowance)
|
|
},
|
|
SAVE_BALANCE(state, { balance }) {
|
|
this._vm.$set(state, 'balance', balance)
|
|
},
|
|
SAVE_SIGNATURE(state, { v, r, s, amount, deadline }) {
|
|
this._vm.$set(state, 'signature', { v, r, s, amount, deadline })
|
|
},
|
|
REMOVE_SIGNATURE(state) {
|
|
this._vm.$set(state, 'signature', { v: '', r: '', s: '', amount: '', deadline: 0 })
|
|
}
|
|
}
|
|
|
|
const actions = {
|
|
async approve({ rootState, getters, commit, dispatch, rootGetters, state }, { amount }) {
|
|
try {
|
|
console.log('call approve')
|
|
const netId = rootGetters['metamask/netId']
|
|
const govInstance = rootGetters['governance/gov/govContract']({ netId })
|
|
const amountToApprove = toHex(toWei(amount.toString()))
|
|
const data = getters.tokenContract.methods
|
|
.approve(govInstance._address, amountToApprove.toString())
|
|
.encodeABI()
|
|
const gas = 100000
|
|
|
|
const callParams = {
|
|
method: 'eth_sendTransaction',
|
|
params: {
|
|
data,
|
|
gas: numberToHex(gas + 100000),
|
|
to: getters.tokenContract._address
|
|
},
|
|
watcherParams: {
|
|
title: 'approve',
|
|
successTitle: 'approved'
|
|
},
|
|
isSaving: false
|
|
}
|
|
|
|
await dispatch('metamask/sendTransaction', callParams, { root: true })
|
|
|
|
dispatch('fetchTokenAllowance')
|
|
} catch (e) {
|
|
console.error('approve action', e)
|
|
throw new Error(e.message)
|
|
}
|
|
},
|
|
async fetchTokenAllowance({ getters, rootGetters, commit, rootState }) {
|
|
const netId = rootGetters['metamask/netId']
|
|
const { ethAccount } = rootState.metamask
|
|
|
|
if (!ethAccount) {
|
|
return
|
|
}
|
|
|
|
const govInstance = rootGetters['governance/gov/govContract']({ netId })
|
|
try {
|
|
const allowance = await getters.tokenContract.methods.allowance(ethAccount, govInstance._address).call()
|
|
commit('SAVE_ALLOWANCE', { allowance })
|
|
|
|
return allowance
|
|
} catch (e) {
|
|
console.error('fetchTokenAllowance', e.message)
|
|
}
|
|
},
|
|
async fetchTokenBalance({ state, getters, rootGetters, commit, rootState }) {
|
|
try {
|
|
const { ethAccount } = rootState.metamask
|
|
if (ethAccount) {
|
|
const balance = await getters.tokenContract.methods.balanceOf(ethAccount).call()
|
|
console.log('torn', balance)
|
|
commit('SAVE_BALANCE', { balance })
|
|
}
|
|
} catch (e) {
|
|
console.error('fetchTokenBalance', e.message)
|
|
}
|
|
},
|
|
async detectedAllowance({ dispatch }, initValue) {
|
|
try {
|
|
const allowance = await dispatch('fetchTokenAllowance')
|
|
|
|
if (toBN(allowance).gte(initValue)) {
|
|
return true
|
|
}
|
|
setTimeout(() => {
|
|
return dispatch('detectedAllowance', initValue)
|
|
}, 5000)
|
|
} catch (err) {
|
|
console.error('detectedAllowance has error', err.message)
|
|
setTimeout(() => {
|
|
return dispatch('detectedAllowance', initValue)
|
|
}, 5000)
|
|
}
|
|
},
|
|
async signApprove({ rootState, getters, rootGetters, dispatch, commit }, { amount }) {
|
|
try {
|
|
const { ethAccount } = rootState.metamask
|
|
const netId = rootGetters['metamask/netId']
|
|
const govInstance = rootGetters['governance/gov/govContract']({ netId })
|
|
|
|
const domain = {
|
|
name: 'TornadoCash',
|
|
version: '1',
|
|
chainId: netId,
|
|
verifyingContract: getters.tokenContract._address
|
|
}
|
|
const oneDayFromNow = Math.ceil(Date.now() / 1000) + 86400
|
|
// fetch nonces
|
|
let nonce = 0
|
|
try {
|
|
nonce = await getters.tokenContract.methods.nonces(ethAccount).call()
|
|
console.log('nonce', nonce)
|
|
} catch (e) {
|
|
throw new Error(e.message)
|
|
}
|
|
|
|
// const pre = toHex(toWei(amount.toString())).substr(2)
|
|
// let result = pre.length % 2 === 0 ? pre : '0' + pre
|
|
// result = '0x' + result
|
|
|
|
const value = toWei(amount.toString())
|
|
|
|
const args = {
|
|
owner: ethAccount,
|
|
spender: govInstance._address,
|
|
value, // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
nonce,
|
|
deadline: oneDayFromNow
|
|
}
|
|
|
|
const permitSigner = new PermitSigner(domain, args)
|
|
const req = permitSigner.getReqPayload()
|
|
|
|
const callParams = {
|
|
method: 'eth_signTypedData_v4',
|
|
params: [ethAccount, JSON.stringify(req)]
|
|
}
|
|
|
|
dispatch(
|
|
'loading/changeText',
|
|
{
|
|
message: this.app.i18n.t('pleaseSignRequestInWallet', {
|
|
wallet: rootState.metamask.walletName
|
|
}),
|
|
type: 'approve'
|
|
},
|
|
{ root: true }
|
|
)
|
|
|
|
const query = await this.$provider.sendRequest(callParams)
|
|
const { v, r, s, hex } = permitSigner.getSignature(query)
|
|
|
|
console.log('signature', v, r, s, hex)
|
|
|
|
// signature validation on contract
|
|
await getters.tokenContract.methods
|
|
.permit(args.owner, args.spender, args.value, args.deadline, v, r, s)
|
|
.call()
|
|
|
|
commit('SAVE_SIGNATURE', { v, r, s, amount, deadline: oneDayFromNow })
|
|
} catch (e) {
|
|
console.error('signApprove', e.message)
|
|
if (!isWalletRejection(e)) {
|
|
setTimeout(async () => {
|
|
await dispatch('approve', { amount })
|
|
dispatch('detectedAllowance', amount)
|
|
}, 1000)
|
|
}
|
|
} finally {
|
|
dispatch('loading/disable', {}, { root: true })
|
|
}
|
|
}
|
|
}
|
|
export default {
|
|
namespaced: true,
|
|
state,
|
|
getters,
|
|
mutations,
|
|
actions
|
|
}
|