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 }