// Package observations handle the request of VAA data from governor endpoint defined in the api. package vaa import ( "encoding/base64" "strconv" "github.com/gofiber/fiber/v2" "github.com/pkg/errors" "github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa" "github.com/wormhole-foundation/wormhole-explorer/api/middleware" "github.com/wormhole-foundation/wormhole-explorer/api/response" _ "github.com/wormhole-foundation/wormhole-explorer/api/response" // required by swaggo "go.uber.org/zap" ) // Controller definition. type Controller struct { srv *vaa.Service logger *zap.Logger } // NewController create a new controler. func NewController(serv *vaa.Service, logger *zap.Logger) *Controller { return &Controller{srv: serv, logger: logger.With(zap.String("module", "VaaController"))} } // FindAll godoc // @Description Returns all VAAs. Output is paginated and can also be be sorted. // @Tags wormholescan // @ID find-all-vaas // @Param page query integer false "Page number." // @Param pageSize query integer false "Number of elements per page." // @Param sortOrder query string false "Sort results in ascending or descending order." Enums(ASC, DESC) // @Param txHash query string false "Transaction hash of the VAA" // @Param parsedPayload query bool false "include the parsed contents of the VAA, if available" // @Param appId query string false "filter by application ID" // @Success 200 {object} response.Response[[]vaa.VaaDoc] // @Failure 400 // @Failure 500 // @Router /api/v1/vaas/ [get] func (c *Controller) FindAll(ctx *fiber.Ctx) error { pagination, err := middleware.ExtractPagination(ctx) if err != nil { return err } txHash, err := middleware.GetTxHash(ctx, c.logger) if err != nil { return err } includeParsedPayload, err := middleware.ExtractParsedPayload(ctx, c.logger) if err != nil { return err } appId := middleware.ExtractAppId(ctx, c.logger) if appId != "" { includeParsedPayload = true } p := vaa.FindAllParams{ Pagination: pagination, TxHash: txHash, IncludeParsedPayload: includeParsedPayload, AppId: appId, } vaas, err := c.srv.FindAll(ctx.Context(), &p) if err != nil { return err } return ctx.JSON(vaas) } // FindByChain godoc // @Description Returns all the VAAs generated in specific blockchain. // @Tags wormholescan // @ID find-vaas-by-chain // @Param chain_id path integer true "id of the blockchain" // @Param page query integer false "Page number." // @Param pageSize query integer false "Number of elements per page." // @Param sortOrder query string false "Sort results in ascending or descending order." Enums(ASC, DESC) // @Success 200 {object} response.Response[[]vaa.VaaDoc] // @Failure 400 // @Failure 500 // @Router /api/v1/vaas/{chain_id} [get] func (c *Controller) FindByChain(ctx *fiber.Ctx) error { p, err := middleware.ExtractPagination(ctx) if err != nil { return err } chainID, err := middleware.ExtractChainID(ctx, c.logger) if err != nil { return err } vaas, err := c.srv.FindByChain(ctx.Context(), chainID, p) if err != nil { return err } return ctx.JSON(vaas) } // FindByEmitter godoc // @Description Returns all all the VAAs generated by a specific emitter address. // @Tags wormholescan // @ID find-vaas-by-emitter // @Param chain_id path integer true "id of the blockchain" // @Param emitter path string true "address of the emitter" // @Param toChain query integer false "destination chain" // @Param page query integer false "Page number." // @Param pageSize query integer false "Number of elements per page." // @Param sortOrder query string false "Sort results in ascending or descending order." Enums(ASC, DESC) // @Success 200 {object} response.Response[[]vaa.VaaDoc] // @Failure 400 // @Failure 500 // @Router /api/v1/vaas/{chain_id}/{emitter} [get] func (c *Controller) FindByEmitter(ctx *fiber.Ctx) error { // Get query parameters pagination, err := middleware.ExtractPagination(ctx) if err != nil { return err } chainID, emitter, err := middleware.ExtractVAAChainIDEmitter(ctx, c.logger) if err != nil { return err } toChain, err := middleware.ExtractToChain(ctx, c.logger) if err != nil { return err } includeParsedPayload, err := middleware.ExtractParsedPayload(ctx, c.logger) if err != nil { return err } // Call the VAA service p := vaa.FindByEmitterParams{ EmitterChain: chainID, EmitterAddress: emitter, ToChain: toChain, IncludeParsedPayload: includeParsedPayload, Pagination: pagination, } vaas, err := c.srv.FindByEmitter(ctx.Context(), &p) if err != nil { return err } return ctx.JSON(vaas) } // FindById godoc // @Description Find a VAA by ID. // @Tags wormholescan // @ID find-vaa-by-id // @Param chain_id path integer true "id of the blockchain" // @Param emitter path string true "address of the emitter" // @Param seq path integer true "sequence of the VAA" // @Param parsedPayload query bool false "include the parsed contents of the VAA, if available" // @Success 200 {object} response.Response[[]vaa.VaaDoc] // @Failure 400 // @Failure 500 // @Router /api/v1/vaas/{chain_id}/{emitter}/{seq} [get] func (c *Controller) FindById(ctx *fiber.Ctx) error { chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger) if err != nil { return err } includeParsedPayload, err := middleware.ExtractParsedPayload(ctx, c.logger) if err != nil { return err } vaa, err := c.srv.FindById( ctx.Context(), chainID, emitter, strconv.FormatUint(seq, 10), includeParsedPayload, ) if err != nil { return err } return ctx.JSON(vaa) } // GetVaaCount godoc // @Description Returns the total number of VAAs emitted for each blockchain. // @Tags wormholescan // @ID get-vaa-counts // @Success 200 {object} response.Response[[]vaa.VaaStats] // @Failure 400 // @Failure 500 // @Router /api/v1/vaas/vaa-counts [get] func (c *Controller) GetVaaCount(ctx *fiber.Ctx) error { vaas, err := c.srv.GetVaaCount(ctx.Context()) if err != nil { return err } return ctx.JSON(vaas) } // ParseVaa godoc // @Description Parse a VAA. // @Tags wormholescan // @ID parse-vaa // @Success 200 {object} parser.ParseVaaWithStandarizedPropertiesdResponse // @Failure 400 // @Failure 404 // @Failure 500 // @Router /api/v1/vaas/parse [post] func (c *Controller) ParseVaa(ctx *fiber.Ctx) error { parseVaaBody := struct { Vaa string `json:"vaa"` }{} err := ctx.BodyParser(&parseVaaBody) if err != nil { return response.NewRequestBodyError(ctx, "invalid vaa request, unable to parse", errors.WithStack(err)) } if len(parseVaaBody.Vaa) == 0 { return response.NewRequestBodyError( ctx, "invalid vaa request, vaa is empty", nil) } vaa, err := base64.StdEncoding.DecodeString(parseVaaBody.Vaa) if err != nil { return response.NewRequestBodyError(ctx, "invalid vaa request, vaa is not base64 encoded", errors.WithStack(err)) } parsedVaa, err := c.srv.ParseVaa(ctx.Context(), vaa) if err != nil { return err } return ctx.JSON(parsedVaa) }