github.com/status-im/status-go@v1.1.0/services/wallet/thirdparty/fourbytegithub/client.go (about)

     1  package fourbytegithub
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"regexp"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum/accounts/abi"
    13  	"github.com/status-im/status-go/services/wallet/thirdparty"
    14  )
    15  
    16  type Signature struct {
    17  	ID   int    `json:"id"`
    18  	Text string `json:"text_signature"`
    19  }
    20  
    21  type ByID []Signature
    22  
    23  func (s ByID) Len() int           { return len(s) }
    24  func (s ByID) Less(i, j int) bool { return s[i].ID > s[j].ID }
    25  func (s ByID) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    26  
    27  type SignatureList struct {
    28  	Count   int         `json:"count"`
    29  	Results []Signature `json:"results"`
    30  }
    31  
    32  type Client struct {
    33  	Client *http.Client
    34  	URL    string
    35  }
    36  
    37  func NewClient() *Client {
    38  	return &Client{Client: &http.Client{Timeout: time.Minute}, URL: "https://raw.githubusercontent.com"}
    39  }
    40  
    41  func (c *Client) DoQuery(url string) (*http.Response, error) {
    42  	resp, err := c.Client.Get(url)
    43  
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	return resp, nil
    48  }
    49  
    50  func (c *Client) Run(data string) (*thirdparty.DataParsed, error) {
    51  	if len(data) < 10 || !strings.HasPrefix(data, "0x") {
    52  		return nil, errors.New("input is badly formatted")
    53  	}
    54  	methodSigData := data[2:10]
    55  	url := fmt.Sprintf("%s/ethereum-lists/4bytes/master/signatures/%s", c.URL, methodSigData)
    56  	resp, err := c.DoQuery(url)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	defer resp.Body.Close()
    61  
    62  	body, err := ioutil.ReadAll(resp.Body)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	signature := string(body)
    67  	rgx := regexp.MustCompile(`\((.*?)\)`)
    68  
    69  	id := fmt.Sprintf("0x%s", methodSigData)
    70  	name := strings.Split(signature, "(")[0]
    71  	rs := rgx.FindStringSubmatch(signature)
    72  	inputsMapString := make(map[string]string)
    73  
    74  	if len(rs[1]) > 0 {
    75  		inputs := make([]string, 0)
    76  		rawInputs := strings.Split(rs[1], ",")
    77  		for index, typ := range rawInputs {
    78  			if index == len(rawInputs)-1 && typ == "bytes" {
    79  				continue
    80  			}
    81  			inputs = append(inputs, fmt.Sprintf("{\"name\":\"%d\",\"type\":\"%s\"}", index, typ))
    82  		}
    83  
    84  		functionABI := fmt.Sprintf("[{\"constant\":true,\"inputs\":[%s],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\", \"name\": \"%s\"}], ", strings.Join(inputs, ","), name)
    85  		contractABI, err := abi.JSON(strings.NewReader(functionABI))
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  		method := contractABI.Methods[name]
    90  		inputsMap := make(map[string]interface{})
    91  		if err := method.Inputs.UnpackIntoMap(inputsMap, []byte(data[10:])); err != nil {
    92  			return nil, err
    93  		}
    94  		for key, value := range inputsMap {
    95  			inputsMapString[key] = fmt.Sprintf("%v", value)
    96  		}
    97  	}
    98  
    99  	return &thirdparty.DataParsed{
   100  		Name:      name,
   101  		ID:        id,
   102  		Signature: signature,
   103  		Inputs:    inputsMapString,
   104  	}, nil
   105  }