token.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /* eslint-disable no-console */
  2. /* eslint-disable import/order */
  3. import Web3 from 'web3'
  4. import networkConfig from '@/networkConfig'
  5. import ERC20ABI from '@/abis/ERC20.abi.json'
  6. const { numberToHex, toBN, toWei } = require('web3-utils')
  7. const BN = require('bn.js')
  8. const zero = new BN(0)
  9. const negative1 = new BN(-1)
  10. export const state = () => {
  11. return {
  12. approvalAmount: 'unlimited',
  13. allowance: '',
  14. balance: ''
  15. }
  16. }
  17. export const getters = {
  18. tokenContract: (state, getters, rootState, rootGetters) => ({ currency, netId }) => {
  19. const config = networkConfig[`netId${netId}`]
  20. const { url } = rootState.settings[`netId${netId}`].rpc
  21. const address = config.tokens[currency].tokenAddress
  22. const web3 = new Web3(url)
  23. return new web3.eth.Contract(ERC20ABI, address)
  24. },
  25. // similar to fromWei from web3
  26. toDecimals: (state, getters, rootState, rootGetters) => (value, decimals, fixed) => {
  27. const { currency } = rootState.application.selectedStatistic
  28. decimals = decimals || rootGetters['metamask/networkConfig'].tokens[currency].decimals
  29. fixed = fixed || 2
  30. value = new BN(value)
  31. const negative = value.lt(zero)
  32. const base = new BN('10').pow(new BN(decimals))
  33. const baseLength = base.toString(10).length - 1 || 1
  34. if (negative) {
  35. value = value.mul(negative1)
  36. }
  37. let fraction = value.mod(base).toString(10)
  38. while (fraction.length < baseLength) {
  39. fraction = `0${fraction}`
  40. }
  41. fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1]
  42. const whole = value.div(base).toString(10)
  43. if (fixed && fraction !== '0') {
  44. fraction = fraction.slice(0, fixed)
  45. }
  46. value = `${whole}${fraction === '0' ? '' : `.${fraction}`}`
  47. if (negative) {
  48. value = `-${value}`
  49. }
  50. return value
  51. },
  52. // similar to toWei from web3
  53. fromDecimals: (state, getters, rootState, rootGetters) => (value, decimals) => {
  54. const { currency } = rootState.application.selectedStatistic
  55. decimals = decimals || rootGetters['metamask/networkConfig'].tokens[currency].decimals
  56. value = value.toString()
  57. let ether = value.toString()
  58. const base = new BN('10').pow(new BN(decimals))
  59. const baseLength = base.toString(10).length - 1 || 1
  60. const negative = ether.substring(0, 1) === '-'
  61. if (negative) {
  62. ether = ether.substring(1)
  63. }
  64. if (ether === '.') {
  65. throw new Error(this.app.i18n.t('unitInvalidValue', { value }))
  66. }
  67. // Split it into a whole and fractional part
  68. const comps = ether.split('.')
  69. if (comps.length > 2) {
  70. throw new Error(this.app.i18n.t('tooManyDecimalPoints', { value }))
  71. }
  72. let whole = comps[0]
  73. let fraction = comps[1]
  74. if (!whole) {
  75. whole = '0'
  76. }
  77. if (!fraction) {
  78. fraction = '0'
  79. }
  80. if (fraction.length > baseLength) {
  81. throw new Error(this.app.i18n.t('tooManyDecimalPlaces', { value }))
  82. }
  83. while (fraction.length < baseLength) {
  84. fraction += '0'
  85. }
  86. whole = new BN(whole)
  87. fraction = new BN(fraction)
  88. let wei = whole.mul(base).add(fraction)
  89. if (negative) {
  90. wei = wei.mul(negative)
  91. }
  92. return new BN(wei.toString(10), 10)
  93. },
  94. isSufficientAllowance: (state, getters, rootState, rootGetters) => {
  95. const { currency, amount } = rootState.application.selectedInstance
  96. const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
  97. return toBN(state.allowance).gte(toBN(getters.fromDecimals(amount, decimals)))
  98. },
  99. isSufficientBalance: (state, getters, rootState, rootGetters) => {
  100. const ethBalance = rootState.metamask.ethBalance
  101. const { currency, amount } = rootState.application.selectedInstance
  102. const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
  103. const nativeCurrency = rootGetters['metamask/nativeCurrency']
  104. if (currency === nativeCurrency) {
  105. return toBN(ethBalance).gte(toBN(toWei(amount.toString())))
  106. } else {
  107. return toBN(state.balance).gte(toBN(getters.fromDecimals(amount, decimals)))
  108. }
  109. },
  110. getSymbol: (state, getters, rootState, rootGetters) => (currency) => {
  111. const tokens = rootGetters['metamask/networkConfig'].tokens
  112. if (tokens[currency]) {
  113. return tokens[currency].symbol
  114. }
  115. return currency.toUpperCase()
  116. }
  117. }
  118. export const mutations = {
  119. SET_APPROVAL_AMOUNT(state, { approvalAmount }) {
  120. state.approvalAmount = approvalAmount
  121. },
  122. SAVE_ALLOWANCE(state, { allowance }) {
  123. this._vm.$set(state, 'allowance', allowance)
  124. },
  125. SAVE_BALANCE(state, { balance }) {
  126. this._vm.$set(state, 'balance', balance)
  127. }
  128. }
  129. export const actions = {
  130. async approve({ rootState, getters, dispatch, rootGetters, state }) {
  131. try {
  132. const netId = rootGetters['metamask/netId']
  133. const { currency } = rootState.application.selectedInstance
  134. const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
  135. const tokenInstance = getters.tokenContract({ currency, netId })
  136. const tornadoProxy = rootGetters['application/tornadoProxyContract']({ netId })
  137. const { ethAccount } = rootState.metamask
  138. const amountToApprove =
  139. state.approvalAmount === 'unlimited'
  140. ? toBN('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
  141. : toBN(getters.fromDecimals(state.approvalAmount, decimals))
  142. const data = tokenInstance.methods
  143. .approve(tornadoProxy._address, amountToApprove.toString())
  144. .encodeABI()
  145. const gas = await tokenInstance.methods
  146. .approve(tornadoProxy._address, amountToApprove.toString())
  147. .estimateGas({
  148. from: ethAccount
  149. })
  150. const callParams = {
  151. method: 'eth_sendTransaction',
  152. params: {
  153. to: tokenInstance._address,
  154. gas: numberToHex(gas + 30000),
  155. data
  156. },
  157. watcherParams: {
  158. title: 'approve',
  159. successTitle: 'approved'
  160. },
  161. isSaving: false
  162. }
  163. await dispatch('metamask/sendTransaction', callParams, { root: true })
  164. } catch (e) {
  165. console.error('approve action', e)
  166. throw new Error(e.message)
  167. }
  168. },
  169. async fetchTokenAllowance({ getters, rootGetters, commit, rootState }) {
  170. const netId = rootGetters['metamask/netId']
  171. const { currency } = rootState.application.selectedInstance
  172. const { ethAccount } = rootState.metamask
  173. try {
  174. const tornadoInstance = rootGetters['application/tornadoProxyContract']({ netId })
  175. const nativeCurrency = rootGetters['metamask/nativeCurrency']
  176. if (currency !== nativeCurrency && ethAccount) {
  177. const tokenInstance = getters.tokenContract({ currency, netId })
  178. const allowance = await tokenInstance.methods.allowance(ethAccount, tornadoInstance._address).call()
  179. commit('SAVE_ALLOWANCE', { allowance })
  180. }
  181. } catch (e) {
  182. console.error('fetchTokenAllowance', e.message)
  183. }
  184. },
  185. async fetchTokenBalance({ state, getters, rootGetters, commit, rootState }) {
  186. try {
  187. const netId = rootGetters['metamask/netId']
  188. const { currency } = rootState.application.selectedInstance
  189. const { ethAccount } = rootState.metamask
  190. const nativeCurrency = rootGetters['metamask/nativeCurrency']
  191. if (currency !== nativeCurrency && ethAccount) {
  192. const tokenInstance = getters.tokenContract({ currency, netId })
  193. const balance = await tokenInstance.methods.balanceOf(ethAccount).call()
  194. commit('SAVE_BALANCE', { balance })
  195. }
  196. } catch (e) {
  197. console.error('fetchTokenBalance', e.message)
  198. }
  199. }
  200. }