// Package governor handle the request of governor data from governor endpoint defined in the api. package governor import ( "strconv" "github.com/gofiber/fiber/v2" "github.com/wormhole-foundation/wormhole-explorer/api/handlers/governor" "github.com/wormhole-foundation/wormhole-explorer/api/middleware" _ "github.com/wormhole-foundation/wormhole-explorer/api/response" // needed by swaggo docs "github.com/wormhole-foundation/wormhole/sdk/vaa" "go.uber.org/zap" ) // Controller definition. type Controller struct { srv *governor.Service logger *zap.Logger } // NewController create a new controler. func NewController(serv *governor.Service, logger *zap.Logger) *Controller { return &Controller{srv: serv, logger: logger.With(zap.String("module", "GovernorController"))} } // AvailableNotionalResponse response compatible with grpc api. type AvailableNotionalResponse struct { Entries []*AvailableNotionalItemResponse `json:"entries"` } type AvailableNotionalItemResponse struct { ChainID vaa.ChainID `json:"chainId"` AvailableNotional string `json:"remainingAvailableNotional"` NotionalLimit string `json:"notionalLimit"` MaxTransactionSize string `json:"bigTransactionSize"` } // GetAvailNotionByChain godoc // @Description Get available notional by chainID // @Description Since from the wormhole-explorer point of view it is not a node, but has the information of all nodes, // @Description in order to build the endpoints it was assumed: // @Description There are N number of remainingAvailableNotional values in the GovernorConfig collection. N = number of guardians // @Description for a chainID. The smallest remainingAvailableNotional value for a chainID is used for the endpoint response. // @Tags Guardian // @ID governor-available-notional-by-chain // @Success 200 {object} AvailableNotionalResponse // @Failure 400 // @Failure 500 // @Router /v1/governor/available_notional_by_chain [get] func (c *Controller) GetAvailNotionByChain(ctx *fiber.Ctx) error { // call service to get available notional by chainID availableNotional, err := c.srv.GetAvailNotionByChain(ctx.Context()) if err != nil { return err } // build response compatible with node grpc api. entries := make([]*AvailableNotionalItemResponse, 0, len(availableNotional)) for _, v := range availableNotional { r := AvailableNotionalItemResponse{ ChainID: v.ChainID, AvailableNotional: v.AvailableNotional.String(), NotionalLimit: v.NotionalLimit.String(), MaxTransactionSize: v.MaxTransactionSize.String(), } entries = append(entries, &r) } response := AvailableNotionalResponse{ Entries: entries, } return ctx.JSON(response) } // EnqueuedVaaResponse response compatible with grpc api. type EnqueuedVaaResponse struct { Entries []*EnqueuedVaaItemResponse `json:"entries"` } type EnqueuedVaaItemResponse struct { EmitterChain vaa.ChainID `json:"emitterChain"` EmitterAddress string `json:"emitterAddress"` Sequence uint64 `json:"sequence"` ReleaseTime int64 `json:"releaseTime"` NotionalValue string `json:"notionalValue"` TxHash string `json:"txHash"` } // GetEnqueuedVaas godoc // @Description Get enqueued VAAs // @Tags Guardian // @ID guardians-enqueued-vaas // @Success 200 {object} EnqueuedVaaResponse // @Failure 400 // @Failure 500 // @Router /v1/governor/enqueued_vaas [get] func (c *Controller) GetEnqueuedVaas(ctx *fiber.Ctx) error { enqueuedVaa, err := c.srv.GetEnqueuedVaas(ctx.Context()) if err != nil { return err } // build response compatible with node grpc api. entries := make([]*EnqueuedVaaItemResponse, 0, len(enqueuedVaa)) for _, v := range enqueuedVaa { seqUint64, err := strconv.ParseUint(v.Sequence, 10, 64) if err != nil { return err } r := EnqueuedVaaItemResponse{ EmitterChain: v.EmitterChain, EmitterAddress: v.EmitterAddress, Sequence: seqUint64, ReleaseTime: v.ReleaseTime, NotionalValue: v.NotionalValue.String(), TxHash: v.TxHash, } entries = append(entries, &r) } response := EnqueuedVaaResponse{ Entries: entries, } return ctx.JSON(response) } // IsVaaEnqueued godoc // @Description Check if vaa is enqueued // @Tags Guardian // @ID guardians-is-vaa-enqueued // @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" // @Success 200 {object} EnqueuedVaaResponse // @Failure 400 // @Failure 500 // @Router /v1/governor/is_vaa_enqueued/{chain_id}/{emitter}/{seq} [get] func (c *Controller) IsVaaEnqueued(ctx *fiber.Ctx) error { chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger) if err != nil { return err } isEnqueued, err := c.srv.IsVaaEnqueued(ctx.Context(), chainID, emitter, strconv.FormatUint(seq, 10)) if err != nil { return err } // build reponse compatible with node grpc api. response := struct { IsEnqueued bool `json:"isEnqueued"` }{ IsEnqueued: isEnqueued, } return ctx.JSON(response) } // GetTokenList godoc // @Description Get token list // @Description Since from the wormhole-explorer point of view it is not a node, but has the information of all nodes, // @Description in order to build the endpoints it was assumed: // @Description For tokens with the same originChainId and originAddress and different price values for each node, // @Description the price that has most occurrences in all the nodes for an originChainId and originAddress is returned. // @Tags Guardian // @ID guardians-token-list // @Success 200 {object} []TokenList // @Failure 400 // @Failure 500 // @Router /v1/governor/token_list [get] func (c *Controller) GetTokenList(ctx *fiber.Ctx) error { tokenList, err := c.srv.GetTokenList(ctx.Context()) if err != nil { return err } // build reponse compatible with node grpc api. response := struct { Entries []*governor.TokenList `json:"entries"` }{ Entries: tokenList, } return ctx.JSON(response) }