123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- package eth
- import (
- "context"
- "crypto/ecdsa"
- "encoding/json"
- "fmt"
- "log"
- "math/big"
- "strings"
- "syncscan-go/erc20"
- "syncscan-go/model"
- "github.com/ethereum/go-ethereum"
- "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/crypto"
- "github.com/ethereum/go-ethereum/ethclient"
- "github.com/pkg/errors"
- "github.com/ybbus/jsonrpc/v2"
- "golang.org/x/crypto/sha3"
- )
- type Eth struct {
- host string
- client *ethclient.Client
- RpcClient jsonrpc.RPCClient
- }
- var LogTransferSignHash = crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))
- func New(rawurl string) (*Eth, error) {
- client, err := ethclient.Dial(rawurl)
- if err != nil {
- fmt.Printf("Failed to connect to eth: %v", err)
- return nil, err
- }
- rpcClient := jsonrpc.NewClient(rawurl)
- return &Eth{host: rawurl, client: client, RpcClient: rpcClient}, nil
- }
- func (this *Eth) GetTxByHash(hash string) (tx model.Tx, err error) {
- response, err := this.RpcClient.Call("eth_getTransactionByHash", hash)
- if err != nil {
- return
- }
- log.Printf("%+v\n\n\n", response.Result)
- err = response.GetObject(&tx)
- if err != nil {
- return
- }
- return
- }
- func (this *Eth) GetLastBlockNumber() (*big.Int, error) {
- header, err := this.client.HeaderByNumber(context.Background(), nil)
- if err != nil {
- return nil, err
- }
- return header.Number, nil
- }
- func (this *Eth) GetBlockByNumber(number *big.Int) (*types.Block, error) {
- b, err := this.client.BlockByNumber(context.Background(), number)
- if err != nil {
- return nil, err
- }
- return b, nil
- }
- const ETH = 1_000_000_000_000_000_000
- func getFixedReward(blockNo uint64) uint64 {
- if blockNo < 4370000 {
- return ETH * 5
- } else if blockNo < 7280000 {
- return ETH * 3
- } else {
- return ETH * 2
- }
- }
- // 당근을 위해 커스터마이징 된 부분
- func getFixedRewardForDangnn(blockNo uint64) uint64 {
- return ETH * 3
- }
- func (this *Eth) GetReward(b *types.Block, rts types.Receipts) (blockReward *big.Float, txFee *big.Int, uncleTotalReward *big.Float, err error) {
- fixedReward := getFixedRewardForDangnn(b.Number().Uint64())
- blockReward = big.NewFloat(float64(fixedReward))
- txFee = big.NewInt(0)
- txs := b.Transactions()
- for i, rt := range rts {
- gasUsed := big.NewInt(int64(rt.GasUsed))
- fee := new(big.Int).Mul(gasUsed, txs[i].GasPrice())
- txFee.Add(txFee, fee)
- }
- uncleCount := len(b.Uncles())
- if uncleCount > 2 {
- uncleCount = 2
- }
- uncleTotalReward = big.NewFloat(0)
- if uncleCount > 0 {
- //블럭 마이너에게 돌아가는 리워드
- uncleInclusionFee := (float64(fixedReward) * 0.03125) * float64(uncleCount)
- for i, u := range b.Uncles() {
- if i == 2 {
- break
- }
- //엉클블럭 마이너에게 돌아가는 리워드
- uncleReward := float64(u.Number.Uint64() - b.Number().Uint64() + 8)
- uncleReward *= float64(fixedReward / 8)
- uncleTotalReward.Add(uncleTotalReward, big.NewFloat(uncleReward))
- }
- uncleTotalReward.Add(uncleTotalReward, big.NewFloat(uncleInclusionFee))
- }
- //Fixed_Fee + Tx_Fee + Uncle_Fee
- blockReward.Add(blockReward, big.NewFloat(float64(txFee.Uint64())))
- blockReward.Add(blockReward, uncleTotalReward)
- return blockReward, txFee, uncleTotalReward, nil
- }
- func (this *Eth) GetTxReceipts(transactions types.Transactions) (ret types.Receipts, err error) {
- for _, tx := range transactions {
- receipt, err := this.client.TransactionReceipt(context.Background(), tx.Hash())
- if err != nil {
- return nil, err
- }
- if receipt != nil {
- ret = append(ret, receipt)
- }
- }
- return
- }
- func (this *Eth) GetTxReceipt(txhash string) (ret *types.Receipt, err error) {
- receipt, err := this.client.TransactionReceipt(context.Background(), common.HexToHash(txhash))
- if err != nil {
- return
- }
- if receipt != nil {
- ret = receipt
- }
- return
- }
- func (this *Eth) GetToken(contractAddr common.Address) (token *erc20.Erc20, err error) {
- token, err = erc20.NewErc20(contractAddr, this.client)
- return
- }
- func (this *Eth) GetUncleBlockByBlockNumberAndIndex(blockNo uint64, idx int) (interface{}, error) {
- b, err := this.RpcClient.Call("eth_getUncleByBlockNumberAndIndex", fmt.Sprintf("0x%x", blockNo), fmt.Sprintf("0x%x", idx))
- if err != nil {
- return "", err
- }
- return b.Result, nil
- }
- func (this *Eth) CreateWallet() (privateKey, walletAddr string) {
- key, err := crypto.GenerateKey()
- if err != nil {
- log.Fatal(err)
- }
- privateKeyBytes := crypto.FromECDSA(key)
- privateKey = hexutil.Encode(privateKeyBytes)[2:]
- publicKey := key.Public()
- publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
- if !ok {
- log.Fatal("error casting public key to ECDSA")
- }
- publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
- //log.Println(hexutil.Encode(publicKeyBytes)[4:])
- //address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
- hash := sha3.NewLegacyKeccak256()
- hash.Write(publicKeyBytes[1:])
- walletAddr = hexutil.Encode(hash.Sum(nil)[12:])
- return
- }
- func (this *Eth) GetWalletAddressFromPrivateKey(privateKey string) (string, error) {
- if privateKey[:2] == "0x" {
- privateKey = privateKey[2:]
- }
- pkey, err := crypto.HexToECDSA(privateKey)
- if err != nil {
- return "", err
- }
- publicKey := pkey.Public()
- publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
- if !ok {
- return "", errors.New("error casting public key to ECDSA")
- }
- fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
- return fromAddress.Hex(), nil
- }
- func (this *Eth) Transfer(privateKey, to string, valueInWei, gasPrice *big.Int, gasLimit uint64) (txHash string, err error) {
- if privateKey[:2] == "0x" {
- privateKey = privateKey[2:]
- }
- if to[:2] == "0x" {
- to = to[2:]
- }
- pkey, err := crypto.HexToECDSA(privateKey)
- if err != nil {
- return
- }
- publicKey := pkey.Public()
- publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
- if !ok {
- err = errors.New("error casting public key to ECDSA")
- }
- fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
- nonce, err := this.getNonce(fromAddress.Hex())
- if err != nil {
- return
- }
- toAddress := common.HexToAddress(to)
- tx := types.NewTransaction(nonce, toAddress, valueInWei, gasLimit, gasPrice, nil)
- chainID, err := this.client.NetworkID(context.Background())
- if err != nil {
- return
- }
- signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), pkey)
- if err != nil {
- log.Fatal(err)
- }
- err = this.client.SendTransaction(context.Background(), signedTx)
- if err != nil {
- return
- }
- txHash = signedTx.Hash().Hex()
- log.Printf("tx sent: %s\n", txHash)
- return
- }
- func (this *Eth) getNonce(fromAddress string) (uint64, error) {
- resp, err := this.RpcClient.Call("eth_getTransactionCount", fromAddress, "pending")
- if err != nil {
- return 0, err
- }
- m := resp.Result.(string)
- nonce, err := hexutil.DecodeUint64(m)
- return nonce, err
- }
- func (this *Eth) GetSuggestedGasPrice() (*big.Int, error) {
- gasPrice, err := this.client.SuggestGasPrice(context.Background())
- return gasPrice, err
- }
- func (this *Eth) GetEstimatedGasLimit(tokenAddr, pkey, toAddr string, tokenAmount *big.Int) ([]byte, uint64, error) {
- if strings.HasPrefix(pkey, "0x") {
- pkey = pkey[2:]
- }
- privateKey, err := crypto.HexToECDSA(pkey)
- if err != nil {
- return nil, 0, err
- }
- publicKey := privateKey.Public()
- publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
- if !ok {
- err = errors.New("error casting public key to ECDSA")
- return nil, 0, err
- }
- fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
- toAddress := common.HexToAddress(toAddr)
- tokenAddress := common.HexToAddress(tokenAddr)
- transferFnSignature := []byte("transfer(address,uint256)")
- hash := sha3.NewLegacyKeccak256()
- hash.Write(transferFnSignature)
- methodID := hash.Sum(nil)[:4]
- paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32)
- paddedAmount := common.LeftPadBytes(tokenAmount.Bytes(), 32)
- var data []byte
- data = append(data, methodID...)
- data = append(data, paddedAddress...)
- data = append(data, paddedAmount...)
- gasLimit, err := this.client.EstimateGas(context.Background(), ethereum.CallMsg{
- From: fromAddress,
- To: &tokenAddress,
- Data: data,
- })
- return data, gasLimit, err
- }
- func (this *Eth) TransferToken(tokenAddr, pkey, toAddr string, tokenAmount *big.Int, gasPrice *big.Int, gasLimit uint64) (txHash string, err error) {
- if strings.HasPrefix(pkey, "0x") {
- pkey = pkey[2:]
- }
- privateKey, err := crypto.HexToECDSA(pkey)
- if err != nil {
- return "", err
- }
- publicKey := privateKey.Public()
- publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
- if !ok {
- err = errors.New("error casting public key to ECDSA")
- return "", err
- }
- fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
- nonce, err := this.getNonce(fromAddress.Hex())
- if err != nil {
- return "", err
- }
- value := big.NewInt(0) // in wei (0 eth)
- tokenAddress := common.HexToAddress(tokenAddr)
- data, _, err := this.GetEstimatedGasLimit(tokenAddr, pkey, toAddr, tokenAmount)
- if err != nil {
- return "", err
- }
- v := &transferValue{
- nonce: nonce,
- gasLimit: gasLimit,
- gasPrice: gasPrice,
- }
- log.Printf("nonce: %d, gas price: %d, gas limit: %d\n", nonce, v.gasPrice, v.gasLimit) // 25624
- //gasPrice = gasPrice.Add(gasPrice, big.NewInt(100_000_000))
- //gasPrice = big.NewInt(5320000000)
- tx := types.NewTransaction(v.nonce, tokenAddress, value, v.gasLimit, v.gasPrice, data)
- chainID, err := this.client.NetworkID(context.Background())
- if err != nil {
- return "", err
- }
- signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
- if err != nil {
- return "", err
- }
- err = this.client.SendTransaction(context.Background(), signedTx)
- if err != nil {
- return "", err
- }
- return signedTx.Hash().Hex(), nil
- }
- func (this *Eth) GetTokenBalance(tokenAddress, address string) (balance *big.Int, err error) {
- token, err := this.GetToken(common.HexToAddress(tokenAddress))
- b, err := token.BalanceOf(nil, common.HexToAddress(address))
- //token.Transfer(&bind.TransactOpts{
- // From: common.Address{},
- // Nonce: nil,
- // Signer: nil,
- // Value: nil,
- // GasPrice: nil,
- // GasFeeCap: nil,
- // GasTipCap: nil,
- // GasLimit: 0,
- // Context: nil,
- // NoSend: false,
- //}, common.HexToAddress(address), big.NewInt(100))
- return b, err
- }
- func (this *Eth) GetBalance(address string) (balance *big.Int, err error) {
- resp, err := this.RpcClient.Call("eth_getBalance", address, "latest")
- if err != nil {
- return
- }
- b, _ := new(big.Int).SetString(resp.Result.(string)[2:], 16)
- return b, nil
- }
- func (this *Eth) GetInternalTxsByHash(blockNo uint64, txHash string) (calls []model.Call, err error) {
- lastestBlockNo, err := this.GetLastBlockNumber()
- if err != nil {
- return
- }
- args := make([]interface{}, 0)
- args = append(args, txHash)
- args = append(args, map[string]interface {
- }{
- "tracer": "callTracer",
- "reexec": lastestBlockNo.Uint64() - blockNo + 20,
- })
- resp, err := this.RpcClient.Call("debug_traceTransaction", args)
- if err != nil {
- return nil, err
- }
- data, _ := json.Marshal(resp.Result)
- log.Println(string(data))
- m, ok := resp.Result.(map[string]interface{})
- if !ok {
- return
- }
- data, err = json.Marshal(m["calls"])
- if err != nil {
- return
- }
- err = json.Unmarshal(data, &calls)
- if err != nil {
- return
- }
- return
- }
|