-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathservice.go
133 lines (119 loc) · 4.37 KB
/
service.go
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package service
import (
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/forta-network/forta-json-rpc-proxy/interfaces"
"github.com/sirupsen/logrus"
)
type wrapperService struct {
chainID *big.Int
rpcClient interfaces.RPCClient
ethClient interfaces.EthClient
bundler interfaces.Bundler
attester interfaces.Attester
enableBundling bool
}
// NewWrapperService creates a new service that wraps a few JSON-RPC methods.
func NewWrapperService(
chainID *big.Int, rpcClient interfaces.RPCClient, ethClient interfaces.EthClient,
bundler interfaces.Bundler, attester interfaces.Attester,
) *wrapperService {
return &wrapperService{
chainID: chainID,
rpcClient: rpcClient,
ethClient: ethClient,
bundler: bundler,
attester: attester,
}
}
// Frontrunning:
func (s *wrapperService) SendRawTransaction(ctx context.Context, userTx hexutil.Bytes) (common.Hash, error) {
tx := new(types.Transaction)
if err := tx.UnmarshalBinary(userTx); err != nil {
return common.Hash{}, err
}
signer, err := types.LatestSignerForChainID(s.chainID).Sender(tx)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to recover tx signer: %v", err)
}
// Refuse to attest to safety of transactions that deploy contracts.
if tx.To() == nil {
logrus.WithField("txHash", tx.Hash()).Debug("skipping attestation for contract deployment - tx forwarded")
return s.sendTx(ctx, userTx)
}
// The attester should give back a transaction.
attestTx, err := s.attester.AttestWithTx(ctx, &interfaces.AttestRequest{
From: signer,
To: *tx.To(),
Input: hexutil.Bytes(tx.Data()).String(),
Value: (*hexutil.Big)(tx.Value()),
ChainID: s.chainID.Uint64(),
})
if err == interfaces.ErrAttestationNotRequired {
logrus.WithField("txHash", tx.Hash()).WithField("tx", tx).Debug("attester says attestation is not required - tx forwarded")
return s.sendTx(ctx, userTx)
}
if err != nil {
logrus.
WithError(err).
WithField("txHash", tx.Hash()).Debug("attester returned error - operation failed")
return common.Hash{}, fmt.Errorf("attestation fails: %v", err)
}
// Send both txs in a bundle.
if err := s.bundler.SendBundle(ctx, []hexutil.Bytes{attestTx, userTx}); err != nil {
logrus.
WithError(err).
WithField("txHash", tx.Hash()).Debug("failed to send transactions")
return common.Hash{}, fmt.Errorf("failed to send transactions: %v", err)
}
return tx.Hash(), nil
}
func (s *wrapperService) sendTx(ctx context.Context, tx hexutil.Bytes) (common.Hash, error) {
return s.ethClient.SendRawTransaction(ctx, tx)
}
func txToArgs(signer common.Address, tx *types.Transaction) (txArgs TransactionArgs) {
txArgs.From = &signer
txArgs.To = tx.To()
gas := tx.Gas()
txArgs.Gas = (*hexutil.Uint64)(&gas)
txArgs.GasPrice = (*hexutil.Big)(tx.GasPrice())
txArgs.Value = (*hexutil.Big)(tx.Value())
data := tx.Data()
txArgs.Data = (*hexutil.Bytes)(&data)
return
}
// State overridden calls:
func (s *wrapperService) Call(ctx context.Context, txArgs TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, stateOverride *StateOverride, blockOverrides *BlockOverrides) (result hexutil.Bytes, err error) {
stateOverride = AddFortaFirewallStateOverride(stateOverride)
err = s.rpcClient.CallContext(ctx, &result, "eth_call", txArgs, blockNrOrHash, stateOverride)
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
"txArgs": txArgs,
"blockNrOrHash": blockNrOrHash,
"stateOverride": stateOverride,
"blockOverrides": blockOverrides,
}).Info("eth_call failed")
}
return
}
func (s *wrapperService) EstimateGas(ctx context.Context, txArgs TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, stateOverride *StateOverride) (result interface{}, err error) {
stateOverride = AddFortaFirewallStateOverride(stateOverride)
err = s.rpcClient.CallContext(ctx, &result, "eth_estimateGas", stateOverride)
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
"txArgs": txArgs,
"blockNrOrHash": blockNrOrHash,
"stateOverride": stateOverride,
}).Info("eth_estimateGas failed")
return nil, err
}
gas, _ := hexutil.DecodeBig(result.(string))
// TODO: Calculate gas bump based on percentage?
gas = gas.Add(gas, big.NewInt(50000))
return ((*hexutil.Big)(gas)).String(), nil
}