-
Notifications
You must be signed in to change notification settings - Fork 239
Expand file tree
/
Copy pathRelayerService.js
More file actions
85 lines (70 loc) · 3.06 KB
/
Copy pathRelayerService.js
File metadata and controls
85 lines (70 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const GasPriceOracle = require('./GasPriceOracle')
module.exports = (artifacts, web3) => class RelayerService {
constructor(wallet, relayer) {
this.wallet = wallet
this.relayer = relayer
}
async relay(transaction) {
await this._assertTargetIsAragonApp(transaction)
await this._assertTransactionWontRevert(transaction)
await this._assertTransactionGasCostIsCovered(transaction)
await this._assertTransactionReasonableGasPrice(transaction)
return this._relay(transaction)
}
async estimateGas(transaction) {
return this._estimateGas(transaction)
}
async _relay(transaction) {
const { from, to, nonce, data, gasRefund, gasPrice, signature } = transaction
const txParams = { from: this.wallet, gas: gasRefund, gasPrice }
// console.log(`\nRelaying transaction ${JSON.stringify(transaction)} with params ${JSON.stringify(txParams)}`)
return this.relayer.relay(from, to, nonce, data, gasRefund, gasPrice, signature, txParams)
}
async _assertTargetIsAragonApp({ to }) {
let relayerKernel, aragonAppKernel
try {
relayerKernel = await this.relayer.kernel()
aragonAppKernel = await artifacts.require('AragonApp').at(to).kernel()
} catch (error) {
throw Error(`Could not ensure target address is actually an AragonApp from the same Kernel: ${error}`)
}
if (relayerKernel === aragonAppKernel) return;
throw Error(`The Kernel of the target app ${aragonAppKernel} does not match with the Kernel of the current realyer ${relayerKernel}`)
}
async _assertTransactionWontRevert(transaction) {
const error = await this._transactionWillFail(transaction)
if (!error) return
throw Error(error.message.search(/(revert|invalid opcode|invalid jump)/) > -1
? `Will not relay failing transaction: ${error.message}`
: `Could not estimate gas: ${error.message}`)
}
async _assertTransactionGasCostIsCovered(transaction) {
const { gasRefund } = transaction
const estimatedGas = await this._estimateGas(transaction)
if (gasRefund < estimatedGas) throw Error(`Given gas refund amount ${gasRefund} does not cover transaction gas cost ${estimatedGas}`)
}
async _assertTransactionReasonableGasPrice(transaction) {
const averageGasPrice = await GasPriceOracle.fetch(this._networkId())
if (transaction.gasPrice < averageGasPrice) throw Error(`Given gas price is below the average ${averageGasPrice}`)
}
async _transactionWillFail(transaction) {
try {
await this._estimateGas(transaction)
return false
} catch (error) {
return error
}
}
async _estimateGas({ from, to, nonce, data, gasRefund, gasPrice, signature }) {
const calldata = this.relayer.contract.relay.getData(from, to, nonce, data, gasRefund, gasPrice, signature)
const call = { from: this.wallet, to: this.relayer.address, data: calldata }
return new Promise((resolve, reject) => {
web3.eth.estimateGas(call, (error, response) => {
return error ? reject(error) : resolve(response)
})
})
}
_networkId() {
return this.relayer.constructor.network_id
}
}