forked from Cogwheel-Validator/spectra-gnoland-indexer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaddress.go
More file actions
154 lines (139 loc) · 4.2 KB
/
Copy pathaddress.go
File metadata and controls
154 lines (139 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
154
package handlers
import (
"context"
"time"
humatypes "github.com/Cogwheel-Validator/spectra-gnoland-indexer/api/huma-types"
"github.com/Cogwheel-Validator/spectra-gnoland-indexer/pkgs/database"
)
type AddressHandler struct {
db AddressDbHandler
chainName string
}
func NewAddressHandler(db AddressDbHandler, chainName string) *AddressHandler {
return &AddressHandler{db: db, chainName: chainName}
}
func (h *AddressHandler) GetDailyActiveAccount(
ctx context.Context,
input *humatypes.DailyActiveAccountGetInput,
) (*humatypes.DailyActiveAccountGetOutput, error) {
startDate := input.StartDate
endDate := input.EndDate
// validate input
if !startDate.Before(endDate.Time) {
return nil, badRequest("start_date must be before end_date")
}
if endDate.Sub(startDate.Time) > 24*time.Hour*30 {
return nil, badRequest("end_date must be within 30 days of start_date")
}
data, err := h.db.GetDailyActiveAccount(
ctx, h.chainName, startDate, endDate, input.SortOrder,
)
if err != nil {
return nil, mapDbError("GetDailyActiveAccount", "daily active account data not found", err)
}
if len(data) == 0 {
return nil, notFound("daily active account data not found")
}
return &humatypes.DailyActiveAccountGetOutput{Body: data}, nil
}
// GetAddressTxs returns transactions involving a given address. Two modes are
// supported:
//
// - Timestamp range: supply both from_timestamp and to_timestamp. sort_order
// controls the ordering; cursor/direction are ignored.
// - Cursor pagination: leave the timestamps empty. The response is always
// newest-first, and the caller walks history with direction=next (older
// rows) or direction=prev (newer rows, requires a cursor). NextCursor/
// PrevCursor are filled in so the caller can paginate in both directions.
func (h *AddressHandler) GetAddressTxs(
ctx context.Context,
input *humatypes.AddressGetInput,
) (*humatypes.AddressGetOutput, error) {
fromTs, toTs, err := parseTimestampPair(input.FromTimestamp, input.ToTimestamp)
if err != nil {
return nil, err
}
timestampMode := fromTs != nil
var limit *uint64
if input.Limit != 0 {
limit = &input.Limit
}
var cursor *string
if input.Cursor != "" {
cursor = &input.Cursor
}
direction := input.Direction
if direction == "" {
direction = database.Next
}
if !timestampMode {
if direction != database.Next && direction != database.Prev {
return nil, badRequest("invalid direction (must be 'next' or 'prev')")
}
if direction == database.Prev && cursor == nil {
return nil, badRequest("direction=prev requires a cursor")
}
}
addressTxs, hasMore, err := h.db.GetAddressTxs(
ctx, input.Address, h.chainName, fromTs, toTs, limit, cursor, direction,
)
if err != nil {
return nil, mapDbError("GetAddressTxs", "address not found", err)
}
body, err := buildAddressTxsBody(*addressTxs, hasMore, direction, cursor)
if err != nil {
return nil, err
}
return &humatypes.AddressGetOutput{Body: body}, nil
}
func parseTimestampPair(from, to time.Time) (*time.Time, *time.Time, error) {
var fromPtr, toPtr *time.Time
if !from.IsZero() {
fromPtr = &from
}
if !to.IsZero() {
toPtr = &to
}
if (fromPtr == nil) != (toPtr == nil) {
return nil, nil, badRequest("from_timestamp and to_timestamp must both be set or both be unset")
}
return fromPtr, toPtr, nil
}
func buildAddressTxsBody(
rows []database.AddressTx,
hasMore bool,
direction database.Direction,
cursor *string,
) (humatypes.AddressTxsBody, error) {
body := humatypes.AddressTxsBody{AddressTxs: rows}
if len(rows) == 0 {
return body, nil
}
newestCur, err := makeTxCursor(rows[0].BlockHeight, rows[0].Hash)
if err != nil {
return body, internalError("GetAddressTxs.makeTxCursor", err)
}
oldestCur, err := makeTxCursor(rows[len(rows)-1].BlockHeight, rows[len(rows)-1].Hash)
if err != nil {
return body, internalError("GetAddressTxs.makeTxCursor", err)
}
switch direction {
case database.Next:
body.HasNext = hasMore
if hasMore {
body.NextCursor = &oldestCur
}
if cursor != nil {
body.HasPrev = true
body.PrevCursor = &newestCur
}
case database.Prev:
body.HasPrev = hasMore
if hasMore {
body.PrevCursor = &newestCur
}
body.HasNext = true
body.NextCursor = &oldestCur
}
return body, nil
}