github.com/hungdoo/bot@v0.0.0-20240325145135-dd1f386f7b81/src/packages/tombplus/client.go (about) 1 package tombplus 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "fmt" 7 "math/big" 8 "strings" 9 10 "github.com/ethereum/go-ethereum" 11 "github.com/ethereum/go-ethereum/accounts/abi/bind" 12 "github.com/ethereum/go-ethereum/core/types" 13 "github.com/ethereum/go-ethereum/ethclient" 14 "github.com/hungdoo/bot/src/common" 15 "github.com/hungdoo/bot/src/packages/utils" 16 "github.com/shopspring/decimal" 17 18 ethCommon "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/rpc" 20 ) 21 22 type TombplusClient struct { 23 ec *ethclient.Client 24 Tomb *Tombplus 25 } 26 27 var _client *TombplusClient 28 29 func GetClient(rpcEndpoint string, contractAddress ethCommon.Address) (*TombplusClient, error) { 30 if _client != nil { 31 return _client, nil 32 } 33 rpcclient, err := rpc.Dial(rpcEndpoint) 34 if err != nil { 35 return nil, err 36 } 37 38 ec := ethclient.NewClient(rpcclient) 39 tomeplusContract, err := GetTombplusContract(ec, contractAddress) 40 if err != nil { 41 return nil, err 42 } 43 _client = &TombplusClient{ 44 ec: ec, 45 Tomb: tomeplusContract, 46 } 47 return _client, nil 48 } 49 50 func (c *TombplusClient) MaxAllowedFutureFlips() int64 { 51 val, err := c.Tomb.MaxAllowedFutureFlips(&bind.CallOpts{}) 52 if err != nil { 53 return -1 54 } 55 return val.Int64() 56 } 57 58 func (c *TombplusClient) GameStarted() bool { 59 val, err := c.Tomb.GameStarted(&bind.CallOpts{}) 60 if err != nil { 61 return false 62 } 63 return val 64 } 65 66 func (c *TombplusClient) GetRewards(user ethCommon.Address) decimal.Decimal { 67 reward, err := c.Tomb.RewardBalance(&bind.CallOpts{}, user) 68 if err != nil { 69 return decimal.Zero 70 } 71 return utils.DivDecimals(decimal.NewFromBigInt(reward, 0), 18) 72 } 73 74 func (c *TombplusClient) GetUserLastedVoteEpochId(user ethCommon.Address) (*big.Int, error) { 75 flips, err := c.Tomb.GetUserFlips(&bind.CallOpts{}, user) 76 if err != nil { 77 return nil, err 78 } 79 latestEpoch := big.NewInt(-1) 80 for _, v := range flips { 81 if v.EpochId.Cmp(latestEpoch) >= 1 { 82 latestEpoch = v.EpochId 83 } 84 } 85 86 return latestEpoch, nil 87 } 88 89 func (c *TombplusClient) CanFlipForCurrentEpoch() (bool, error) { 90 ok, err := c.Tomb.CanFlipForCurrentEpoch(&bind.CallOpts{}) 91 if err != nil { 92 return false, err 93 } 94 return ok, nil 95 } 96 func (c *TombplusClient) CurrentEpoch() int64 { 97 epochNum, err := c.Tomb.CurrentEpochId(&bind.CallOpts{}) 98 if err != nil { 99 return -1 100 } 101 return epochNum.Int64() 102 } 103 104 func (c *TombplusClient) GetPauseGameAtEpoch() int64 { 105 val, err := c.Tomb.PauseGameAtEpoch(&bind.CallOpts{}) 106 if err != nil { 107 return 0 108 } 109 return val.Int64() 110 } 111 112 func (c *TombplusClient) GetUpgradedMasonry() ethCommon.Address { 113 val, err := c.Tomb.UpgradedMasonry(&bind.CallOpts{}) 114 if err != nil { 115 return ethCommon.Address{} 116 } 117 return val 118 } 119 120 func (c *TombplusClient) IsVotedAtEpoch(user ethCommon.Address, epoch int64) (bool, error) { 121 val, err := c.Tomb.GetUserFlipIdByEpochId(&bind.CallOpts{}, user, big.NewInt(epoch)) 122 if err != nil { 123 return false, err 124 } 125 return val.Found, nil 126 } 127 128 func (c *TombplusClient) Claim(privateKey *ecdsa.PrivateKey, maxGas *big.Int) (*types.Transaction, *common.ErrorWithSeverity) { 129 noSendOpts, err := NewAuthorizedTransactor(c.ec, privateKey, 0, maxGas, big.NewInt(0)) 130 if err != nil { 131 return nil, common.NewErrorWithSeverity(common.Error, err.Error()) 132 } 133 134 signedTx, err := c.Tomb.Claim(noSendOpts) 135 if err != nil { 136 return nil, common.NewErrorWithSeverity(common.Error, err.Error()) 137 } 138 139 return c.dryrunAndSend(noSendOpts.From, signedTx) 140 } 141 142 func (c *TombplusClient) Flip(privateKey *ecdsa.PrivateKey, maxGas *big.Int, up bool) (*types.Transaction, *common.ErrorWithSeverity) { 143 noSendOpts, err := NewAuthorizedTransactor(c.ec, privateKey, 0, maxGas, big.NewInt(0)) 144 if err != nil { 145 return nil, common.NewErrorWithSeverity(common.Critical, err.Error()) 146 } 147 signedTx, err := c.Tomb.Flip(noSendOpts, up) 148 if err != nil { 149 return nil, common.NewErrorWithSeverity(common.Critical, err.Error()) 150 } 151 return c.dryrunAndSend(noSendOpts.From, signedTx) 152 } 153 154 func (c *TombplusClient) Flipmultiple(privateKey *ecdsa.PrivateKey, maxGas *big.Int, epochs int64, up bool) (*types.Transaction, *common.ErrorWithSeverity) { 155 noSendOpts, err := NewAuthorizedTransactor(c.ec, privateKey, 0, maxGas, big.NewInt(0)) 156 if err != nil { 157 return nil, common.NewErrorWithSeverity(common.Error, err.Error()) 158 } 159 160 ups := make([]bool, epochs) 161 for i := int64(0); i < epochs; i++ { 162 ups[i] = up 163 } 164 165 signedTx, err := c.Tomb.FlipMultiple(noSendOpts, ups) 166 if err != nil { 167 return nil, common.NewErrorWithSeverity(common.Error, err.Error()) 168 } 169 return c.dryrunAndSend(noSendOpts.From, signedTx) 170 } 171 172 func (c *TombplusClient) CheckResult(txHash string) *common.ErrorWithSeverity { 173 // Wait for the transaction to be mined 174 // receipt, err := bind.WaitMined(context.Background(), c.ec, tx) 175 receipt, err := c.ec.TransactionReceipt(context.Background(), ethCommon.HexToHash(txHash)) 176 if err != nil { 177 // rpc error 178 return common.NewErrorWithSeverity(common.Error, err.Error()) 179 } 180 181 if receipt.Status == 0 { 182 reasons := []string{} 183 for _, l := range receipt.Logs { 184 reasons = append(reasons, string(l.Data)) 185 } 186 187 // onchain tx reverted, raise to Critical 188 return common.NewErrorWithSeverity(common.Critical, fmt.Sprintf("transaction %s was reverted with reason: %s", receipt.TxHash.Hex(), strings.Join(reasons, "\n"))) 189 } 190 191 return nil 192 } 193 194 func (c *TombplusClient) dryrunAndSend(fromAddress ethCommon.Address, signedTx *types.Transaction) (*types.Transaction, *common.ErrorWithSeverity) { 195 // Dryrun first to save gas in case of revert 196 _, err := c.ec.CallContract(context.Background(), ethereum.CallMsg{ 197 To: signedTx.To(), 198 From: fromAddress, 199 Gas: signedTx.Gas(), 200 GasPrice: signedTx.GasPrice(), 201 Value: big.NewInt(0), 202 Data: signedTx.Data(), 203 }, nil) 204 if err != nil { 205 return nil, common.NewErrorWithSeverity(common.Critical, err.Error()) 206 } 207 208 err = c.ec.SendTransaction(context.Background(), signedTx) 209 if err != nil { 210 // rpc error 211 return nil, common.NewErrorWithSeverity(common.Critical, err.Error()) 212 } 213 return signedTx, nil 214 }