mirror of
https://github.com/alexgo-io/gaze-indexer.git
synced 2026-01-12 16:53:08 +08:00
2
.github/workflows/sqlc-verify.yml
vendored
2
.github/workflows/sqlc-verify.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Setup Sqlc
|
- name: Setup Sqlc
|
||||||
uses: sqlc-dev/setup-sqlc@v4
|
uses: sqlc-dev/setup-sqlc@v4
|
||||||
with:
|
with:
|
||||||
sqlc-version: "1.26.0"
|
sqlc-version: "1.27.0"
|
||||||
|
|
||||||
- name: Check Diff
|
- name: Check Diff
|
||||||
run: sqlc diff
|
run: sqlc diff
|
||||||
|
|||||||
@@ -101,3 +101,6 @@ linters-settings:
|
|||||||
attr-only: true
|
attr-only: true
|
||||||
key-naming-case: snake
|
key-naming-case: snake
|
||||||
args-on-sep-lines: true
|
args-on-sep-lines: true
|
||||||
|
gosec:
|
||||||
|
excludes:
|
||||||
|
- G115
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: blocks.sql
|
// source: blocks.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: events.sql
|
// source: events.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: nodes.sql
|
// source: nodes.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: nodesales.sql
|
// source: nodesales.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: test.sql
|
// source: test.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package httphandler
|
package httphandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/gaze-network/indexer-network/common/errs"
|
"github.com/gaze-network/indexer-network/common/errs"
|
||||||
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
|
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
|
||||||
@@ -11,11 +13,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type getBalancesRequest struct {
|
type getBalancesRequest struct {
|
||||||
|
paginationRequest
|
||||||
Wallet string `params:"wallet"`
|
Wallet string `params:"wallet"`
|
||||||
Id string `query:"id"`
|
Id string `query:"id"`
|
||||||
BlockHeight uint64 `query:"blockHeight"`
|
BlockHeight uint64 `query:"blockHeight"`
|
||||||
Limit int32 `query:"limit"`
|
|
||||||
Offset int32 `query:"offset"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -23,13 +24,20 @@ const (
|
|||||||
getBalancesDefaultLimit = 100
|
getBalancesDefaultLimit = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r getBalancesRequest) Validate() error {
|
func (r *getBalancesRequest) Validate() error {
|
||||||
var errList []error
|
var errList []error
|
||||||
if r.Wallet == "" {
|
if r.Wallet == "" {
|
||||||
errList = append(errList, errors.New("'wallet' is required"))
|
errList = append(errList, errors.New("'wallet' is required"))
|
||||||
}
|
}
|
||||||
if r.Id != "" && !isRuneIdOrRuneName(r.Id) {
|
if r.Id != "" {
|
||||||
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
|
id, err := url.QueryUnescape(r.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
r.Id = id
|
||||||
|
if !isRuneIdOrRuneName(r.Id) {
|
||||||
|
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if r.Limit < 0 {
|
if r.Limit < 0 {
|
||||||
errList = append(errList, errors.New("'limit' must be non-negative"))
|
errList = append(errList, errors.New("'limit' must be non-negative"))
|
||||||
@@ -66,8 +74,8 @@ func (h *HttpHandler) GetBalances(ctx *fiber.Ctx) (err error) {
|
|||||||
if err := req.Validate(); err != nil {
|
if err := req.Validate(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
if req.Limit == 0 {
|
if err := req.ParseDefault(); err != nil {
|
||||||
req.Limit = getBalancesDefaultLimit
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pkScript, ok := resolvePkScript(h.network, req.Wallet)
|
pkScript, ok := resolvePkScript(h.network, req.Wallet)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (r getBalancesBatchRequest) Validate() error {
|
|||||||
errList = append(errList, errors.Errorf("queries[%d]: 'wallet' is required", i))
|
errList = append(errList, errors.Errorf("queries[%d]: 'wallet' is required", i))
|
||||||
}
|
}
|
||||||
if query.Id != "" && !isRuneIdOrRuneName(query.Id) {
|
if query.Id != "" && !isRuneIdOrRuneName(query.Id) {
|
||||||
errList = append(errList, errors.Errorf("queries[%d]: 'id' is not valid rune id or rune name", i))
|
errList = append(errList, errors.Errorf("queries[%d]: id '%s' is not valid rune id or rune name", i, query.Id))
|
||||||
}
|
}
|
||||||
if query.Limit < 0 {
|
if query.Limit < 0 {
|
||||||
errList = append(errList, errors.Errorf("queries[%d]: 'limit' must be non-negative", i))
|
errList = append(errList, errors.Errorf("queries[%d]: 'limit' must be non-negative", i))
|
||||||
@@ -89,7 +89,7 @@ func (h *HttpHandler) GetBalancesBatch(ctx *fiber.Ctx) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if query.Limit == 0 {
|
if query.Limit == 0 {
|
||||||
query.Limit = getBalancesMaxLimit
|
query.Limit = getBalancesDefaultLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
balances, err := h.usecase.GetBalancesByPkScript(ctx, pkScript, blockHeight, query.Limit, query.Offset)
|
balances, err := h.usecase.GetBalancesByPkScript(ctx, pkScript, blockHeight, query.Limit, query.Offset)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package httphandler
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
@@ -15,21 +16,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type getHoldersRequest struct {
|
type getHoldersRequest struct {
|
||||||
|
paginationRequest
|
||||||
Id string `params:"id"`
|
Id string `params:"id"`
|
||||||
BlockHeight uint64 `query:"blockHeight"`
|
BlockHeight uint64 `query:"blockHeight"`
|
||||||
Limit int32 `json:"limit"`
|
|
||||||
Offset int32 `json:"offset"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
getHoldersMaxLimit = 1000
|
getHoldersMaxLimit = 1000
|
||||||
getHoldersDefaultLimit = 100
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r getHoldersRequest) Validate() error {
|
func (r *getHoldersRequest) Validate() error {
|
||||||
var errList []error
|
var errList []error
|
||||||
|
id, err := url.QueryUnescape(r.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
r.Id = id
|
||||||
if !isRuneIdOrRuneName(r.Id) {
|
if !isRuneIdOrRuneName(r.Id) {
|
||||||
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
|
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
|
||||||
}
|
}
|
||||||
if r.Limit < 0 {
|
if r.Limit < 0 {
|
||||||
errList = append(errList, errors.New("'limit' must be non-negative"))
|
errList = append(errList, errors.New("'limit' must be non-negative"))
|
||||||
@@ -68,6 +72,9 @@ func (h *HttpHandler) GetHolders(ctx *fiber.Ctx) (err error) {
|
|||||||
if err := req.Validate(); err != nil {
|
if err := req.Validate(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
if err := req.ParseDefault(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
blockHeight := req.BlockHeight
|
blockHeight := req.BlockHeight
|
||||||
if blockHeight == 0 {
|
if blockHeight == 0 {
|
||||||
@@ -78,10 +85,6 @@ func (h *HttpHandler) GetHolders(ctx *fiber.Ctx) (err error) {
|
|||||||
blockHeight = uint64(blockHeader.Height)
|
blockHeight = uint64(blockHeader.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Limit == 0 {
|
|
||||||
req.Limit = getHoldersDefaultLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
var runeId runes.RuneId
|
var runeId runes.RuneId
|
||||||
if req.Id != "" {
|
if req.Id != "" {
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package httphandler
|
package httphandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
@@ -17,10 +18,15 @@ type getTokenInfoRequest struct {
|
|||||||
BlockHeight uint64 `query:"blockHeight"`
|
BlockHeight uint64 `query:"blockHeight"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r getTokenInfoRequest) Validate() error {
|
func (r *getTokenInfoRequest) Validate() error {
|
||||||
var errList []error
|
var errList []error
|
||||||
|
id, err := url.QueryUnescape(r.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
r.Id = id
|
||||||
if !isRuneIdOrRuneName(r.Id) {
|
if !isRuneIdOrRuneName(r.Id) {
|
||||||
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
|
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
|
||||||
}
|
}
|
||||||
return errs.WithPublicMessage(errors.Join(errList...), "validation error")
|
return errs.WithPublicMessage(errors.Join(errList...), "validation error")
|
||||||
}
|
}
|
||||||
@@ -57,9 +63,9 @@ type getTokenInfoResult struct {
|
|||||||
MintedAmount uint128.Uint128 `json:"mintedAmount"`
|
MintedAmount uint128.Uint128 `json:"mintedAmount"`
|
||||||
BurnedAmount uint128.Uint128 `json:"burnedAmount"`
|
BurnedAmount uint128.Uint128 `json:"burnedAmount"`
|
||||||
Decimals uint8 `json:"decimals"`
|
Decimals uint8 `json:"decimals"`
|
||||||
DeployedAt uint64 `json:"deployedAt"` // unix timestamp
|
DeployedAt int64 `json:"deployedAt"` // unix timestamp
|
||||||
DeployedAtHeight uint64 `json:"deployedAtHeight"`
|
DeployedAtHeight uint64 `json:"deployedAtHeight"`
|
||||||
CompletedAt *uint64 `json:"completedAt"` // unix timestamp
|
CompletedAt *int64 `json:"completedAt"` // unix timestamp
|
||||||
CompletedAtHeight *uint64 `json:"completedAtHeight"`
|
CompletedAtHeight *uint64 `json:"completedAtHeight"`
|
||||||
HoldersCount int `json:"holdersCount"`
|
HoldersCount int `json:"holdersCount"`
|
||||||
Extend tokenInfoExtend `json:"extend"`
|
Extend tokenInfoExtend `json:"extend"`
|
||||||
@@ -144,9 +150,9 @@ func (h *HttpHandler) GetTokenInfo(ctx *fiber.Ctx) (err error) {
|
|||||||
MintedAmount: mintedAmount,
|
MintedAmount: mintedAmount,
|
||||||
BurnedAmount: runeEntry.BurnedAmount,
|
BurnedAmount: runeEntry.BurnedAmount,
|
||||||
Decimals: runeEntry.Divisibility,
|
Decimals: runeEntry.Divisibility,
|
||||||
DeployedAt: uint64(runeEntry.EtchedAt.Unix()),
|
DeployedAt: runeEntry.EtchedAt.Unix(),
|
||||||
DeployedAtHeight: runeEntry.EtchingBlock,
|
DeployedAtHeight: runeEntry.EtchingBlock,
|
||||||
CompletedAt: lo.Ternary(runeEntry.CompletedAt.IsZero(), nil, lo.ToPtr(uint64(runeEntry.CompletedAt.Unix()))),
|
CompletedAt: lo.Ternary(runeEntry.CompletedAt.IsZero(), nil, lo.ToPtr(runeEntry.CompletedAt.Unix())),
|
||||||
CompletedAtHeight: runeEntry.CompletedAtHeight,
|
CompletedAtHeight: runeEntry.CompletedAtHeight,
|
||||||
HoldersCount: len(holdingBalances),
|
HoldersCount: len(holdingBalances),
|
||||||
Extend: tokenInfoExtend{
|
Extend: tokenInfoExtend{
|
||||||
|
|||||||
172
modules/runes/api/httphandler/get_tokens.go
Normal file
172
modules/runes/api/httphandler/get_tokens.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package httphandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors"
|
||||||
|
"github.com/gaze-network/indexer-network/common/errs"
|
||||||
|
"github.com/gaze-network/indexer-network/modules/runes/runes"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTokensMaxLimit = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetTokensScope string
|
||||||
|
|
||||||
|
const (
|
||||||
|
GetTokensScopeAll GetTokensScope = "all"
|
||||||
|
GetTokensScopeOngoing GetTokensScope = "ongoing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s GetTokensScope) IsValid() bool {
|
||||||
|
switch s {
|
||||||
|
case GetTokensScopeAll, GetTokensScopeOngoing:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type getTokensRequest struct {
|
||||||
|
paginationRequest
|
||||||
|
Search string `query:"search"`
|
||||||
|
BlockHeight uint64 `query:"blockHeight"`
|
||||||
|
Scope GetTokensScope `query:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req getTokensRequest) Validate() error {
|
||||||
|
var errList []error
|
||||||
|
if err := req.paginationRequest.Validate(); err != nil {
|
||||||
|
errList = append(errList, err)
|
||||||
|
}
|
||||||
|
if req.Limit > getTokensMaxLimit {
|
||||||
|
errList = append(errList, errors.Errorf("limit must be less than or equal to 1000"))
|
||||||
|
}
|
||||||
|
if req.Scope != "" && !req.Scope.IsValid() {
|
||||||
|
errList = append(errList, errors.Errorf("invalid scope: %s", req.Scope))
|
||||||
|
}
|
||||||
|
return errs.WithPublicMessage(errors.Join(errList...), "validation error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *getTokensRequest) ParseDefault() error {
|
||||||
|
if err := req.paginationRequest.ParseDefault(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if req.Scope == "" {
|
||||||
|
req.Scope = GetTokensScopeAll
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type getTokensResult struct {
|
||||||
|
List []getTokenInfoResult `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type getTokensResponse = HttpResponse[getTokensResult]
|
||||||
|
|
||||||
|
func (h *HttpHandler) GetTokens(ctx *fiber.Ctx) (err error) {
|
||||||
|
var req getTokensRequest
|
||||||
|
if err := ctx.QueryParser(&req); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if err := req.Validate(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if err := req.ParseDefault(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockHeight := req.BlockHeight
|
||||||
|
if blockHeight == 0 {
|
||||||
|
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errs.NotFound) {
|
||||||
|
return errs.NewPublicError("latest block not found")
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "error during GetLatestBlock")
|
||||||
|
}
|
||||||
|
blockHeight = uint64(blockHeader.Height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove spacers
|
||||||
|
search := strings.Replace(strings.Replace(req.Search, "•", "", -1), ".", "", -1)
|
||||||
|
|
||||||
|
var entries []*runes.RuneEntry
|
||||||
|
switch req.Scope {
|
||||||
|
case GetTokensScopeAll:
|
||||||
|
entries, err = h.usecase.GetRuneEntries(ctx.UserContext(), search, blockHeight, req.Limit, req.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error during GetRuneEntryList")
|
||||||
|
}
|
||||||
|
case GetTokensScopeOngoing:
|
||||||
|
entries, err = h.usecase.GetOngoingRuneEntries(ctx.UserContext(), search, blockHeight, req.Limit, req.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error during GetRuneEntryList")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errs.NewPublicError(fmt.Sprintf("invalid scope: %s", req.Scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
runeIds := lo.Map(entries, func(item *runes.RuneEntry, _ int) runes.RuneId { return item.RuneId })
|
||||||
|
totalHolders, err := h.usecase.GetTotalHoldersByRuneIds(ctx.UserContext(), runeIds, blockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error during GetTotalHoldersByRuneIds")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]getTokenInfoResult, 0, len(entries))
|
||||||
|
for _, ent := range entries {
|
||||||
|
totalSupply, err := ent.Supply()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "cannot get total supply of rune")
|
||||||
|
}
|
||||||
|
mintedAmount, err := ent.MintedAmount()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "cannot get minted amount of rune")
|
||||||
|
}
|
||||||
|
circulatingSupply := mintedAmount.Sub(ent.BurnedAmount)
|
||||||
|
|
||||||
|
terms := lo.FromPtr(ent.Terms)
|
||||||
|
result = append(result, getTokenInfoResult{
|
||||||
|
Id: ent.RuneId,
|
||||||
|
Name: ent.SpacedRune,
|
||||||
|
Symbol: string(ent.Symbol),
|
||||||
|
TotalSupply: totalSupply,
|
||||||
|
CirculatingSupply: circulatingSupply,
|
||||||
|
MintedAmount: mintedAmount,
|
||||||
|
BurnedAmount: ent.BurnedAmount,
|
||||||
|
Decimals: ent.Divisibility,
|
||||||
|
DeployedAt: ent.EtchedAt.Unix(),
|
||||||
|
DeployedAtHeight: ent.EtchingBlock,
|
||||||
|
CompletedAt: lo.Ternary(ent.CompletedAt.IsZero(), nil, lo.ToPtr(ent.CompletedAt.Unix())),
|
||||||
|
CompletedAtHeight: ent.CompletedAtHeight,
|
||||||
|
HoldersCount: int(totalHolders[ent.RuneId]),
|
||||||
|
Extend: tokenInfoExtend{
|
||||||
|
Entry: entry{
|
||||||
|
Divisibility: ent.Divisibility,
|
||||||
|
Premine: ent.Premine,
|
||||||
|
Rune: ent.SpacedRune.Rune,
|
||||||
|
Spacers: ent.SpacedRune.Spacers,
|
||||||
|
Symbol: string(ent.Symbol),
|
||||||
|
Terms: entryTerms{
|
||||||
|
Amount: lo.FromPtr(terms.Amount),
|
||||||
|
Cap: lo.FromPtr(terms.Cap),
|
||||||
|
HeightStart: terms.HeightStart,
|
||||||
|
HeightEnd: terms.HeightEnd,
|
||||||
|
OffsetStart: terms.OffsetStart,
|
||||||
|
OffsetEnd: terms.OffsetEnd,
|
||||||
|
},
|
||||||
|
Turbo: ent.Turbo,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.WithStack(ctx.JSON(getTokensResponse{
|
||||||
|
Result: &getTokensResult{
|
||||||
|
List: result,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"cmp"
|
"cmp"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
@@ -16,23 +17,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type getTransactionsRequest struct {
|
type getTransactionsRequest struct {
|
||||||
|
paginationRequest
|
||||||
Wallet string `query:"wallet"`
|
Wallet string `query:"wallet"`
|
||||||
Id string `query:"id"`
|
Id string `query:"id"`
|
||||||
FromBlock int64 `query:"fromBlock"`
|
FromBlock int64 `query:"fromBlock"`
|
||||||
ToBlock int64 `query:"toBlock"`
|
ToBlock int64 `query:"toBlock"`
|
||||||
Limit int32 `query:"limit"`
|
|
||||||
Offset int32 `query:"offset"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
getTransactionsMaxLimit = 3000
|
getTransactionsMaxLimit = 3000
|
||||||
getTransactionsDefaultLimit = 100
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r getTransactionsRequest) Validate() error {
|
func (r *getTransactionsRequest) Validate() error {
|
||||||
var errList []error
|
var errList []error
|
||||||
if r.Id != "" && !isRuneIdOrRuneName(r.Id) {
|
if r.Id != "" {
|
||||||
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
|
id, err := url.QueryUnescape(r.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
r.Id = id
|
||||||
|
if !isRuneIdOrRuneName(r.Id) {
|
||||||
|
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if r.FromBlock < -1 {
|
if r.FromBlock < -1 {
|
||||||
errList = append(errList, errors.Errorf("invalid fromBlock range"))
|
errList = append(errList, errors.Errorf("invalid fromBlock range"))
|
||||||
@@ -128,6 +134,9 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
|
|||||||
if err := req.Validate(); err != nil {
|
if err := req.Validate(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
if err := req.ParseDefault(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
var pkScript []byte
|
var pkScript []byte
|
||||||
if req.Wallet != "" {
|
if req.Wallet != "" {
|
||||||
@@ -146,9 +155,6 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
|
|||||||
return errs.NewPublicError("unable to resolve rune id from \"id\"")
|
return errs.NewPublicError("unable to resolve rune id from \"id\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.Limit == 0 {
|
|
||||||
req.Limit = getTransactionsDefaultLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
// default to latest block
|
// default to latest block
|
||||||
if req.ToBlock == 0 {
|
if req.ToBlock == 0 {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package httphandler
|
package httphandler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/gaze-network/indexer-network/common/errs"
|
"github.com/gaze-network/indexer-network/common/errs"
|
||||||
@@ -12,25 +14,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type getUTXOsRequest struct {
|
type getUTXOsRequest struct {
|
||||||
|
paginationRequest
|
||||||
Wallet string `params:"wallet"`
|
Wallet string `params:"wallet"`
|
||||||
Id string `query:"id"`
|
Id string `query:"id"`
|
||||||
BlockHeight uint64 `query:"blockHeight"`
|
BlockHeight uint64 `query:"blockHeight"`
|
||||||
Limit int32 `query:"limit"`
|
|
||||||
Offset int32 `query:"offset"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
getUTXOsMaxLimit = 3000
|
getUTXOsMaxLimit = 3000
|
||||||
getUTXOsDefaultLimit = 100
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r getUTXOsRequest) Validate() error {
|
func (r *getUTXOsRequest) Validate() error {
|
||||||
var errList []error
|
var errList []error
|
||||||
if r.Wallet == "" {
|
if r.Wallet == "" {
|
||||||
errList = append(errList, errors.New("'wallet' is required"))
|
errList = append(errList, errors.New("'wallet' is required"))
|
||||||
}
|
}
|
||||||
if r.Id != "" && !isRuneIdOrRuneName(r.Id) {
|
if r.Id != "" {
|
||||||
errList = append(errList, errors.New("'id' is not valid rune id or rune name"))
|
id, err := url.QueryUnescape(r.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
r.Id = id
|
||||||
|
if !isRuneIdOrRuneName(r.Id) {
|
||||||
|
errList = append(errList, errors.Errorf("id '%s' is not valid rune id or rune name", r.Id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if r.Limit < 0 {
|
if r.Limit < 0 {
|
||||||
errList = append(errList, errors.New("'limit' must be non-negative"))
|
errList = append(errList, errors.New("'limit' must be non-negative"))
|
||||||
@@ -78,16 +85,15 @@ func (h *HttpHandler) GetUTXOs(ctx *fiber.Ctx) (err error) {
|
|||||||
if err := req.Validate(); err != nil {
|
if err := req.Validate(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
if err := req.ParseDefault(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
pkScript, ok := resolvePkScript(h.network, req.Wallet)
|
pkScript, ok := resolvePkScript(h.network, req.Wallet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errs.NewPublicError("unable to resolve pkscript from \"wallet\"")
|
return errs.NewPublicError("unable to resolve pkscript from \"wallet\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Limit == 0 {
|
|
||||||
req.Limit = getUTXOsDefaultLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
blockHeight := req.BlockHeight
|
blockHeight := req.BlockHeight
|
||||||
if blockHeight == 0 {
|
if blockHeight == 0 {
|
||||||
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())
|
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/gaze-network/indexer-network/common"
|
"github.com/gaze-network/indexer-network/common"
|
||||||
|
"github.com/gaze-network/indexer-network/common/errs"
|
||||||
"github.com/gaze-network/indexer-network/modules/runes/runes"
|
"github.com/gaze-network/indexer-network/modules/runes/runes"
|
||||||
"github.com/gaze-network/indexer-network/modules/runes/usecase"
|
"github.com/gaze-network/indexer-network/modules/runes/usecase"
|
||||||
"github.com/gaze-network/indexer-network/pkg/logger"
|
"github.com/gaze-network/indexer-network/pkg/logger"
|
||||||
@@ -31,6 +33,53 @@ type HttpResponse[T any] struct {
|
|||||||
Result *T `json:"result,omitempty"`
|
Result *T `json:"result,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type paginationRequest struct {
|
||||||
|
Offset int32 `query:"offset"`
|
||||||
|
Limit int32 `query:"limit"`
|
||||||
|
|
||||||
|
// OrderBy string `query:"orderBy"` // ASC or DESC
|
||||||
|
// SortBy string `query:"sortBy"` // column name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req paginationRequest) Validate() error {
|
||||||
|
var errList []error
|
||||||
|
|
||||||
|
// this just safeguard for limit,
|
||||||
|
// each path should have own validation.
|
||||||
|
if req.Limit > 10000 {
|
||||||
|
errList = append(errList, errors.Errorf("too large limit"))
|
||||||
|
}
|
||||||
|
if req.Limit < 0 {
|
||||||
|
errList = append(errList, errors.Errorf("limit must be greater than or equal to 0"))
|
||||||
|
}
|
||||||
|
if req.Offset < 0 {
|
||||||
|
errList = append(errList, errors.Errorf("offset must be greater than or equal to 0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// if req.OrderBy != "" && req.OrderBy != "ASC" && req.OrderBy != "DESC" {
|
||||||
|
// errList = append(errList, errors.Errorf("invalid orderBy value, must be `ASC` or `DESC`"))
|
||||||
|
// }
|
||||||
|
|
||||||
|
return errs.WithPublicMessage(errors.Join(errList...), "pagination validation error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *paginationRequest) ParseDefault() error {
|
||||||
|
if req == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Limit == 0 {
|
||||||
|
req.Limit = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// if req.OrderBy == "" {
|
||||||
|
// req.OrderBy = "ASC"
|
||||||
|
// }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func resolvePkScript(network common.Network, wallet string) ([]byte, bool) {
|
func resolvePkScript(network common.Network, wallet string) ([]byte, bool) {
|
||||||
if wallet == "" {
|
if wallet == "" {
|
||||||
return nil, false
|
return nil, false
|
||||||
|
|||||||
@@ -16,5 +16,6 @@ func (h *HttpHandler) Mount(router fiber.Router) error {
|
|||||||
r.Post("/utxos/output/batch", h.GetUTXOsOutputByLocationBatch)
|
r.Post("/utxos/output/batch", h.GetUTXOsOutputByLocationBatch)
|
||||||
r.Get("/utxos/output/:txHash", h.GetUTXOsOutputByLocation)
|
r.Get("/utxos/output/:txHash", h.GetUTXOsOutputByLocation)
|
||||||
r.Get("/block", h.GetCurrentBlock)
|
r.Get("/block", h.GetCurrentBlock)
|
||||||
|
r.Get("/tokens", h.GetTokens)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE EXTENSION pg_trgm;
|
||||||
-- Indexer Client Information
|
-- Indexer Client Information
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "runes_indexer_stats" (
|
CREATE TABLE IF NOT EXISTS "runes_indexer_stats" (
|
||||||
@@ -48,6 +49,7 @@ CREATE TABLE IF NOT EXISTS "runes_entries" (
|
|||||||
"etched_at" TIMESTAMP NOT NULL
|
"etched_at" TIMESTAMP NOT NULL
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_rune_idx ON "runes_entries" USING BTREE ("rune");
|
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_rune_idx ON "runes_entries" USING BTREE ("rune");
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_rune_gin_idx ON "runes_entries" USING GIN ("rune" gin_trgm_ops); -- to speed up queries with LIKE operator
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_number_idx ON "runes_entries" USING BTREE ("number");
|
CREATE UNIQUE INDEX IF NOT EXISTS runes_entries_number_idx ON "runes_entries" USING BTREE ("number");
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "runes_entry_states" (
|
CREATE TABLE IF NOT EXISTS "runes_entry_states" (
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ SELECT * FROM balances WHERE amount > 0 ORDER BY amount DESC, pkscript LIMIT $3
|
|||||||
-- name: GetBalanceByPkScriptAndRuneId :one
|
-- name: GetBalanceByPkScriptAndRuneId :one
|
||||||
SELECT * FROM runes_balances WHERE pkscript = $1 AND rune_id = $2 AND block_height <= $3 ORDER BY block_height DESC LIMIT 1;
|
SELECT * FROM runes_balances WHERE pkscript = $1 AND rune_id = $2 AND block_height <= $3 ORDER BY block_height DESC LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetTotalHoldersByRuneIds :many
|
||||||
|
WITH balances AS (
|
||||||
|
SELECT DISTINCT ON (rune_id, pkscript) * FROM runes_balances WHERE rune_id = ANY(@rune_ids::TEXT[]) AND block_height <= @block_height ORDER BY rune_id, pkscript, block_height DESC
|
||||||
|
)
|
||||||
|
SELECT rune_id, COUNT(DISTINCT pkscript) FROM balances WHERE amount > 0 GROUP BY rune_id;
|
||||||
|
|
||||||
-- name: GetOutPointBalancesAtOutPoint :many
|
-- name: GetOutPointBalancesAtOutPoint :many
|
||||||
SELECT * FROM runes_outpoint_balances WHERE tx_hash = $1 AND tx_idx = $2;
|
SELECT * FROM runes_outpoint_balances WHERE tx_hash = $1 AND tx_idx = $2;
|
||||||
|
|
||||||
@@ -57,6 +63,47 @@ SELECT * FROM runes_entries
|
|||||||
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
|
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
|
||||||
WHERE runes_entries.rune_id = ANY(@rune_ids::text[]) AND etching_block <= @height;
|
WHERE runes_entries.rune_id = ANY(@rune_ids::text[]) AND etching_block <= @height;
|
||||||
|
|
||||||
|
-- name: GetRuneEntries :many
|
||||||
|
WITH states AS (
|
||||||
|
-- select latest state
|
||||||
|
SELECT DISTINCT ON (rune_id) * FROM runes_entry_states WHERE block_height <= @height ORDER BY rune_id, block_height DESC
|
||||||
|
)
|
||||||
|
SELECT * FROM runes_entries
|
||||||
|
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
|
||||||
|
WHERE (
|
||||||
|
@search = '' OR
|
||||||
|
runes_entries.rune ILIKE @search || '%'
|
||||||
|
)
|
||||||
|
ORDER BY runes_entries.number
|
||||||
|
LIMIT @_limit OFFSET @_offset;
|
||||||
|
|
||||||
|
-- name: GetOngoingRuneEntries :many
|
||||||
|
WITH states AS (
|
||||||
|
-- select latest state
|
||||||
|
SELECT DISTINCT ON (rune_id) * FROM runes_entry_states WHERE block_height <= @height::integer ORDER BY rune_id, block_height DESC
|
||||||
|
)
|
||||||
|
SELECT * FROM runes_entries
|
||||||
|
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
|
||||||
|
WHERE (
|
||||||
|
runes_entries.terms = TRUE AND
|
||||||
|
states.mints < runes_entries.terms_cap AND
|
||||||
|
(
|
||||||
|
runes_entries.terms_height_start IS NULL OR runes_entries.terms_height_start <= @height::integer
|
||||||
|
) AND (
|
||||||
|
runes_entries.terms_height_end IS NULL OR @height::integer <= runes_entries.terms_height_end
|
||||||
|
) AND (
|
||||||
|
runes_entries.terms_offset_start IS NULL OR runes_entries.terms_offset_start + runes_entries.etching_block <= @height::integer
|
||||||
|
) AND (
|
||||||
|
runes_entries.terms_offset_end IS NULL OR @height::integer <= runes_entries.terms_offset_start + runes_entries.etching_block
|
||||||
|
)
|
||||||
|
|
||||||
|
) AND (
|
||||||
|
@search::text = '' OR
|
||||||
|
runes_entries.rune ILIKE @search::text || '%'
|
||||||
|
)
|
||||||
|
ORDER BY (states.mints / runes_entries.terms_cap::float) DESC
|
||||||
|
LIMIT @_limit OFFSET @_offset;
|
||||||
|
|
||||||
-- name: GetRuneIdFromRune :one
|
-- name: GetRuneIdFromRune :one
|
||||||
SELECT rune_id FROM runes_entries WHERE rune = $1;
|
SELECT rune_id FROM runes_entries WHERE rune = $1;
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ type RunesReaderDataGateway interface {
|
|||||||
GetRuneEntryByRuneIdAndHeight(ctx context.Context, runeId runes.RuneId, blockHeight uint64) (*runes.RuneEntry, error)
|
GetRuneEntryByRuneIdAndHeight(ctx context.Context, runeId runes.RuneId, blockHeight uint64) (*runes.RuneEntry, error)
|
||||||
// GetRuneEntryByRuneIdAndHeightBatch returns the RuneEntries for the given runeIds and block height.
|
// GetRuneEntryByRuneIdAndHeightBatch returns the RuneEntries for the given runeIds and block height.
|
||||||
GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]*runes.RuneEntry, error)
|
GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]*runes.RuneEntry, error)
|
||||||
|
// GetRuneEntries returns a list of rune entries, sorted by etching order. If search is not empty, it will filter the results by rune name (prefix).
|
||||||
|
GetRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error)
|
||||||
|
// GetOngoingRuneEntries returns a list of ongoing rune entries (can still mint), sorted by mint progress percent. If search is not empty, it will filter the results by rune name (prefix).
|
||||||
|
GetOngoingRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error)
|
||||||
// CountRuneEntries returns the number of existing rune entries.
|
// CountRuneEntries returns the number of existing rune entries.
|
||||||
CountRuneEntries(ctx context.Context) (uint64, error)
|
CountRuneEntries(ctx context.Context) (uint64, error)
|
||||||
|
|
||||||
@@ -56,6 +60,8 @@ type RunesReaderDataGateway interface {
|
|||||||
GetBalancesByRuneId(ctx context.Context, runeId runes.RuneId, blockHeight uint64, limit int32, offset int32) ([]*entity.Balance, error)
|
GetBalancesByRuneId(ctx context.Context, runeId runes.RuneId, blockHeight uint64, limit int32, offset int32) ([]*entity.Balance, error)
|
||||||
// GetBalancesByPkScriptAndRuneId returns the balance for the given pkScript and runeId at the given blockHeight.
|
// GetBalancesByPkScriptAndRuneId returns the balance for the given pkScript and runeId at the given blockHeight.
|
||||||
GetBalanceByPkScriptAndRuneId(ctx context.Context, pkScript []byte, runeId runes.RuneId, blockHeight uint64) (*entity.Balance, error)
|
GetBalanceByPkScriptAndRuneId(ctx context.Context, pkScript []byte, runeId runes.RuneId, blockHeight uint64) (*entity.Balance, error)
|
||||||
|
// GetTotalHoldersByRuneIds returns the total holders of each the given runeIds.
|
||||||
|
GetTotalHoldersByRuneIds(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunesWriterDataGateway interface {
|
type RunesWriterDataGateway interface {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: batch.go
|
// source: batch.go
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: data.sql
|
// source: data.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
@@ -428,6 +428,118 @@ func (q *Queries) GetLatestIndexedBlock(ctx context.Context) (RunesIndexedBlock,
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOngoingRuneEntries = `-- name: GetOngoingRuneEntries :many
|
||||||
|
WITH states AS (
|
||||||
|
-- select latest state
|
||||||
|
SELECT DISTINCT ON (rune_id) rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entry_states WHERE block_height <= $1::integer ORDER BY rune_id, block_height DESC
|
||||||
|
)
|
||||||
|
SELECT runes_entries.rune_id, number, rune, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at, states.rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entries
|
||||||
|
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
|
||||||
|
WHERE (
|
||||||
|
runes_entries.terms = TRUE AND
|
||||||
|
states.mints < runes_entries.terms_cap AND
|
||||||
|
(
|
||||||
|
runes_entries.terms_height_start IS NULL OR runes_entries.terms_height_start <= $1::integer
|
||||||
|
) AND (
|
||||||
|
runes_entries.terms_height_end IS NULL OR $1::integer <= runes_entries.terms_height_end
|
||||||
|
) AND (
|
||||||
|
runes_entries.terms_offset_start IS NULL OR runes_entries.terms_offset_start + runes_entries.etching_block <= $1::integer
|
||||||
|
) AND (
|
||||||
|
runes_entries.terms_offset_end IS NULL OR $1::integer <= runes_entries.terms_offset_start + runes_entries.etching_block
|
||||||
|
)
|
||||||
|
|
||||||
|
) AND (
|
||||||
|
$2::text = '' OR
|
||||||
|
runes_entries.rune ILIKE $2::text || '%'
|
||||||
|
)
|
||||||
|
ORDER BY (states.mints / runes_entries.terms_cap::float) DESC
|
||||||
|
LIMIT $4 OFFSET $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetOngoingRuneEntriesParams struct {
|
||||||
|
Height int32
|
||||||
|
Search string
|
||||||
|
Offset int32
|
||||||
|
Limit int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetOngoingRuneEntriesRow struct {
|
||||||
|
RuneID string
|
||||||
|
Number int64
|
||||||
|
Rune string
|
||||||
|
Spacers int32
|
||||||
|
Premine pgtype.Numeric
|
||||||
|
Symbol int32
|
||||||
|
Divisibility int16
|
||||||
|
Terms bool
|
||||||
|
TermsAmount pgtype.Numeric
|
||||||
|
TermsCap pgtype.Numeric
|
||||||
|
TermsHeightStart pgtype.Int4
|
||||||
|
TermsHeightEnd pgtype.Int4
|
||||||
|
TermsOffsetStart pgtype.Int4
|
||||||
|
TermsOffsetEnd pgtype.Int4
|
||||||
|
Turbo bool
|
||||||
|
EtchingBlock int32
|
||||||
|
EtchingTxHash string
|
||||||
|
EtchedAt pgtype.Timestamp
|
||||||
|
RuneID_2 pgtype.Text
|
||||||
|
BlockHeight pgtype.Int4
|
||||||
|
Mints pgtype.Numeric
|
||||||
|
BurnedAmount pgtype.Numeric
|
||||||
|
CompletedAt pgtype.Timestamp
|
||||||
|
CompletedAtHeight pgtype.Int4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetOngoingRuneEntries(ctx context.Context, arg GetOngoingRuneEntriesParams) ([]GetOngoingRuneEntriesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getOngoingRuneEntries,
|
||||||
|
arg.Height,
|
||||||
|
arg.Search,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetOngoingRuneEntriesRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetOngoingRuneEntriesRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.RuneID,
|
||||||
|
&i.Number,
|
||||||
|
&i.Rune,
|
||||||
|
&i.Spacers,
|
||||||
|
&i.Premine,
|
||||||
|
&i.Symbol,
|
||||||
|
&i.Divisibility,
|
||||||
|
&i.Terms,
|
||||||
|
&i.TermsAmount,
|
||||||
|
&i.TermsCap,
|
||||||
|
&i.TermsHeightStart,
|
||||||
|
&i.TermsHeightEnd,
|
||||||
|
&i.TermsOffsetStart,
|
||||||
|
&i.TermsOffsetEnd,
|
||||||
|
&i.Turbo,
|
||||||
|
&i.EtchingBlock,
|
||||||
|
&i.EtchingTxHash,
|
||||||
|
&i.EtchedAt,
|
||||||
|
&i.RuneID_2,
|
||||||
|
&i.BlockHeight,
|
||||||
|
&i.Mints,
|
||||||
|
&i.BurnedAmount,
|
||||||
|
&i.CompletedAt,
|
||||||
|
&i.CompletedAtHeight,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getOutPointBalancesAtOutPoint = `-- name: GetOutPointBalancesAtOutPoint :many
|
const getOutPointBalancesAtOutPoint = `-- name: GetOutPointBalancesAtOutPoint :many
|
||||||
SELECT rune_id, pkscript, tx_hash, tx_idx, amount, block_height, spent_height FROM runes_outpoint_balances WHERE tx_hash = $1 AND tx_idx = $2
|
SELECT rune_id, pkscript, tx_hash, tx_idx, amount, block_height, spent_height FROM runes_outpoint_balances WHERE tx_hash = $1 AND tx_idx = $2
|
||||||
`
|
`
|
||||||
@@ -465,6 +577,105 @@ func (q *Queries) GetOutPointBalancesAtOutPoint(ctx context.Context, arg GetOutP
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getRuneEntries = `-- name: GetRuneEntries :many
|
||||||
|
WITH states AS (
|
||||||
|
-- select latest state
|
||||||
|
SELECT DISTINCT ON (rune_id) rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entry_states WHERE block_height <= $4 ORDER BY rune_id, block_height DESC
|
||||||
|
)
|
||||||
|
SELECT runes_entries.rune_id, number, rune, spacers, premine, symbol, divisibility, terms, terms_amount, terms_cap, terms_height_start, terms_height_end, terms_offset_start, terms_offset_end, turbo, etching_block, etching_tx_hash, etched_at, states.rune_id, block_height, mints, burned_amount, completed_at, completed_at_height FROM runes_entries
|
||||||
|
LEFT JOIN states ON runes_entries.rune_id = states.rune_id
|
||||||
|
WHERE (
|
||||||
|
$1 = '' OR
|
||||||
|
runes_entries.rune ILIKE $1 || '%'
|
||||||
|
)
|
||||||
|
ORDER BY runes_entries.number
|
||||||
|
LIMIT $3 OFFSET $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetRuneEntriesParams struct {
|
||||||
|
Search interface{}
|
||||||
|
Offset int32
|
||||||
|
Limit int32
|
||||||
|
Height int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetRuneEntriesRow struct {
|
||||||
|
RuneID string
|
||||||
|
Number int64
|
||||||
|
Rune string
|
||||||
|
Spacers int32
|
||||||
|
Premine pgtype.Numeric
|
||||||
|
Symbol int32
|
||||||
|
Divisibility int16
|
||||||
|
Terms bool
|
||||||
|
TermsAmount pgtype.Numeric
|
||||||
|
TermsCap pgtype.Numeric
|
||||||
|
TermsHeightStart pgtype.Int4
|
||||||
|
TermsHeightEnd pgtype.Int4
|
||||||
|
TermsOffsetStart pgtype.Int4
|
||||||
|
TermsOffsetEnd pgtype.Int4
|
||||||
|
Turbo bool
|
||||||
|
EtchingBlock int32
|
||||||
|
EtchingTxHash string
|
||||||
|
EtchedAt pgtype.Timestamp
|
||||||
|
RuneID_2 pgtype.Text
|
||||||
|
BlockHeight pgtype.Int4
|
||||||
|
Mints pgtype.Numeric
|
||||||
|
BurnedAmount pgtype.Numeric
|
||||||
|
CompletedAt pgtype.Timestamp
|
||||||
|
CompletedAtHeight pgtype.Int4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetRuneEntries(ctx context.Context, arg GetRuneEntriesParams) ([]GetRuneEntriesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getRuneEntries,
|
||||||
|
arg.Search,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
arg.Height,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetRuneEntriesRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetRuneEntriesRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.RuneID,
|
||||||
|
&i.Number,
|
||||||
|
&i.Rune,
|
||||||
|
&i.Spacers,
|
||||||
|
&i.Premine,
|
||||||
|
&i.Symbol,
|
||||||
|
&i.Divisibility,
|
||||||
|
&i.Terms,
|
||||||
|
&i.TermsAmount,
|
||||||
|
&i.TermsCap,
|
||||||
|
&i.TermsHeightStart,
|
||||||
|
&i.TermsHeightEnd,
|
||||||
|
&i.TermsOffsetStart,
|
||||||
|
&i.TermsOffsetEnd,
|
||||||
|
&i.Turbo,
|
||||||
|
&i.EtchingBlock,
|
||||||
|
&i.EtchingTxHash,
|
||||||
|
&i.EtchedAt,
|
||||||
|
&i.RuneID_2,
|
||||||
|
&i.BlockHeight,
|
||||||
|
&i.Mints,
|
||||||
|
&i.BurnedAmount,
|
||||||
|
&i.CompletedAt,
|
||||||
|
&i.CompletedAtHeight,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getRuneEntriesByRuneIds = `-- name: GetRuneEntriesByRuneIds :many
|
const getRuneEntriesByRuneIds = `-- name: GetRuneEntriesByRuneIds :many
|
||||||
WITH states AS (
|
WITH states AS (
|
||||||
-- select latest state
|
-- select latest state
|
||||||
@@ -971,6 +1182,43 @@ func (q *Queries) GetRunesUTXOsByRuneIdAndPkScript(ctx context.Context, arg GetR
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getTotalHoldersByRuneIds = `-- name: GetTotalHoldersByRuneIds :many
|
||||||
|
WITH balances AS (
|
||||||
|
SELECT DISTINCT ON (rune_id, pkscript) pkscript, block_height, rune_id, amount FROM runes_balances WHERE rune_id = ANY($1::TEXT[]) AND block_height <= $2 ORDER BY rune_id, pkscript, block_height DESC
|
||||||
|
)
|
||||||
|
SELECT rune_id, COUNT(DISTINCT pkscript) FROM balances WHERE amount > 0 GROUP BY rune_id
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetTotalHoldersByRuneIdsParams struct {
|
||||||
|
RuneIds []string
|
||||||
|
BlockHeight int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetTotalHoldersByRuneIdsRow struct {
|
||||||
|
RuneID string
|
||||||
|
Count int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetTotalHoldersByRuneIds(ctx context.Context, arg GetTotalHoldersByRuneIdsParams) ([]GetTotalHoldersByRuneIdsRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getTotalHoldersByRuneIds, arg.RuneIds, arg.BlockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetTotalHoldersByRuneIdsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetTotalHoldersByRuneIdsRow
|
||||||
|
if err := rows.Scan(&i.RuneID, &i.Count); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const spendOutPointBalances = `-- name: SpendOutPointBalances :exec
|
const spendOutPointBalances = `-- name: SpendOutPointBalances :exec
|
||||||
UPDATE runes_outpoint_balances SET spent_height = $1 WHERE tx_hash = $2 AND tx_idx = $3
|
UPDATE runes_outpoint_balances SET spent_height = $1 WHERE tx_hash = $2 AND tx_idx = $3
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
// source: info.sql
|
// source: info.sql
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.26.0
|
// sqlc v1.27.0
|
||||||
|
|
||||||
package gen
|
package gen
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func mapIndexerStateTypeToParams(src entity.IndexerState) gen.SetIndexerStatePar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapRuneEntryModelToType(src gen.GetRuneEntriesByRuneIdsRow) (runes.RuneEntry, error) {
|
func mapRuneEntryModelToType(src gen.GetRuneEntriesRow) (runes.RuneEntry, error) {
|
||||||
runeId, err := runes.NewRuneIdFromString(src.RuneID)
|
runeId, err := runes.NewRuneIdFromString(src.RuneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return runes.RuneEntry{}, errors.Wrap(err, "failed to parse rune id")
|
return runes.RuneEntry{}, errors.Wrap(err, "failed to parse rune id")
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ func (r *Repository) GetRuneEntryByRuneIdBatch(ctx context.Context, runeIds []ru
|
|||||||
runeEntries := make(map[runes.RuneId]*runes.RuneEntry, len(rows))
|
runeEntries := make(map[runes.RuneId]*runes.RuneEntry, len(rows))
|
||||||
var errs []error
|
var errs []error
|
||||||
for i, runeEntryModel := range rows {
|
for i, runeEntryModel := range rows {
|
||||||
runeEntry, err := mapRuneEntryModelToType(runeEntryModel)
|
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesRow(runeEntryModel))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
|
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
|
||||||
continue
|
continue
|
||||||
@@ -302,7 +302,7 @@ func (r *Repository) GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, run
|
|||||||
runeEntries := make(map[runes.RuneId]*runes.RuneEntry, len(rows))
|
runeEntries := make(map[runes.RuneId]*runes.RuneEntry, len(rows))
|
||||||
var errs []error
|
var errs []error
|
||||||
for i, runeEntryModel := range rows {
|
for i, runeEntryModel := range rows {
|
||||||
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesByRuneIdsRow(runeEntryModel))
|
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesRow(runeEntryModel))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
|
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
|
||||||
continue
|
continue
|
||||||
@@ -316,6 +316,62 @@ func (r *Repository) GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, run
|
|||||||
return runeEntries, nil
|
return runeEntries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Repository) GetRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error) {
|
||||||
|
rows, err := r.queries.GetRuneEntries(ctx, gen.GetRuneEntriesParams{
|
||||||
|
Search: search,
|
||||||
|
Height: int32(blockHeight),
|
||||||
|
Limit: limit,
|
||||||
|
Offset: offset,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error during query")
|
||||||
|
}
|
||||||
|
|
||||||
|
runeEntries := make([]*runes.RuneEntry, 0, len(rows))
|
||||||
|
var errs []error
|
||||||
|
for i, model := range rows {
|
||||||
|
runeEntry, err := mapRuneEntryModelToType(model)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runeEntries = append(runeEntries, &runeEntry)
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runeEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repository) GetOngoingRuneEntries(ctx context.Context, search string, blockHeight uint64, limit int32, offset int32) ([]*runes.RuneEntry, error) {
|
||||||
|
rows, err := r.queries.GetOngoingRuneEntries(ctx, gen.GetOngoingRuneEntriesParams{
|
||||||
|
Search: search,
|
||||||
|
Height: int32(blockHeight),
|
||||||
|
Limit: limit,
|
||||||
|
Offset: offset,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error during query")
|
||||||
|
}
|
||||||
|
|
||||||
|
runeEntries := make([]*runes.RuneEntry, 0, len(rows))
|
||||||
|
var errs []error
|
||||||
|
for i, model := range rows {
|
||||||
|
runeEntry, err := mapRuneEntryModelToType(gen.GetRuneEntriesRow(model))
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, errors.Wrapf(err, "failed to parse rune entry model index %d", i))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runeEntries = append(runeEntries, &runeEntry)
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runeEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Repository) CountRuneEntries(ctx context.Context) (uint64, error) {
|
func (r *Repository) CountRuneEntries(ctx context.Context) (uint64, error) {
|
||||||
count, err := r.queries.CountRuneEntries(ctx)
|
count, err := r.queries.CountRuneEntries(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -400,6 +456,25 @@ func (r *Repository) GetBalanceByPkScriptAndRuneId(ctx context.Context, pkScript
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Repository) GetTotalHoldersByRuneIds(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]int64, error) {
|
||||||
|
rows, err := r.queries.GetTotalHoldersByRuneIds(ctx, gen.GetTotalHoldersByRuneIdsParams{
|
||||||
|
RuneIds: lo.Map(runeIds, func(runeId runes.RuneId, _ int) string { return runeId.String() }),
|
||||||
|
BlockHeight: int32(blockHeight),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error during query")
|
||||||
|
}
|
||||||
|
holders := make(map[runes.RuneId]int64, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
runeId, err := runes.NewRuneIdFromString(row.RuneID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to parse RuneId")
|
||||||
|
}
|
||||||
|
holders[runeId] = row.Count
|
||||||
|
}
|
||||||
|
return holders, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Repository) CreateRuneTransaction(ctx context.Context, tx *entity.RuneTransaction) error {
|
func (r *Repository) CreateRuneTransaction(ctx context.Context, tx *entity.RuneTransaction) error {
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -25,3 +25,11 @@ func (u *Usecase) GetBalancesByRuneId(ctx context.Context, runeId runes.RuneId,
|
|||||||
}
|
}
|
||||||
return balances, nil
|
return balances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Usecase) GetTotalHoldersByRuneIds(ctx context.Context, runeIds []runes.RuneId, blockHeight uint64) (map[runes.RuneId]int64, error) {
|
||||||
|
holders, err := u.runesDg.GetTotalHoldersByRuneIds(ctx, runeIds, blockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get total holders by rune ids")
|
||||||
|
}
|
||||||
|
return holders, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,3 +46,19 @@ func (u *Usecase) GetRuneEntryByRuneIdAndHeightBatch(ctx context.Context, runeId
|
|||||||
}
|
}
|
||||||
return runeEntry, nil
|
return runeEntry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Usecase) GetRuneEntries(ctx context.Context, search string, blockHeight uint64, limit, offset int32) ([]*runes.RuneEntry, error) {
|
||||||
|
entries, err := u.runesDg.GetRuneEntries(ctx, search, blockHeight, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to listing rune entries")
|
||||||
|
}
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Usecase) GetOngoingRuneEntries(ctx context.Context, search string, blockHeight uint64, limit, offset int32) ([]*runes.RuneEntry, error) {
|
||||||
|
entries, err := u.runesDg.GetOngoingRuneEntries(ctx, search, blockHeight, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to listing rune entries")
|
||||||
|
}
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user