api.go 4.62 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
package api

import (
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/go-chi/chi"
)

// Database interface for deposits
type DepositDB interface {
	GetDeposits(limit int, cursor string, sortDirection string) ([]Deposit, string, bool, error)
}

// Database interface for withdrawals
type WithdrawalDB interface {
	GetWithdrawals(limit int, cursor string, sortDirection string, sortBy string) ([]Withdrawal, string, bool, error)
}

// Deposit data structure
23
// TODO this should be coming from the ORM instead
24 25 26 27 28 29 30 31 32 33 34 35 36
type Deposit struct {
	Guid            string        `json:"guid"`
	Amount          string        `json:"amount"`
	BlockNumber     int           `json:"blockNumber"`
	BlockTimestamp  time.Time     `json:"blockTimestamp"`
	From            string        `json:"from"`
	To              string        `json:"to"`
	TransactionHash string        `json:"transactionHash"`
	L1Token         TokenListItem `json:"l1Token"`
	L2Token         TokenListItem `json:"l2Token"`
}

// Withdrawal data structure
37
// TODO this should be coming from teh ORM instead
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
type Withdrawal struct {
	Guid            string        `json:"guid"`
	Amount          string        `json:"amount"`
	BlockNumber     int           `json:"blockNumber"`
	BlockTimestamp  time.Time     `json:"blockTimestamp"`
	From            string        `json:"from"`
	To              string        `json:"to"`
	TransactionHash string        `json:"transactionHash"`
	WithdrawalState string        `json:"withdrawalState"`
	Proof           *ProofClaim   `json:"proof"`
	Claim           *ProofClaim   `json:"claim"`
	L1Token         TokenListItem `json:"l1Token"`
	L2Token         TokenListItem `json:"l2Token"`
}

// TokenListItem data structure
type TokenListItem struct {
	ChainId    int        `json:"chainId"`
	Address    string     `json:"address"`
	Name       string     `json:"name"`
	Symbol     string     `json:"symbol"`
	Decimals   int        `json:"decimals"`
	LogoURI    string     `json:"logoURI"`
	Extensions Extensions `json:"extensions"`
}

// Extensions data structure
type Extensions struct {
	OptimismBridgeAddress string `json:"optimismBridgeAddress"`
	BridgeType            string `json:"bridgeType"`
}

// ProofClaim data structure
type ProofClaim struct {
	TransactionHash string    `json:"transactionHash"`
	BlockTimestamp  time.Time `json:"blockTimestamp"`
	BlockNumber     int       `json:"blockNumber"`
}

// PaginationResponse for paginated responses
type PaginationResponse struct {
	// TODO type this better
	Data        interface{} `json:"data"`
	Cursor      string      `json:"cursor"`
	HasNextPage bool        `json:"hasNextPage"`
}

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
func (a *Api) DepositsHandler(w http.ResponseWriter, r *http.Request) {

	limit := getIntFromQuery(r, "limit", 10)
	cursor := r.URL.Query().Get("cursor")
	sortDirection := r.URL.Query().Get("sortDirection")

	deposits, nextCursor, hasNextPage, err := a.DepositDB.GetDeposits(limit, cursor, sortDirection)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	response := PaginationResponse{
		Data:        deposits,
		Cursor:      nextCursor,
		HasNextPage: hasNextPage,
101
	}
102 103

	jsonResponse(w, response, http.StatusOK)
104 105
}

106 107 108 109 110 111 112 113 114 115
func (a *Api) WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
	limit := getIntFromQuery(r, "limit", 10)
	cursor := r.URL.Query().Get("cursor")
	sortDirection := r.URL.Query().Get("sortDirection")
	sortBy := r.URL.Query().Get("sortBy")

	withdrawals, nextCursor, hasNextPage, err := a.WithdrawalDB.GetWithdrawals(limit, cursor, sortDirection, sortBy)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
116
	}
117 118 119 120 121 122 123 124

	response := PaginationResponse{
		Data:        withdrawals,
		Cursor:      nextCursor,
		HasNextPage: hasNextPage,
	}

	jsonResponse(w, response, http.StatusOK)
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
}

func getIntFromQuery(r *http.Request, key string, defaultValue int) int {
	valueStr := r.URL.Query().Get(key)
	if valueStr == "" {
		return defaultValue
	}
	value, err := strconv.Atoi(valueStr)
	if err != nil {
		return defaultValue
	}
	return value
}

func jsonResponse(w http.ResponseWriter, data interface{}, statusCode int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(statusCode)
	json.NewEncoder(w).Encode(data)
}

type Api struct {
	Router       *chi.Mux
	DepositDB    DepositDB
	WithdrawalDB WithdrawalDB
}

func NewApi(depositDB DepositDB, withdrawalDB WithdrawalDB) *Api {
	r := chi.NewRouter()

154
	api := &Api{
155 156 157 158
		Router:       r,
		DepositDB:    depositDB,
		WithdrawalDB: withdrawalDB,
	}
159 160 161 162 163 164

	r.Get("/api/v0/deposits", api.DepositsHandler)
	r.Get("/api/v0/withdrawals", api.WithdrawalsHandler)

	return api

165 166 167 168 169
}

func (a *Api) Listen(port string) {
	http.ListenAndServe(port, a.Router)
}