112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
|
package fourbyte
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"regexp"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||
|
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||
|
)
|
||
|
|
||
|
type Signature struct {
|
||
|
ID int `json:"id"`
|
||
|
Text string `json:"text_signature"`
|
||
|
}
|
||
|
|
||
|
type ByID []Signature
|
||
|
|
||
|
func (s ByID) Len() int { return len(s) }
|
||
|
func (s ByID) Less(i, j int) bool { return s[i].ID < s[j].ID }
|
||
|
func (s ByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||
|
|
||
|
type SignatureList struct {
|
||
|
Count int `json:"count"`
|
||
|
Results []Signature `json:"results"`
|
||
|
}
|
||
|
|
||
|
type Client struct {
|
||
|
client *http.Client
|
||
|
}
|
||
|
|
||
|
func NewClient() *Client {
|
||
|
return &Client{client: &http.Client{Timeout: time.Minute}}
|
||
|
}
|
||
|
|
||
|
func (c *Client) DoQuery(url string) (*http.Response, error) {
|
||
|
resp, err := c.client.Get(url)
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return resp, nil
|
||
|
}
|
||
|
|
||
|
func (c *Client) Run(data string) (*thirdparty.DataParsed, error) {
|
||
|
if len(data) < 10 || !strings.HasPrefix(data, "0x") {
|
||
|
return nil, errors.New("input is badly formatted")
|
||
|
}
|
||
|
methodSigData := data[2:10]
|
||
|
url := fmt.Sprintf("https://www.4byte.directory/api/v1/signatures/?hex_signature=%s", methodSigData)
|
||
|
resp, err := c.DoQuery(url)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
body, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var signatures SignatureList
|
||
|
err = json.Unmarshal(body, &signatures)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if signatures.Count == 0 {
|
||
|
return nil, err
|
||
|
}
|
||
|
rgx := regexp.MustCompile(`\((.*?)\)`)
|
||
|
results := signatures.Results
|
||
|
sort.Sort(ByID(results))
|
||
|
for _, signature := range results {
|
||
|
name := strings.Split(signature.Text, "(")[0]
|
||
|
rs := rgx.FindStringSubmatch(signature.Text)
|
||
|
|
||
|
inputs := make([]string, 0)
|
||
|
for index, typ := range strings.Split(rs[1], ",") {
|
||
|
inputs = append(inputs, fmt.Sprintf("{\"name\":\"%d\",\"type\":\"%s\"}", index, typ))
|
||
|
}
|
||
|
|
||
|
functionABI := fmt.Sprintf("[{\"constant\":true,\"inputs\":[%s],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\", \"name\": \"%s\"}], ", strings.Join(inputs, ","), name)
|
||
|
contractABI, err := abi.JSON(strings.NewReader(functionABI))
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
method := contractABI.Methods[name]
|
||
|
inputsMap := make(map[string]interface{})
|
||
|
if err := method.Inputs.UnpackIntoMap(inputsMap, []byte(data[10:])); err != nil {
|
||
|
continue
|
||
|
}
|
||
|
inputsMapString := make(map[string]string)
|
||
|
for key, value := range inputsMap {
|
||
|
inputsMapString[key] = fmt.Sprintf("%v", value)
|
||
|
}
|
||
|
|
||
|
return &thirdparty.DataParsed{
|
||
|
Name: name,
|
||
|
Signature: signature.Text,
|
||
|
Inputs: inputsMapString,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
return nil, errors.New("couldn't find a corresponding signature")
|
||
|
}
|