Change gas & fees calculations:

- Use @tornado/tornado-oracles lib to calculate withdrawal fee via relayer
	- When calculating fee, save backwards compatibility for Ethereum Mainnet V4 relayers & old UI builds
	- Bump UI version to 1.2.0
	- Display most up-to-date information about relayer withdrawal fees on WithdrawalTotal page
This commit is contained in:
Theo 2023-08-27 12:30:25 -07:00
parent fb0a5131f4
commit 9210330208
7 changed files with 73 additions and 112 deletions

View File

@ -192,7 +192,7 @@ export default {
...mapState('relayer', ['isLoadingRelayers']), ...mapState('relayer', ['isLoadingRelayers']),
...mapGetters('txHashKeeper', ['txExplorerUrl']), ...mapGetters('txHashKeeper', ['txExplorerUrl']),
...mapGetters('application', ['isNotEnoughTokens', 'selectedStatisticCurrency']), ...mapGetters('application', ['isNotEnoughTokens', 'selectedStatisticCurrency']),
...mapGetters('metamask', ['networkConfig', 'netId', 'isLoggedIn', 'nativeCurrency']), ...mapGetters('metamask', ['networkConfig', 'netId', 'isLoggedIn']),
notEnoughDeposits() { notEnoughDeposits() {
if (this.depositsPast < 5) { if (this.depositsPast < 5) {
return true return true
@ -337,10 +337,7 @@ export default {
}) })
} }
this.$store.dispatch('application/setAndUpdateStatistic', { currency, amount: Number(amount) }) this.$store.dispatch('application/setAndUpdateStatistic', { currency, amount: Number(amount) })
this.$store.dispatch('fees/calculateWithdrawalNetworkFee', {}) this.$store.dispatch('fees/calculateWithdrawalFeeViaRelayer', {})
if (currency !== this.nativeCurrency) {
this.$store.dispatch('application/setDefaultEthToReceive', { currency })
}
this.$store.dispatch('loading/updateProgress', { progress: -1 }) this.$store.dispatch('loading/updateProgress', { progress: -1 })
this.depositsPast = Number(depositsPast) <= 0 ? 0 : depositsPast this.depositsPast = Number(depositsPast) <= 0 ? 0 : depositsPast
this.depositTxHash = txHash this.depositTxHash = txHash

View File

@ -71,7 +71,7 @@ export default {
}, },
computed: { computed: {
...mapState('application', ['selectedStatistic']), ...mapState('application', ['selectedStatistic']),
...mapState('fees', ['withdrawalNetworkFee']), ...mapState('fees', ['withdrawalNetworkFee', 'withdrawalFeeViaRelayer']),
...mapGetters('metamask', ['networkConfig', 'nativeCurrency']), ...mapGetters('metamask', ['networkConfig', 'nativeCurrency']),
...mapGetters('metamask', { ...mapGetters('metamask', {
networkCurrency: 'currency' networkCurrency: 'currency'
@ -119,17 +119,12 @@ export default {
return fromWei(this.ethToReceive) return fromWei(this.ethToReceive)
}, },
total() { total() {
const { amount, currency } = this.selectedStatistic const { amount } = this.selectedStatistic
let total = toBN(this.fromDecimals(amount.toString())) let total = toBN(this.fromDecimals(amount.toString()))
if (this.withdrawType === 'relayer') { if (this.withdrawType === 'relayer') {
const relayerFee = this.totalRelayerFee const relayerFee = this.withdrawalFeeViaRelayer
total = total.sub(relayerFee)
if (currency === this.nativeCurrency) {
total = total.sub(relayerFee)
} else {
total = total.sub(relayerFee).sub(this.ethToReceiveInToken)
}
} }
return this.toDecimals(total, null, 6) return this.toDecimals(total, null, 6)

View File

@ -1,6 +1,6 @@
{ {
"name": "tornadocash-classic-ui", "name": "tornadocash-classic-ui",
"version": "1.1.0", "version": "1.2.0",
"description": "Private ethereum transactions", "description": "Private ethereum transactions",
"author": "Tornado Cash Team", "author": "Tornado Cash Team",
"scripts": { "scripts": {
@ -24,7 +24,7 @@
"@metamask/onboarding": "^1.0.0", "@metamask/onboarding": "^1.0.0",
"@nuxtjs/moment": "^1.6.0", "@nuxtjs/moment": "^1.6.0",
"@tornado/snarkjs": "0.1.20-p2", "@tornado/snarkjs": "0.1.20-p2",
"@tornado/tornado-oracles": "1.2", "@tornado/tornado-oracles": "1.3.2",
"@tornado/websnark": "0.0.4-p1", "@tornado/websnark": "0.0.4-p1",
"@walletconnect/web3-provider": "1.7.8", "@walletconnect/web3-provider": "1.7.8",
"ajv": "^6.10.2", "ajv": "^6.10.2",

View File

@ -9,7 +9,6 @@ import MulticallABI from '@/abis/Multicall.json'
import InstanceABI from '@/abis/Instance.abi.json' import InstanceABI from '@/abis/Instance.abi.json'
import TornadoProxyABI from '@/abis/TornadoProxy.abi.json' import TornadoProxyABI from '@/abis/TornadoProxy.abi.json'
import { ACTION, ACTION_GAS } from '@/constants/variables'
import { graph, treesInterface, EventsFactory } from '@/services' import { graph, treesInterface, EventsFactory } from '@/services'
import { import {
@ -18,7 +17,6 @@ import {
toFixedHex, toFixedHex,
saveAsFile, saveAsFile,
isEmptyArray, isEmptyArray,
decimalPlaces,
parseHexNote, parseHexNote,
checkCommitments, checkCommitments,
buffPedersenHash buffPedersenHash
@ -63,8 +61,7 @@ const state = () => {
withdrawType: 'relayer', withdrawType: 'relayer',
ethToReceive: '20000000000000000', ethToReceive: '20000000000000000',
defaultEthToReceive: '20000000000000000', defaultEthToReceive: '20000000000000000',
withdrawNote: '', withdrawNote: ''
relayerWithdrawGasLimit: null
} }
} }
@ -105,9 +102,6 @@ const mutations = {
}, },
SET_WITHDRAW_NOTE(state, withdrawNote) { SET_WITHDRAW_NOTE(state, withdrawNote) {
state.withdrawNote = withdrawNote state.withdrawNote = withdrawNote
},
SET_RELAYER_WITHDRAW_GAS_LIMIT(state, { relayerWithdrawGasLimit }) {
this._vm.$set(state, 'relayerWithdrawGasLimit', relayerWithdrawGasLimit)
} }
} }
@ -146,42 +140,24 @@ const getters = {
currentContract: (state, getters) => (params) => { currentContract: (state, getters) => (params) => {
return getters.tornadoProxyContract(params) return getters.tornadoProxyContract(params)
}, },
defaultWithdrawGas: (state, getters) => { relayerWithdrawalTxData: (state, getters, rootState, rootGetters) => ({
let action = ACTION.WITHDRAW_WITH_EXTRA proof,
withdrawCallArgs,
currency,
amount
}) => {
const netId = rootGetters['metamask/netId']
const tornadoProxy = getters.tornadoProxyContract({ netId })
const tornadoInstance = getters.instanceContract({ currency, amount, netId })
if (getters.hasEnabledLightProxy) { const calldata = tornadoProxy.methods
action = ACTION.WITHDRAW .withdraw(tornadoInstance._address, proof, ...withdrawCallArgs)
} .encodeABI()
if (getters.isOptimismConnected) { return {
action = ACTION.OP_WITHDRAW to: tornadoProxy._address,
} data: calldata,
value: withdrawCallArgs[5] || 0
if (getters.isArbitrumConnected) {
action = ACTION.ARB_WITHDRAW
}
return ACTION_GAS[action]
},
relayerFee: (state, getters, rootState, rootGetters) => {
const { currency, amount } = rootState.application.selectedStatistic
const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
const nativeCurrency = rootGetters['metamask/nativeCurrency']
const total = toBN(rootGetters['token/fromDecimals'](amount.toString()))
const fee = rootState.relayer.selectedRelayer.tornadoServiceFee
const decimalsPoint = decimalPlaces(fee)
const roundDecimal = 10 ** decimalsPoint
const aroundFee = toBN(parseInt(fee * roundDecimal, 10))
const tornadoServiceFee = total.mul(aroundFee).div(toBN(roundDecimal * 100))
const ethFee = rootState.fees.withdrawalNetworkFee
switch (currency) {
case nativeCurrency: {
return ethFee.add(tornadoServiceFee)
}
default: {
const tokenFee = ethFee.mul(toBN(10 ** decimals)).div(toBN(rootState.price.prices[currency]))
return tokenFee.add(tornadoServiceFee)
}
} }
}, },
ethToReceiveInToken: (state, getters, rootState, rootGetters) => { ethToReceiveInToken: (state, getters, rootState, rootGetters) => {
@ -196,7 +172,7 @@ const getters = {
let total = toBN(rootGetters['token/fromDecimals'](amount.toString())) let total = toBN(rootGetters['token/fromDecimals'](amount.toString()))
if (state.withdrawType === 'relayer') { if (state.withdrawType === 'relayer') {
const relayerFee = getters.relayerFee const relayerFee = rootState.fees.withdrawalFeeViaRelayer
const nativeCurrency = rootGetters['metamask/nativeCurrency'] const nativeCurrency = rootGetters['metamask/nativeCurrency']
if (currency === nativeCurrency) { if (currency === nativeCurrency) {
@ -214,9 +190,8 @@ const getters = {
const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency] const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
const total = toBN(rootGetters['token/fromDecimals'](amount.toString())) const total = toBN(rootGetters['token/fromDecimals'](amount.toString()))
const price = rootState.price.prices[currency] const price = rootState.price.prices[currency]
const relayerFee = getters.relayerFee
return total return total
.sub(relayerFee) .sub(rootState.fees.withdrawalFeeViaRelayer)
.mul(toBN(price)) .mul(toBN(price))
.div(toBN(10 ** decimals)) .div(toBN(10 ** decimals))
}, },
@ -628,34 +603,6 @@ const actions = {
return false return false
} }
}, },
async estimateRelayerWithdrawGasLimit(
{ getters, rootState, rootGetters, commit },
{ currency, amount, netId, proof, withdrawCallArgs }
) {
const tornadoProxy = getters.tornadoProxyContract({ netId })
const tornadoInstance = getters.instanceContract({ currency, amount, netId })
const relayer = rootState.relayer.selectedRelayer.address
const gasPrice = toBN(rootGetters['fees/gasPrice'])
let gasLimit
try {
const fetchedGasLimit = await tornadoProxy.methods
.withdraw(tornadoInstance._address, proof, ...withdrawCallArgs)
.estimateGas({
from: relayer,
to: tornadoProxy._address,
value: withdrawCallArgs[5] || 0,
gasPrice,
gasLimit: getters.defaultWithdrawGas
})
gasLimit = Math.floor(fetchedGasLimit * 1.3)
} catch (e) {
gasLimit = getters.defaultWithdrawGas
console.error(`Cannot fetch gas limit for relayer withdrawal, using default: ${gasLimit}`)
}
commit('SET_RELAYER_WITHDRAW_GAS_LIMIT', { relayerWithdrawGasLimit: gasLimit })
},
async checkSpentEventFromNullifier({ getters, dispatch }, parsedNote) { async checkSpentEventFromNullifier({ getters, dispatch }, parsedNote) {
try { try {
const isSpent = await dispatch('loadEvent', { const isSpent = await dispatch('loadEvent', {
@ -724,7 +671,7 @@ const actions = {
const nativeCurrency = rootGetters['metamask/nativeCurrency'] const nativeCurrency = rootGetters['metamask/nativeCurrency']
const withdrawType = state.withdrawType const withdrawType = state.withdrawType
let relayer = BigInt(recipient) const relayer = BigInt(rootState.relayer.selectedRelayer.address)
let fee = BigInt(0) let fee = BigInt(0)
let refund = BigInt(0) let refund = BigInt(0)
@ -770,24 +717,21 @@ const actions = {
// Don't need to calculate or estimate relayer fee, so, return proof immediately // Don't need to calculate or estimate relayer fee, so, return proof immediately
if (withdrawType !== 'relayer') return calculateSnarkProof() if (withdrawType !== 'relayer') return calculateSnarkProof()
relayer = BigInt(rootState.relayer.selectedRelayer.address) fee = BigInt(rootState.fees.withdrawalFeeViaRelayer)
const { proof: dummyProof, args: dummyArgs } = await calculateSnarkProof() const naiveProof = await calculateSnarkProof()
const { netId, amount, currency } = note if (Number(note.netId) === 1) return naiveProof // Don't need to smart-estimate fee if we use V4 withdrawal
await dispatch('estimateRelayerWithdrawGasLimit', {
netId, const { proof: dummyProof, args: dummyArgs } = naiveProof
amount, const withdrawalTx = getters.relayerWithdrawalTxData({
currency,
proof: dummyProof, proof: dummyProof,
withdrawCallArgs: dummyArgs withdrawCallArgs: dummyArgs,
amount: note.amount,
currency: note.currency
}) })
let totalRelayerFee = getters.relayerFee if (note.currency !== nativeCurrency) refund = BigInt(state.ethToReceive.toString())
if (note.currency !== nativeCurrency) { await dispatch('fees/calculateWithdrawalFeeViaRelayer', { tx: withdrawalTx }, { root: true })
refund = BigInt(state.ethToReceive.toString()) fee = BigInt(rootState.fees.withdrawalFeeViaRelayer)
totalRelayerFee = totalRelayerFee.add(getters.ethToReceiveInToken)
}
fee = BigInt(totalRelayerFee.toString())
// Recalculate proof with actual fee and refund // Recalculate proof with actual fee and refund
return calculateSnarkProof() return calculateSnarkProof()

View File

@ -5,7 +5,8 @@ import { TornadoFeeOracleV4, TornadoFeeOracleV5 } from '@tornado/tornado-oracles
export const state = () => { export const state = () => {
return { return {
gasPriceParams: { gasPrice: toWei(toBN(50), 'gwei') }, gasPriceParams: { gasPrice: toWei(toBN(50), 'gwei') },
withdrawalNetworkFee: toBN(0) withdrawalNetworkFee: toBN(0),
withdrawalFeeViaRelayer: toBN(0)
} }
} }
@ -38,6 +39,9 @@ export const mutations = {
}, },
SAVE_WITHDRAWAL_NETWORK_FEE(state, gasFee) { SAVE_WITHDRAWAL_NETWORK_FEE(state, gasFee) {
state.withdrawalNetworkFee = gasFee state.withdrawalNetworkFee = gasFee
},
SAVE_WITHDRAWAL_FEE_VIA_RELAYER(state, fee) {
state.withdrawalFeeViaRelayer = fee
} }
} }
@ -47,8 +51,6 @@ export const actions = {
try { try {
const gasPriceParams = await getters.oracle.getGasPriceParams() const gasPriceParams = await getters.oracle.getGasPriceParams()
console.log(gasPriceParams)
commit('SAVE_GAS_PARAMS', gasPriceParams) commit('SAVE_GAS_PARAMS', gasPriceParams)
} catch (e) { } catch (e) {
console.error('fetchGasPrice', e) console.error('fetchGasPrice', e)
@ -64,5 +66,28 @@ export const actions = {
const withdrawalGas = await getters.oracle.getGas(tx, 'user_withdrawal') const withdrawalGas = await getters.oracle.getGas(tx, 'user_withdrawal')
commit('SAVE_WITHDRAWAL_NETWORK_FEE', toBN(withdrawalGas)) commit('SAVE_WITHDRAWAL_NETWORK_FEE', toBN(withdrawalGas))
},
async calculateWithdrawalFeeViaRelayer({ dispatch, getters, commit, rootGetters, rootState }, { tx }) {
const feePercent = rootState.relayer.selectedRelayer.tornadoServiceFee
const { currency, amount } = rootState.application.selectedStatistic
const nativeCurrency = rootGetters['metamask/nativeCurrency']
const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
await dispatch('calculateWithdrawalNetworkFee', { tx })
if (currency !== nativeCurrency)
await dispatch('application/setDefaultEthToReceive', { currency }, { root: true })
const withdrawalFee = await getters.oracle.calculateWithdrawalFeeViaRelayer(
'user_withdrawal',
tx,
feePercent,
currency.toLowerCase(),
amount,
decimals,
rootState.application.ethToReceive || 0,
rootState.price.prices[currency.toLowerCase()]
)
commit('SAVE_WITHDRAWAL_FEE_VIA_RELAYER', toBN(withdrawalFee))
} }
} }

View File

@ -249,7 +249,7 @@ export const actions = {
const isUpdatedMajor = major === requiredMajor const isUpdatedMajor = major === requiredMajor
if (prerelease) return false if (prerelease) return false
return isUpdatedMajor && Number(patch) >= 4 // Patch checking - also backwards compatibility return isUpdatedMajor && (Number(patch) >= 4 || netId !== 1) // Patch checking - also backwards compatibility for Mainnet
} }
if (!isRelayerUpdated()) { if (!isRelayerUpdated()) {

View File

@ -2477,10 +2477,10 @@
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ftornado-config/-/2.0.0/tornado-config-2.0.0.tgz#52bbc179ecb2385f71b4d56e060b68e7dd6fb8b4" resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ftornado-config/-/2.0.0/tornado-config-2.0.0.tgz#52bbc179ecb2385f71b4d56e060b68e7dd6fb8b4"
integrity sha512-7EkpWNfEm34VEOrbLnPpvd/aUJYnA1L+6/qx2fZ/AfmuJFkjSZ18Z4jvVGNY7ktKIhTu3/Tbze+9l3eNueCNIA== integrity sha512-7EkpWNfEm34VEOrbLnPpvd/aUJYnA1L+6/qx2fZ/AfmuJFkjSZ18Z4jvVGNY7ktKIhTu3/Tbze+9l3eNueCNIA==
"@tornado/tornado-oracles@1.2": "@tornado/tornado-oracles@1.3.2":
version "1.2.0" version "1.3.2"
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ftornado-oracles/-/1.2.0/tornado-oracles-1.2.0.tgz#07121625343ba683f2d82f3e0cdf5ec70378d833" resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ftornado-oracles/-/1.3.2/tornado-oracles-1.3.2.tgz#83d269619b6e3a9a1abe964a561c3dfd533cca4c"
integrity sha512-AmqudzDi5LS7Enm2uEAzDxjqqByUeUvs4Bg6FfkEufE6MPs3twOwdWxMDKx8xxlFMVlVeVCv2hMs1nGHnabWdw== integrity sha512-z3NI35GKKznxVigF0OghqQ8S3sT/S4oD3AtXqyf+P2bfkriAsdrXnsSMW7Vxej529e5k3cA0dPtOs4eFqFE/7Q==
dependencies: dependencies:
"@tornado/gas-price-oracle" "^0.5.3" "@tornado/gas-price-oracle" "^0.5.3"
"@tornado/tornado-config" "^2.0.0" "@tornado/tornado-config" "^2.0.0"