-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfrontrunner.go
133 lines (106 loc) · 3.19 KB
/
frontrunner.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 main
import (
"context"
"math/big"
"os"
"os/signal"
"time"
"github.com/FastLane-Labs/break-monad-frontrunner-bot/contract/frontrunner"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"golang.org/x/sync/errgroup"
)
var (
frontrunnerContractAddress = common.HexToAddress("0x9EaBA701a49adE7525dFfE338f0C7E06Eca7Cf07")
taskInterval = 1 * time.Second
)
func main() {
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stdout, log.LvlDebug, true)))
ethClient, err := ethclient.Dial(os.Getenv("RPC_URL"))
if err != nil {
log.Error("failed to connect to Ethereum client", "error", err)
os.Exit(1)
}
chainId, err := ethClient.ChainID(context.TODO())
if err != nil {
log.Error("failed to get chain ID", "error", err)
os.Exit(1)
}
pk, err := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
if err != nil {
log.Error("failed to parse private key", "error", err)
os.Exit(1)
}
executor, err := bind.NewKeyedTransactorWithChainID(pk, chainId)
if err != nil {
log.Error("failed to create executor", "error", err)
os.Exit(1)
}
frontrunnerContract, err := frontrunner.NewFrontrunner(frontrunnerContractAddress, ethClient)
if err != nil {
log.Error("failed to create Frontrunner contract", "error", err)
os.Exit(1)
}
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
go runBot(executor, frontrunnerContract, ethClient)
<-interrupt
}
func runBot(executor *bind.TransactOpts, frontrunnerContract *frontrunner.Frontrunner, ethClient *ethclient.Client) {
ticker := time.NewTicker(taskInterval)
defer ticker.Stop()
for range ticker.C {
feeCap, tipCap, err := getGasPrice(ethClient)
if err != nil {
log.Error("failed to get gas price", "error", err)
continue
}
executor.GasFeeCap = feeCap
executor.GasTipCap = tipCap
log.Info("sending frontrun", "maxFeePerGas", feeCap, "maxPriorityFeePerGas", tipCap)
tx, err := frontrunnerContract.Frontrun(executor)
if err != nil {
log.Error("failed to frontrun", "error", err)
continue
}
log.Info("frontrun sent, waiting for confirmation", "hash", tx.Hash().Hex())
receipt, err := bind.WaitMined(context.TODO(), ethClient, tx)
if err != nil {
log.Error("failed to land frontrun", "error", err)
continue
}
log.Info("confirmed frontrun", "reverted", receipt.Status == types.ReceiptStatusFailed)
}
}
// Returns calculated (maxFeePerGas, maxPriorityFeePerGas), override with better logic
func getGasPrice(ethClient *ethclient.Client) (*big.Int, *big.Int, error) {
var (
feeCap *big.Int
tipCap *big.Int
g, _ = errgroup.WithContext(context.TODO())
)
g.Go(func() error {
_feeCap, err := ethClient.SuggestGasPrice(context.TODO())
if err != nil {
return err
}
feeCap = _feeCap
return nil
})
g.Go(func() error {
_tipCap, err := ethClient.SuggestGasTipCap(context.TODO())
if err != nil {
return err
}
tipCap = _tipCap
return nil
})
if err := g.Wait(); err != nil {
return nil, nil, err
}
return feeCap, tipCap, nil
}