github.com/diadata-org/diadata@v1.4.593/pkg/dia/helpers/ethhelper/tokenInfo.go (about)

     1  package ethhelper
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"io/ioutil"
     7  	"math/big"
     8  	"os"
     9  	"os/user"
    10  	"strings"
    11  
    12  	"github.com/diadata-org/diadata/pkg/dia"
    13  	"github.com/ethereum/go-ethereum/accounts/abi"
    14  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    15  	"github.com/ethereum/go-ethereum/common"
    16  	"github.com/ethereum/go-ethereum/ethclient"
    17  	log "github.com/sirupsen/logrus"
    18  )
    19  
    20  const tokenABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"mintingFinished\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"unpause\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"finishMinting\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"pause\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_amount\",\"type\":\"uint256\"},{\"name\":\"_releaseTime\",\"type\":\"uint256\"}],\"name\":\"mintTimelocked\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"MintFinished\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Pause\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Unpause\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"}]" //nolint:gosec
    21  
    22  // const tokenABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\": \"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\": \"_owner\",\"type\":\"address\"}],\"name\": \"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"}]"
    23  
    24  // TokenCaller is an auto generated read-only Go binding around an Ethereum contract.
    25  type TokenCaller struct {
    26  	Contract *bind.BoundContract // Generic contract wrapper for the low level calls
    27  }
    28  
    29  // NewETHClient returns an ethclient, using either our node for production or infura for testing
    30  func NewETHClient() (*ethclient.Client, error) {
    31  	executionMode := os.Getenv("EXEC_MODE")
    32  	var connection *ethclient.Client
    33  	var err error
    34  	if executionMode == "production" {
    35  		connection, err = ethclient.Dial("http://159.69.120.42:8545/")
    36  		if err != nil {
    37  			log.Error("Error connecting Eth Client")
    38  		}
    39  	} else {
    40  		connection, err = ethclient.Dial("https://mainnet.infura.io/v3/806b0419b2d041869fc83727e0043236")
    41  		if err != nil {
    42  			log.Error("Error connecting Eth Client")
    43  		}
    44  	}
    45  	return connection, err
    46  }
    47  
    48  // NewTokenCaller creates a new read-only instance of token, bound to a specific deployed contract.
    49  func NewTokenCaller(address common.Address, caller bind.ContractCaller) (*TokenCaller, error) {
    50  	contract, err := bindToken(address, caller, nil)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return &TokenCaller{Contract: contract}, nil
    55  }
    56  
    57  // bindToken binds a generic wrapper to an already deployed contract.
    58  func bindToken(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) {
    59  	parsed, err := abi.JSON(strings.NewReader(tokenABI))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	return bind.NewBoundContract(address, parsed, caller, transactor, nil), nil
    64  }
    65  
    66  func GetBalanceOf(tokenAddress common.Address, walletAddress common.Address, client *ethclient.Client) (*big.Int, error) {
    67  	var balance []interface{}
    68  	tokenCaller, err := NewTokenCaller(tokenAddress, client)
    69  	if err != nil {
    70  		return big.NewInt(0), err
    71  	}
    72  	err = tokenCaller.Contract.Call(&bind.CallOpts{}, &balance, "balanceOf", walletAddress)
    73  	if err != nil {
    74  		return big.NewInt(0), err
    75  	}
    76  	if len(balance) > 0 {
    77  		return balance[0].(*big.Int), nil
    78  	}
    79  	return big.NewInt(0), errors.New("Not enough data.")
    80  }
    81  
    82  // ETHAddressToAsset takes an Ethereum address and returns the underlying
    83  // token as a dia.Asset.
    84  func ETHAddressToAsset(address common.Address, client *ethclient.Client, blockchainName string) (dia.Asset, error) {
    85  	var asset dia.Asset
    86  	tc, err := NewTokenCaller(address, client)
    87  	if err != nil {
    88  		log.Error("error: ", err)
    89  	}
    90  
    91  	asset.Address = address.Hex()
    92  
    93  	var symbol []interface{}
    94  	err = tc.Contract.Call(&bind.CallOpts{}, &symbol, "symbol")
    95  	if err != nil {
    96  		return dia.Asset{}, err
    97  	}
    98  	asset.Symbol = symbol[0].(string)
    99  
   100  	var name []interface{}
   101  	err = tc.Contract.Call(&bind.CallOpts{}, &name, "name")
   102  	if err != nil {
   103  		return dia.Asset{}, err
   104  	}
   105  	asset.Name = name[0].(string)
   106  
   107  	var decimals []interface{}
   108  	err = tc.Contract.Call(&bind.CallOpts{}, &decimals, "decimals")
   109  	if err != nil {
   110  		return dia.Asset{}, err
   111  	}
   112  	aux := decimals[0].(*big.Int)
   113  	asset.Decimals = uint8(aux.Int64())
   114  	asset.Blockchain = blockchainName
   115  	return asset, err
   116  }
   117  
   118  func ConfigFilePath(filename string) string {
   119  	usr, _ := user.Current()
   120  	dir := usr.HomeDir
   121  	if dir == "/root" || dir == "/home" {
   122  		return "/config/token_supply/" + filename + ".json" //hack for docker...
   123  	}
   124  	if dir == "/home/travis" {
   125  		return "../config/token_supply/" + filename + ".json" //hack for travis
   126  	}
   127  	return os.Getenv("GOPATH") + "/src/github.com/diadata-org/diadata/config/token_supply/" + filename + ".json"
   128  }
   129  
   130  // GetAddressesFromFile fetches token addresses from a config file available here:
   131  // https://etherscan.io/exportData?type=open-source-contract-codes
   132  func GetAddressesFromFile(filename string) (addresses []string, err error) {
   133  	jsonFile, err := os.Open(ConfigFilePath(filename))
   134  	if err != nil {
   135  		log.Errorln("Error opening file", err)
   136  		return
   137  	}
   138  	defer func() {
   139  		cerr := jsonFile.Close()
   140  		if err == nil {
   141  			err = cerr
   142  		}
   143  	}()
   144  
   145  	byteData, err := ioutil.ReadAll(jsonFile)
   146  	if err != nil {
   147  		log.Error(err)
   148  		return
   149  	}
   150  	// For now we only return token address. Name can be retrieved through contract.
   151  	type token struct {
   152  		Address string
   153  		Name    string
   154  	}
   155  	type TokenInfo struct {
   156  		Tokens []token `json:"Tokens"`
   157  	}
   158  	var tokeninfo TokenInfo
   159  	err = json.Unmarshal(byteData, &tokeninfo)
   160  	if err != nil {
   161  		return
   162  	}
   163  	for _, token := range tokeninfo.Tokens {
   164  		addresses = append(addresses, token.Address)
   165  	}
   166  	return
   167  }