tornado-classic-ui/store/torn.js

228 lines
6.5 KiB
JavaScript

/* eslint-disable no-console */
import Web3 from 'web3'
import { toBN } from 'web3-utils'
import tornABI from '../abis/ERC20.abi.json'
import { isWalletRejection } from '@/utils'
const { toWei, toHex, numberToHex } = require('web3-utils')
const { PermitSigner } = require('../lib/Permit')
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 message = permitSigner.getPayload()
const callParams = {
method: 'eth_signTypedData_v4',
params: [ethAccount, JSON.stringify(message)]
}
dispatch(
'loading/changeText',
{
message: this.app.i18n.t('pleaseSignRequestInWallet', {
wallet: rootState.metamask.walletName
}),
type: 'approve'
},
{ root: true }
)
let signature = await this.$provider.sendRequest(callParams)
signature = signature.substring(2)
const r = '0x' + signature.substring(0, 64)
const s = '0x' + signature.substring(64, 128)
let v = parseInt(signature.substring(128, 130), 16)
// fix ledger sign
if (v === 0 || v === 1) {
v = v + 27
}
console.log('signature', v, r, s, signature)
// 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
}