2022-11-23 04:06:29 -08:00
|
|
|
// package middleare contains all the middleware function to use in the API.
|
2022-11-17 07:37:29 -08:00
|
|
|
package middleware
|
|
|
|
|
|
|
|
import (
|
2022-11-23 04:06:29 -08:00
|
|
|
"fmt"
|
2023-03-07 11:25:42 -08:00
|
|
|
"regexp"
|
2022-11-17 07:37:29 -08:00
|
|
|
"strconv"
|
2023-03-07 11:25:42 -08:00
|
|
|
"strings"
|
|
|
|
"time"
|
2022-11-17 07:37:29 -08:00
|
|
|
|
|
|
|
"github.com/gofiber/fiber/v2"
|
2022-11-23 04:06:29 -08:00
|
|
|
"github.com/pkg/errors"
|
2023-05-10 13:39:18 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/transactions"
|
2022-11-23 04:06:29 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
2023-02-28 12:50:23 -08:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/api/types"
|
2023-03-21 12:12:23 -07:00
|
|
|
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
2022-11-23 04:06:29 -08:00
|
|
|
"go.uber.org/zap"
|
2022-11-17 07:37:29 -08:00
|
|
|
)
|
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
// ExtractChainID get chain parameter from route path.
|
2023-03-21 12:12:23 -07:00
|
|
|
func ExtractChainID(c *fiber.Ctx, l *zap.Logger) (sdk.ChainID, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-17 07:37:29 -08:00
|
|
|
chain, err := c.ParamsInt("chain")
|
|
|
|
if err != nil {
|
2022-11-23 04:06:29 -08:00
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
2023-02-09 09:28:39 -08:00
|
|
|
l.Error("failed to get chain parameter",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.Int("chain", chain),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
2022-11-23 04:06:29 -08:00
|
|
|
|
2023-03-21 12:12:23 -07:00
|
|
|
return sdk.ChainIDUnset, response.NewInvalidParamError(c, "WRONG CHAIN ID", errors.WithStack(err))
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-03-21 12:12:23 -07:00
|
|
|
return sdk.ChainID(chain), nil
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
|
|
|
|
2023-03-21 12:12:23 -07:00
|
|
|
// ExtractEmitterAddr parses the emitter address from the request path.
|
|
|
|
//
|
|
|
|
// When the parameter `chainIdHint` is not nil, this function will attempt to parse the
|
|
|
|
// native address format of the specified chain.
|
|
|
|
//
|
|
|
|
// The fallback behavior is to parse the address according to the Wormhole hex format.
|
|
|
|
func ExtractEmitterAddr(c *fiber.Ctx, l *zap.Logger, chainIdHint *sdk.ChainID) (*types.Address, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-17 07:37:29 -08:00
|
|
|
emitterStr := c.Params("emitter")
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-04-12 11:51:33 -07:00
|
|
|
// Decide whether to accept the Solana address format based on the context
|
|
|
|
var acceptSolanaFormat bool
|
2023-03-21 12:12:23 -07:00
|
|
|
if chainIdHint != nil && *chainIdHint == sdk.ChainIDSolana {
|
2023-04-12 11:51:33 -07:00
|
|
|
acceptSolanaFormat = true
|
|
|
|
}
|
2023-03-21 12:12:23 -07:00
|
|
|
|
2023-04-12 11:51:33 -07:00
|
|
|
// Attempt to parse the address
|
|
|
|
emitter, err := types.StringToAddress(emitterStr, acceptSolanaFormat)
|
2022-11-17 07:37:29 -08:00
|
|
|
if err != nil {
|
2022-11-23 04:06:29 -08:00
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
2023-03-21 12:12:23 -07:00
|
|
|
l.Error("failed to convert emitter to wormhole address",
|
2023-02-09 09:28:39 -08:00
|
|
|
zap.Error(err),
|
|
|
|
zap.String("emitterStr", emitterStr),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
2022-11-23 04:06:29 -08:00
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED EMITTER_ADDR", errors.WithStack(err))
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-02-28 12:50:23 -08:00
|
|
|
return emitter, nil
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
// ExtractSequence get sequence parameter from route path.
|
|
|
|
func ExtractSequence(c *fiber.Ctx, l *zap.Logger) (uint64, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-17 07:37:29 -08:00
|
|
|
sequence := c.Params("sequence")
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-17 07:37:29 -08:00
|
|
|
seq, err := strconv.ParseUint(sequence, 10, 64)
|
|
|
|
if err != nil {
|
2022-11-23 04:06:29 -08:00
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
2023-02-09 09:28:39 -08:00
|
|
|
l.Error("failed to get sequence parameter",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.String("sequence", sequence),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
2022-11-23 04:06:29 -08:00
|
|
|
return 0, response.NewInvalidParamError(c, "MALFORMED SEQUENCE NUMBER", errors.WithStack(err))
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-17 07:37:29 -08:00
|
|
|
return seq, nil
|
|
|
|
}
|
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
// ExtractGuardianAddress get guardian address from route path.
|
2023-02-28 12:50:23 -08:00
|
|
|
func ExtractGuardianAddress(c *fiber.Ctx, l *zap.Logger) (*types.Address, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
|
|
|
// read the address from query params
|
|
|
|
tmp := c.Params("guardian_address")
|
|
|
|
if tmp == "" {
|
2023-02-28 12:50:23 -08:00
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED GUARDIAN ADDR", nil)
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-04-12 11:51:33 -07:00
|
|
|
// Attempt to parse the address
|
|
|
|
guardianAddress, err := types.StringToAddress(tmp, false /*acceptSolanaFormat*/)
|
2023-02-09 09:28:39 -08:00
|
|
|
if err != nil {
|
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
|
|
|
l.Error("failed to decode guardian address",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
2023-02-28 12:50:23 -08:00
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED GUARDIAN ADDR", errors.WithStack(err))
|
2023-02-09 09:28:39 -08:00
|
|
|
}
|
|
|
|
|
2023-02-28 12:50:23 -08:00
|
|
|
return guardianAddress, nil
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
// ExtractVAAParams get VAA chain, address from route path.
|
2023-03-21 12:12:23 -07:00
|
|
|
func ExtractVAAChainIDEmitter(c *fiber.Ctx, l *zap.Logger) (sdk.ChainID, *types.Address, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
chainID, err := ExtractChainID(c, l)
|
|
|
|
if err != nil {
|
2023-03-21 12:12:23 -07:00
|
|
|
return sdk.ChainIDUnset, nil, err
|
2022-11-23 04:06:29 -08:00
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-03-21 12:12:23 -07:00
|
|
|
address, err := ExtractEmitterAddr(c, l, &chainID)
|
2022-11-23 04:06:29 -08:00
|
|
|
if err != nil {
|
|
|
|
return chainID, nil, err
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
return chainID, address, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtractVAAParams get VAAA chain, address and sequence from route path.
|
2023-03-21 12:12:23 -07:00
|
|
|
func ExtractVAAParams(c *fiber.Ctx, l *zap.Logger) (sdk.ChainID, *types.Address, uint64, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
chainID, err := ExtractChainID(c, l)
|
2022-11-17 07:37:29 -08:00
|
|
|
if err != nil {
|
2023-03-21 12:12:23 -07:00
|
|
|
return sdk.ChainIDUnset, nil, 0, err
|
2022-11-17 07:37:29 -08:00
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-03-21 12:12:23 -07:00
|
|
|
address, err := ExtractEmitterAddr(c, l, &chainID)
|
2022-11-17 07:37:29 -08:00
|
|
|
if err != nil {
|
|
|
|
return chainID, nil, 0, err
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
seq, err := ExtractSequence(c, l)
|
2022-11-17 07:37:29 -08:00
|
|
|
if err != nil {
|
|
|
|
return chainID, address, 0, err
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-17 07:37:29 -08:00
|
|
|
return chainID, address, seq, nil
|
|
|
|
}
|
2022-11-23 04:06:29 -08:00
|
|
|
|
|
|
|
// ExtractObservationSigner get signer from route path.
|
2023-03-21 12:12:23 -07:00
|
|
|
func ExtractObservationSigner(c *fiber.Ctx, l *zap.Logger) (*sdk.Address, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
signer := c.Params("signer")
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-03-21 12:12:23 -07:00
|
|
|
signerAddr, err := sdk.StringToAddress(signer)
|
2022-11-23 04:06:29 -08:00
|
|
|
if err != nil {
|
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
2023-02-09 09:28:39 -08:00
|
|
|
l.Error("failed to covert signer to address",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.String("signer", signer),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
2022-11-23 04:06:29 -08:00
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED SIGNER", errors.WithStack(err))
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
return &signerAddr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtractObservationHash get a hash from route path.
|
|
|
|
func ExtractObservationHash(c *fiber.Ctx, l *zap.Logger) (string, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
hash := c.Params("hash")
|
|
|
|
if hash == "" {
|
|
|
|
return "", response.NewInvalidParamError(c, "MALFORMED HASH", nil)
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2022-11-23 04:06:29 -08:00
|
|
|
return hash, nil
|
|
|
|
}
|
2023-01-26 06:54:41 -08:00
|
|
|
|
Add endpoint `GET /api/v1/transactions` (#388)
### Summary
Tracking issue: https://github.com/wormhole-foundation/wormhole-explorer/issues/385
This pull request implements a new endpoint, `GET /api/v1/transactions`, which will be consumed by the wormhole explorer UI.
The endpoint returns a paginated list of transactions, in which each element contains a brief overview of the transaction (ID, txHash, status, etc.).
It exposes offset-based pagination via the parameters `page` and `pageSize`. Also, results can be obtained for a specific address by using the `address` query parameter.
The response model looks like this:
```json
{
"transactions": [
{
"id": "1/5ec18c34b47c63d17ab43b07b9b2319ea5ee2d163bce2e467000174e238c8e7f/12965",
"timestamp": "2023-06-08T19:30:19Z",
"txHash": "a302c4ab2d6b9a6003951d2e91f8fdbb83cfa20f6ffb588b95ef0290aab37066",
"originChain": 1,
"status": "ongoing"
},
{
"id": "22/0000000000000000000000000000000000000000000000000000000000000001/18308",
"timestamp": "2023-06-08T19:17:14Z",
"txHash": "00000000000000000000000000000000000000000000000000000000000047e7",
"originChain": 22,
"destinationAddress": "0x00000000000000000000000067e8a40816a983fbe3294aaebd0cc2391815b86b",
"destinationChain": 5,
"tokenAmount": "0.12",
"usdAmount": "0.12012",
"symbol": "USDC",
"status": "completed"
},
...
]
}
```
### Limitations of the current implementation
1. Doesn't return the total number of results (this may result in a performance issue when we filter by address)
2. Can only filter by receiver address (we don't have sender information in the database yet)
2023-06-12 07:43:48 -07:00
|
|
|
// ExtractAddressFromQueryParams parses the `address` parameter from the query string.
|
|
|
|
//
|
|
|
|
// If the parameter doesn't exist, the function returns a nil address without errors.
|
|
|
|
func ExtractAddressFromQueryParams(c *fiber.Ctx, l *zap.Logger) (*types.Address, error) {
|
|
|
|
|
|
|
|
val := c.Query("address")
|
|
|
|
|
|
|
|
if val == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to parse the address
|
|
|
|
addr, err := types.StringToAddress(val, true /*acceptSolanaFormat*/)
|
|
|
|
if err != nil {
|
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
|
|
|
l.Error("failed to decode address",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED ADDR", errors.WithStack(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtractAddressFromPath parses the `id` parameter from the route path.
|
|
|
|
func ExtractAddressFromPath(c *fiber.Ctx, l *zap.Logger) (*types.Address, error) {
|
2023-04-12 11:51:33 -07:00
|
|
|
|
|
|
|
val := c.Params("id")
|
|
|
|
|
|
|
|
// Attempt to parse the address
|
|
|
|
addr, err := types.StringToAddress(val, true /*acceptSolanaFormat*/)
|
|
|
|
if err != nil {
|
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
|
|
|
l.Error("failed to decode address",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED ADDR", errors.WithStack(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
2023-04-05 06:33:28 -07:00
|
|
|
// GetTxHash parses the `txHash` parameter from query params.
|
|
|
|
func GetTxHash(c *fiber.Ctx, l *zap.Logger) (*types.TxHash, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-04-05 06:33:28 -07:00
|
|
|
value := c.Query("txHash")
|
|
|
|
if value == "" {
|
2023-01-26 06:54:41 -08:00
|
|
|
return nil, nil
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-04-05 06:33:28 -07:00
|
|
|
txHash, err := types.ParseTxHash(value)
|
2023-01-26 06:54:41 -08:00
|
|
|
if err != nil {
|
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
2023-04-05 06:33:28 -07:00
|
|
|
l.Error("failed to parse txHash",
|
2023-02-09 09:28:39 -08:00
|
|
|
zap.Error(err),
|
2023-04-05 06:33:28 -07:00
|
|
|
zap.String("txHash", value),
|
2023-02-09 09:28:39 -08:00
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
2023-01-26 06:54:41 -08:00
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED TX HASH", errors.WithStack(err))
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-04-05 06:33:28 -07:00
|
|
|
return txHash, nil
|
2023-01-26 06:54:41 -08:00
|
|
|
}
|
2023-01-27 08:47:17 -08:00
|
|
|
|
|
|
|
// ExtractParsedPayload get parsedPayload query parameter.
|
|
|
|
func ExtractParsedPayload(c *fiber.Ctx, l *zap.Logger) (bool, error) {
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-01-27 08:47:17 -08:00
|
|
|
parsedPayloadStr := c.Query("parsedPayload", "false")
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-01-27 08:47:17 -08:00
|
|
|
parsedPayload, err := strconv.ParseBool(parsedPayloadStr)
|
|
|
|
if err != nil {
|
|
|
|
return false, response.NewInvalidQueryParamError(c, "INVALID <parsedPayload> QUERY PARAMETER", errors.WithStack(err))
|
|
|
|
}
|
2023-02-09 09:28:39 -08:00
|
|
|
|
2023-01-27 08:47:17 -08:00
|
|
|
return parsedPayload, nil
|
|
|
|
}
|
2023-02-01 04:59:51 -08:00
|
|
|
|
|
|
|
func ExtractAppId(c *fiber.Ctx, l *zap.Logger) string {
|
|
|
|
return c.Query("appId")
|
|
|
|
}
|
2023-03-07 11:25:42 -08:00
|
|
|
|
|
|
|
func ExtractTimeSpan(c *fiber.Ctx, l *zap.Logger) (string, error) {
|
|
|
|
// get the timeSpan from query params
|
2023-05-03 10:01:55 -07:00
|
|
|
timeSpanStr := c.Query("timeSpan", "1d")
|
2023-03-07 11:25:42 -08:00
|
|
|
|
|
|
|
// validate the timeSpan
|
|
|
|
if !isValidTimeSpan(timeSpanStr) {
|
|
|
|
return "", response.NewInvalidQueryParamError(c, "INVALID <timeSpan> QUERY PARAMETER", nil)
|
|
|
|
}
|
|
|
|
return timeSpanStr, nil
|
|
|
|
}
|
|
|
|
|
2023-05-03 10:01:55 -07:00
|
|
|
// isValidTimeSpan check that the timeSpan is valid.
|
2023-03-07 11:25:42 -08:00
|
|
|
func isValidTimeSpan(timeSpan string) bool {
|
2023-05-03 10:01:55 -07:00
|
|
|
return regexp.MustCompile(`^1d$|^1w$|^1mo$`).MatchString(timeSpan)
|
2023-03-07 11:25:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func ExtractSampleRate(c *fiber.Ctx, l *zap.Logger) (string, error) {
|
|
|
|
// get the sampleRate from query params
|
2023-05-03 10:01:55 -07:00
|
|
|
sampleRateStr := c.Query("sampleRate", "1h")
|
|
|
|
|
2023-03-07 11:25:42 -08:00
|
|
|
// validate the sampleRate
|
|
|
|
if !isValidSampleRate(sampleRateStr) {
|
|
|
|
return "", response.NewInvalidQueryParamError(c, "INVALID <sampleRate> QUERY PARAMETER", nil)
|
|
|
|
}
|
|
|
|
return sampleRateStr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func isValidSampleRate(sampleRate string) bool {
|
2023-05-03 10:01:55 -07:00
|
|
|
return regexp.MustCompile(`^1h$|^1d$`).MatchString(sampleRate)
|
2023-03-07 11:25:42 -08:00
|
|
|
}
|
|
|
|
|
2023-05-15 12:59:54 -07:00
|
|
|
func ExtractTimeSpanAndSampleRate(c *fiber.Ctx, l *zap.Logger) (string, string, error) {
|
|
|
|
timeSpan, err := ExtractTimeSpan(c, l)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
sampleRate, err := ExtractSampleRate(c, l)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch timeSpan {
|
|
|
|
case "1d":
|
|
|
|
if sampleRate != "1h" {
|
|
|
|
return "", "", response.NewInvalidQueryParamError(c, "INVALID CONFIGURATION <timeSpan>, <sampleRate> QUERY PARAMETERS.", nil)
|
|
|
|
}
|
|
|
|
case "1w":
|
|
|
|
if sampleRate != "1d" {
|
|
|
|
return "", "", response.NewInvalidQueryParamError(c, "INVALID CONFIGURATION <timeSpan>, <sampleRate> QUERY PARAMETERS", nil)
|
|
|
|
}
|
|
|
|
case "1mo":
|
|
|
|
if sampleRate != "1d" {
|
|
|
|
return "", "", response.NewInvalidQueryParamError(c, "INVALID CONFIGURATION <timeSpan>, <sampleRate> QUERY PARAMETERS", nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return timeSpan, sampleRate, nil
|
|
|
|
}
|
|
|
|
|
2023-03-07 11:25:42 -08:00
|
|
|
func ExtractTime(c *fiber.Ctx, queryParam string) (*time.Time, error) {
|
|
|
|
// get the start_time from query params
|
|
|
|
date := c.Query(queryParam, "")
|
|
|
|
if date == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
t, err := time.Parse("20060102T150405Z", date)
|
|
|
|
if err != nil {
|
|
|
|
return nil, response.NewInvalidQueryParamError(c, fmt.Sprintf("INVALID <%s> QUERY PARAMETER", queryParam), nil)
|
|
|
|
}
|
|
|
|
return &t, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExtractApps(ctx *fiber.Ctx) ([]string, error) {
|
|
|
|
apps := ctx.Query("apps")
|
|
|
|
if apps == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return strings.Split(apps, ","), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExtractIsNotional(ctx *fiber.Ctx) (bool, error) {
|
|
|
|
by := ctx.Query("by")
|
|
|
|
if by == "" {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
if by == "notional" {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
if by == "tx" {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, response.NewInvalidQueryParamError(ctx, "INVALID <by> QUERY PARAMETER", nil)
|
|
|
|
}
|
2023-04-04 06:49:27 -07:00
|
|
|
|
2023-06-20 06:34:20 -07:00
|
|
|
func ExtractChainActivityTimeSpan(ctx *fiber.Ctx) (transactions.ChainActivityTimeSpan, error) {
|
|
|
|
s := ctx.Query("timeSpan", string(transactions.ChainActivityTs7Days))
|
|
|
|
timeSpan, err := transactions.ParseChainActivityTimeSpan(s)
|
|
|
|
if err != nil {
|
|
|
|
return "", response.NewInvalidQueryParamError(ctx, "INVALID <timeSpan> QUERY PARAMETER", nil)
|
|
|
|
}
|
|
|
|
return timeSpan, nil
|
|
|
|
}
|
|
|
|
|
2023-05-12 09:05:18 -07:00
|
|
|
// ExtractTopStatisticsTimeSpan parses the `timespan` parameter used on top statistics endpoints.
|
|
|
|
//
|
|
|
|
// The endpoints that accept this parameter are:
|
|
|
|
// * `GET /api/v1/top-assets-by-volume`
|
|
|
|
// * `GET /api/v1/top-chain-pairs-by-num-transfers`
|
|
|
|
func ExtractTopStatisticsTimeSpan(ctx *fiber.Ctx) (*transactions.TopStatisticsTimeSpan, error) {
|
2023-05-10 13:39:18 -07:00
|
|
|
|
2023-05-12 09:05:18 -07:00
|
|
|
s := ctx.Query("timeSpan")
|
|
|
|
timeSpan, err := transactions.ParseTopStatisticsTimeSpan(s)
|
2023-05-10 13:39:18 -07:00
|
|
|
if err != nil {
|
2023-05-12 09:05:18 -07:00
|
|
|
return nil, response.NewInvalidQueryParamError(ctx, "INVALID <timeSpan> QUERY PARAMETER", nil)
|
2023-05-10 13:39:18 -07:00
|
|
|
}
|
|
|
|
|
2023-05-12 09:05:18 -07:00
|
|
|
return timeSpan, nil
|
2023-05-10 13:39:18 -07:00
|
|
|
}
|
|
|
|
|
2023-05-31 06:29:16 -07:00
|
|
|
// ExtractTokenAddress get token address from route path.
|
|
|
|
func ExtractTokenAddress(c *fiber.Ctx, l *zap.Logger) (*types.Address, error) {
|
|
|
|
strTokenAddress := c.Params("token_address")
|
|
|
|
tokenAddress, err := types.StringToAddress(strTokenAddress, true)
|
|
|
|
if err != nil {
|
|
|
|
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
|
|
|
l.Error("failed to convert string to address",
|
|
|
|
zap.Error(err),
|
|
|
|
zap.String("token_address", strTokenAddress),
|
|
|
|
zap.String("requestID", requestID),
|
|
|
|
)
|
|
|
|
return nil, response.NewInvalidParamError(c, "MALFORMED TOKEN_ADDRESS", errors.WithStack(err))
|
|
|
|
}
|
|
|
|
return tokenAddress, nil
|
|
|
|
}
|