github.com/wormhole-foundation/wormhole-explorer/common@v0.0.0-20240604151348-09585b5b97c5/client/parser/parser.go (about)

     1  package parser
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"time"
    11  
    12  	sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
    13  	"go.uber.org/zap"
    14  )
    15  
    16  const DefaultTimeout = 10
    17  
    18  var (
    19  	ErrCallEndpoint        = errors.New("ERROR CALL ENPOINT")
    20  	ErrNotFound            = errors.New("NOT FOUND")
    21  	ErrInternalError       = errors.New("INTERNAL ERROR")
    22  	ErrUnprocessableEntity = errors.New("UNPROCESSABLE")
    23  	ErrBadRequest          = errors.New("BAD REQUEST")
    24  )
    25  
    26  // ParseVaaResponse represent a parse vaa response.
    27  type ParseVaaResponse struct {
    28  	ChainID        uint16      `json:"chainId"`
    29  	EmitterAddress string      `json:"address"`
    30  	Sequence       string      `json:"sequence"`
    31  	AppID          string      `json:"appId"`
    32  	Result         interface{} `json:"result"`
    33  }
    34  
    35  // ParserVAAAPIClient parse vaa api client.
    36  type ParserVAAAPIClient struct {
    37  	Client  http.Client
    38  	BaseURL string
    39  	Logger  *zap.Logger
    40  }
    41  
    42  // ParseVaaFunc represent a parse vaa function.
    43  type ParseVaaFunc func(vaa *sdk.VAA) (any, error)
    44  
    45  // NewParserVAAAPIClient create new instances of ParserVAAAPIClient.
    46  func NewParserVAAAPIClient(timeout int64, baseURL string, logger *zap.Logger) (ParserVAAAPIClient, error) {
    47  	if timeout == 0 {
    48  		timeout = DefaultTimeout
    49  	}
    50  	if baseURL == "" {
    51  		return ParserVAAAPIClient{}, errors.New("baseURL can not be empty")
    52  	}
    53  
    54  	return ParserVAAAPIClient{
    55  		Client: http.Client{
    56  			Timeout: time.Duration(timeout) * time.Second,
    57  		},
    58  		BaseURL: baseURL,
    59  		Logger:  logger,
    60  	}, nil
    61  }
    62  
    63  type ParseData struct {
    64  	PayloadID int `bson:"payloadid"`
    65  	Fields    interface{}
    66  }
    67  
    68  // ParsePayload invoke the endpoint to parse a VAA from the VAAParserAPI.
    69  func (c ParserVAAAPIClient) ParsePayload(chainID uint16, address, sequence string, vaa []byte) (*ParseVaaResponse, error) {
    70  	endpointUrl := fmt.Sprintf("%s/vaa/parser/%v/%s/%v", c.BaseURL, chainID,
    71  		address, sequence)
    72  
    73  	// create request body.
    74  	payload := struct {
    75  		Payload []byte `json:"payload"`
    76  	}{
    77  		Payload: vaa,
    78  	}
    79  
    80  	body, err := json.Marshal(payload)
    81  	if err != nil {
    82  		c.Logger.Error("error marshalling payload", zap.Error(err), zap.Uint16("chainID", chainID),
    83  			zap.String("address", address), zap.String("sequence", sequence))
    84  		return nil, err
    85  	}
    86  
    87  	response, err := c.Client.Post(endpointUrl, "application/json", bytes.NewBuffer(body))
    88  	if err != nil {
    89  		c.Logger.Error("error call parse vaa endpoint", zap.Error(err), zap.Uint16("chainID", chainID),
    90  			zap.String("address", address), zap.String("sequence", sequence))
    91  		return nil, ErrCallEndpoint
    92  	}
    93  	defer response.Body.Close()
    94  
    95  	switch response.StatusCode {
    96  	case http.StatusCreated:
    97  		var parsedVAA ParseVaaResponse
    98  		json.NewDecoder(response.Body).Decode(&parsedVAA)
    99  		return &parsedVAA, nil
   100  	case http.StatusNotFound:
   101  		return nil, ErrNotFound
   102  	case http.StatusBadRequest:
   103  		return nil, ErrBadRequest
   104  	case http.StatusUnprocessableEntity:
   105  		return nil, ErrUnprocessableEntity
   106  	default:
   107  		return nil, ErrInternalError
   108  	}
   109  }
   110  
   111  // StandardizedProperties represent a standardized properties.
   112  type StandardizedProperties struct {
   113  	AppIds       []string    `json:"appIds" bson:"appIds"`
   114  	FromChain    sdk.ChainID `json:"fromChain" bson:"fromChain"`
   115  	FromAddress  string      `json:"fromAddress" bson:"fromAddress"`
   116  	ToChain      sdk.ChainID `json:"toChain" bson:"toChain"`
   117  	ToAddress    string      `json:"toAddress" bson:"toAddress"`
   118  	TokenChain   sdk.ChainID `json:"tokenChain" bson:"tokenChain"`
   119  	TokenAddress string      `json:"tokenAddress" bson:"tokenAddress"`
   120  	Amount       string      `json:"amount" bson:"amount"`
   121  	FeeAddress   string      `json:"feeAddress" bson:"feeAddress"`
   122  	FeeChain     sdk.ChainID `json:"feeChain" bson:"feeChain"`
   123  	Fee          string      `json:"fee" bson:"fee"`
   124  }
   125  
   126  // ParseVaaWithStandarizedPropertiesdResponse represent a parse vaa response.
   127  type ParseVaaWithStandarizedPropertiesdResponse struct {
   128  	ParsedPayload          any                    `json:"parsedPayload"`
   129  	StandardizedProperties StandardizedProperties `json:"standardizedProperties"`
   130  }
   131  
   132  // ParseVaaWithStandarizedProperties invoke the endpoint to parse a VAA from the VAAParserAPI.
   133  func (c *ParserVAAAPIClient) ParseVaaWithStandarizedProperties(vaa *sdk.VAA) (*ParseVaaWithStandarizedPropertiesdResponse, error) {
   134  	endpointUrl := fmt.Sprintf("%s/vaas/parse", c.BaseURL)
   135  
   136  	vaaBytes, err := vaa.Marshal()
   137  	if err != nil {
   138  		return nil, errors.New("error marshalling vaa")
   139  	}
   140  
   141  	body := base64.StdEncoding.EncodeToString(vaaBytes)
   142  	response, err := c.Client.Post(endpointUrl, "text/plain", bytes.NewBuffer([]byte(body)))
   143  	if err != nil {
   144  		c.Logger.Error("error call parse vaa endpoint", zap.Error(err), zap.Uint16("chainID", uint16(vaa.EmitterChain)),
   145  			zap.String("address", vaa.EmitterAddress.String()), zap.Uint64("sequence", vaa.Sequence))
   146  		return nil, ErrCallEndpoint
   147  	}
   148  	defer response.Body.Close()
   149  
   150  	switch response.StatusCode {
   151  	case http.StatusCreated:
   152  		var parsedVAA ParseVaaWithStandarizedPropertiesdResponse
   153  		json.NewDecoder(response.Body).Decode(&parsedVAA)
   154  		return &parsedVAA, nil
   155  	case http.StatusNotFound:
   156  		return nil, ErrNotFound
   157  	case http.StatusBadRequest:
   158  		return nil, ErrBadRequest
   159  	case http.StatusUnprocessableEntity:
   160  		return nil, ErrUnprocessableEntity
   161  	default:
   162  		return nil, ErrInternalError
   163  	}
   164  }
   165  
   166  // ParseVaa invoke the endpoint to parse a VAA from the VAAParserAPI.
   167  func (c *ParserVAAAPIClient) ParseVaa(vaa *sdk.VAA) (any, error) {
   168  	endpointUrl := fmt.Sprintf("%s/vaas/parse", c.BaseURL)
   169  
   170  	vaaBytes, err := vaa.Marshal()
   171  	if err != nil {
   172  		return nil, errors.New("error marshalling vaa")
   173  	}
   174  
   175  	body := base64.StdEncoding.EncodeToString(vaaBytes)
   176  	response, err := c.Client.Post(endpointUrl, "text/plain", bytes.NewBuffer([]byte(body)))
   177  	if err != nil {
   178  		c.Logger.Error("error call parse vaa endpoint", zap.Error(err), zap.Uint16("chainID", uint16(vaa.EmitterChain)),
   179  			zap.String("address", vaa.EmitterAddress.String()), zap.Uint64("sequence", vaa.Sequence))
   180  		return nil, ErrCallEndpoint
   181  	}
   182  	defer response.Body.Close()
   183  
   184  	switch response.StatusCode {
   185  	case http.StatusCreated:
   186  		var parsedVAA any
   187  		json.NewDecoder(response.Body).Decode(&parsedVAA)
   188  		return &parsedVAA, nil
   189  	case http.StatusNotFound:
   190  		return nil, ErrNotFound
   191  	case http.StatusBadRequest:
   192  		return nil, ErrBadRequest
   193  	case http.StatusUnprocessableEntity:
   194  		return nil, ErrUnprocessableEntity
   195  	default:
   196  		return nil, ErrInternalError
   197  	}
   198  }