228 lines
6.5 KiB
JavaScript
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
|
||
|
}
|