mirror of
https://github.com/tornadocash/tornado-initiation-ui.git
synced 2025-02-09 23:56:24 +01:00
377 lines
8.2 KiB
JavaScript
377 lines
8.2 KiB
JavaScript
import Web3 from 'web3'
|
|
import { hexToNumberString, toChecksumAddress } from 'web3-utils'
|
|
|
|
class Provider {
|
|
/**
|
|
* Constructor params.
|
|
*
|
|
* @param options {object}
|
|
* @param {object} options.config
|
|
* @param {number} options.config.id
|
|
* @param {string} options.config.rpcUrl
|
|
* @param {number} options.config.rpcCallRetryAttempt
|
|
* @param {object} options.provider
|
|
* @param {string} options.rpcUrl - node api endpoint.
|
|
* @returns {*}
|
|
*/
|
|
constructor(options) {
|
|
this.address = ''
|
|
this.version = 'new'
|
|
|
|
this.config = options.config
|
|
this.web3 = new Web3(new Web3.providers.HttpProvider(options.rpcUrl))
|
|
}
|
|
|
|
async initProvider(provider) {
|
|
try {
|
|
this.provider = provider
|
|
this.web3 = new Web3(provider)
|
|
|
|
await this._checkVersion()
|
|
return await this._initProvider()
|
|
} catch (err) {
|
|
throw new Error(`Provider method initProvider has error: ${err.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a request to the provider.
|
|
*
|
|
* @param params {object}
|
|
* @returns {*}
|
|
*/
|
|
async sendRequest(params) {
|
|
try {
|
|
const request = (args) =>
|
|
this.version === 'old'
|
|
? this._sendAsync(args)
|
|
: this.provider.request(args)
|
|
|
|
return await request(params)
|
|
} catch (err) {
|
|
throw new Error(`Provider method sendRequest has error: ${err.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calling a custom method on the provider.
|
|
*
|
|
* @param params {object}
|
|
* @param {string} params.methodName
|
|
* @param {string} params.data
|
|
* @param {string} params.to
|
|
* @param {string} params.from
|
|
* @param {string} params.gas
|
|
* @param {number} params.value
|
|
* @returns {*}
|
|
*/
|
|
async contractRequest({ methodName, data, to, from, gas, value = 0 }) {
|
|
const { rpcCallRetryAttempt, blockGasLimit } = this.config
|
|
|
|
try {
|
|
const params = {
|
|
to,
|
|
data,
|
|
value,
|
|
from: from || this.address,
|
|
gas: gas || blockGasLimit + 100000,
|
|
}
|
|
|
|
return await this._repeatUntilResult(
|
|
() => this.web3.eth[methodName](params),
|
|
rpcCallRetryAttempt
|
|
)
|
|
} catch (err) {
|
|
throw new Error(
|
|
`Provider method contractRequest has error: ${err.message}`
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get your address balance.
|
|
*
|
|
* @param params {object}
|
|
* @param {string} params.address.
|
|
* @returns {*}
|
|
*/
|
|
async getBalance({ address }) {
|
|
const { rpcCallRetryAttempt } = this.config
|
|
|
|
try {
|
|
const params = {
|
|
method: 'eth_getBalance',
|
|
params: [address, 'latest'],
|
|
}
|
|
|
|
const balance = await this._repeatUntilResult(
|
|
() => this.sendRequest(params),
|
|
rpcCallRetryAttempt
|
|
)
|
|
|
|
return hexToNumberString(balance)
|
|
} catch (err) {
|
|
throw new Error(`Provider method getBalance has error: ${err.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waiting for receipt of the transaction.
|
|
*
|
|
* @param params {object}
|
|
* @param {string} params.txHash
|
|
* @returns {*}
|
|
*/
|
|
async waitForTxReceipt({ txHash }) {
|
|
const { rpcCallRetryAttempt } = this.config
|
|
|
|
try {
|
|
const params = {
|
|
method: 'eth_getTransactionReceipt',
|
|
params: [txHash],
|
|
}
|
|
|
|
return await this._repeatUntilResult(
|
|
() => this.sendRequest(params),
|
|
rpcCallRetryAttempt * 10
|
|
)
|
|
} catch (err) {
|
|
throw new Error(
|
|
`Provider method waitForTxReceipt has error: ${err.message}`
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get network version, the current provider.
|
|
*
|
|
* @param params {object}
|
|
* @param {array} params.txs
|
|
* @param {function} params.callback
|
|
* @returns {*}
|
|
*/
|
|
async batchRequest({ txs, callback }) {
|
|
try {
|
|
const web3 = new Web3(this.provider)
|
|
|
|
const batch = new web3.BatchRequest()
|
|
const txsPromisesBucket = []
|
|
|
|
for (const params of txs) {
|
|
const txPromise = new Promise((resolve, reject) => {
|
|
const tx = web3.eth.sendTransaction.request(
|
|
params,
|
|
(error, txHash) => {
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
resolve(txHash)
|
|
}
|
|
}
|
|
)
|
|
batch.add(tx)
|
|
})
|
|
txsPromisesBucket.push(txPromise)
|
|
}
|
|
|
|
batch.execute()
|
|
|
|
callback(txsPromisesBucket)
|
|
|
|
return await Promise.all(txsPromisesBucket)
|
|
} catch (err) {
|
|
throw new Error(err.message)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get network version, the current provider.
|
|
*/
|
|
async checkNetworkVersion() {
|
|
try {
|
|
return await this.sendRequest({ method: 'net_version' })
|
|
} catch (err) {
|
|
throw new Error(
|
|
`Provider method _checkNetworkVersion has error: ${err.message}`
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize provider.
|
|
*
|
|
*/
|
|
async _initProvider() {
|
|
try {
|
|
const request = () =>
|
|
this.version
|
|
? this.provider.enable()
|
|
: this.sendRequest({ method: 'eth_requestAccounts' })
|
|
|
|
const [account] = await request('')
|
|
|
|
if (!account) {
|
|
throw new Error('Locked metamask')
|
|
}
|
|
|
|
this.address = account
|
|
|
|
this.provider.on('accountsChanged', (accounts) =>
|
|
this._onAccountsChanged(accounts)
|
|
)
|
|
this.provider.on('chainChanged', (id) => this._onNetworkChanged({ id }))
|
|
|
|
this.config.id = await this.checkNetworkVersion()
|
|
|
|
return toChecksumAddress(account)
|
|
} catch (err) {
|
|
throw new Error(`Provider method _initProvider has error: ${err.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Subscribe to event in provider.
|
|
*
|
|
* @param method {string}
|
|
* @param {function} callback
|
|
* @returns {*}
|
|
*/
|
|
on(method, callback) {
|
|
if (method) {
|
|
this.provider.on(method, callback)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a request to the provider (old api).
|
|
*
|
|
* @param options {object}
|
|
* @param {string} params.method.
|
|
* @param {string} params.params.
|
|
* @param {string} params.from.
|
|
* @returns {*}
|
|
*/
|
|
_sendAsync({ method, params, from }) {
|
|
const { id } = this.config
|
|
|
|
switch (id) {
|
|
case 77:
|
|
case 99:
|
|
case 100:
|
|
from = undefined
|
|
break
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const callback = (err, response) => {
|
|
if (err || response.error) {
|
|
reject(err)
|
|
}
|
|
|
|
resolve(response.result)
|
|
}
|
|
|
|
this.provider.sendAsync(
|
|
{
|
|
method,
|
|
params,
|
|
jsonrpc: '2.0',
|
|
from,
|
|
},
|
|
callback
|
|
)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Send a request to the provider (old api).
|
|
*
|
|
* @param options {object}
|
|
* @param {string} params.method.
|
|
* @param {string} params.params.
|
|
* @returns {*}
|
|
*/
|
|
async _send({ method, params }) {
|
|
try {
|
|
return await this.provider.send(method, params)
|
|
} catch (err) {
|
|
throw new Error(`Provider method _send has error: ${err.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set network id.
|
|
*
|
|
* @param params {object}
|
|
* @param {string} params.id.
|
|
* @returns {*}
|
|
*/
|
|
_onNetworkChanged({ id, callback }) {
|
|
if (id) {
|
|
this.network = id
|
|
if (typeof callback === 'function') {
|
|
callback()
|
|
}
|
|
window.location.reload()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set default address to the provider.
|
|
*
|
|
* @param accounts {String[]}.
|
|
* @returns {*}
|
|
*/
|
|
_onAccountsChanged(accounts) {
|
|
const [account] = accounts
|
|
|
|
if (account) {
|
|
this.address = toChecksumAddress(account)
|
|
window.location.reload()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check version currently provider.
|
|
*
|
|
*/
|
|
_checkVersion() {
|
|
if (this.provider && this.provider.request) {
|
|
this.version = 'new'
|
|
} else {
|
|
this.version = 'old'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Repeat the function call as many times as needed.
|
|
*
|
|
* @function action.
|
|
* @function totalAttempts {number}
|
|
* @function retryAttempt {number}
|
|
* @returns {*}
|
|
*/
|
|
_repeatUntilResult(action, totalAttempts, retryAttempt = 1) {
|
|
return new Promise((resolve, reject) => {
|
|
const iteration = async () => {
|
|
const result = await action()
|
|
|
|
if (!result) {
|
|
if (retryAttempt <= totalAttempts) {
|
|
retryAttempt++
|
|
setTimeout(iteration, 1000 * retryAttempt)
|
|
} else {
|
|
return reject(new Error('Tx not minted'))
|
|
}
|
|
} else {
|
|
resolve(result)
|
|
}
|
|
}
|
|
|
|
iteration()
|
|
})
|
|
}
|
|
}
|
|
|
|
export default Provider
|