feat: brc20 indexing logic

This commit is contained in:
Gaze
2024-06-09 14:49:13 +07:00
parent 806d27fb46
commit 99bdf49f02
18 changed files with 884 additions and 63 deletions

View File

@@ -50,10 +50,8 @@ CREATE TABLE IF NOT EXISTS "brc20_tick_entry_states" (
PRIMARY KEY ("tick", "block_height") PRIMARY KEY ("tick", "block_height")
); );
CREATE SEQUENCE IF NOT EXISTS brc20_event_id_seq;
CREATE TABLE IF NOT EXISTS "brc20_event_deploys" ( CREATE TABLE IF NOT EXISTS "brc20_event_deploys" (
"id" BIGINT PRIMARY KEY DEFAULT nextval('brc20_event_id_seq'), "id" BIGINT PRIMARY KEY NOT NULL,
"inscription_id" TEXT NOT NULL, "inscription_id" TEXT NOT NULL,
"inscription_number" BIGINT NOT NULL, "inscription_number" BIGINT NOT NULL,
"tick" TEXT NOT NULL, -- lowercase of original_tick "tick" TEXT NOT NULL, -- lowercase of original_tick
@@ -73,7 +71,7 @@ CREATE TABLE IF NOT EXISTS "brc20_event_deploys" (
CREATE INDEX IF NOT EXISTS brc20_event_deploys_block_height_idx ON "brc20_event_deploys" USING BTREE ("block_height"); CREATE INDEX IF NOT EXISTS brc20_event_deploys_block_height_idx ON "brc20_event_deploys" USING BTREE ("block_height");
CREATE TABLE IF NOT EXISTS "brc20_event_mints" ( CREATE TABLE IF NOT EXISTS "brc20_event_mints" (
"id" BIGINT PRIMARY KEY DEFAULT nextval('brc20_event_id_seq'), "id" BIGINT PRIMARY KEY NOT NULL,
"inscription_id" TEXT NOT NULL, "inscription_id" TEXT NOT NULL,
"inscription_number" BIGINT NOT NULL, "inscription_number" BIGINT NOT NULL,
"tick" TEXT NOT NULL, -- lowercase of original_tick "tick" TEXT NOT NULL, -- lowercase of original_tick
@@ -91,7 +89,7 @@ CREATE TABLE IF NOT EXISTS "brc20_event_mints" (
CREATE INDEX IF NOT EXISTS brc20_event_mints_block_height_idx ON "brc20_event_mints" USING BTREE ("block_height"); CREATE INDEX IF NOT EXISTS brc20_event_mints_block_height_idx ON "brc20_event_mints" USING BTREE ("block_height");
CREATE TABLE IF NOT EXISTS "brc20_event_inscribe_transfers" ( CREATE TABLE IF NOT EXISTS "brc20_event_inscribe_transfers" (
"id" BIGINT PRIMARY KEY DEFAULT nextval('brc20_event_id_seq'), "id" BIGINT PRIMARY KEY NOT NULL,
"inscription_id" TEXT NOT NULL, "inscription_id" TEXT NOT NULL,
"inscription_number" BIGINT NOT NULL, "inscription_number" BIGINT NOT NULL,
"tick" TEXT NOT NULL, -- lowercase of original_tick "tick" TEXT NOT NULL, -- lowercase of original_tick
@@ -108,9 +106,10 @@ CREATE TABLE IF NOT EXISTS "brc20_event_inscribe_transfers" (
"amount" DECIMAL NOT NULL "amount" DECIMAL NOT NULL
); );
CREATE INDEX IF NOT EXISTS brc20_event_inscribe_transfers_block_height_idx ON "brc20_event_inscribe_transfers" USING BTREE ("block_height"); CREATE INDEX IF NOT EXISTS brc20_event_inscribe_transfers_block_height_idx ON "brc20_event_inscribe_transfers" USING BTREE ("block_height");
CREATE INDEX IF NOT EXISTS brc20_event_inscribe_transfers_inscription_id_idx ON "brc20_event_inscribe_transfers" USING BTREE ("inscription_id"); -- used for validating transfer transfer events
CREATE TABLE IF NOT EXISTS "brc20_event_transfer_transfers" ( CREATE TABLE IF NOT EXISTS "brc20_event_transfer_transfers" (
"id" BIGINT PRIMARY KEY DEFAULT nextval('brc20_event_id_seq'), "id" BIGINT PRIMARY KEY NOT NULL,
"inscription_id" TEXT NOT NULL, "inscription_id" TEXT NOT NULL,
"inscription_number" BIGINT NOT NULL, "inscription_number" BIGINT NOT NULL,
"tick" TEXT NOT NULL, -- lowercase of original_tick "tick" TEXT NOT NULL, -- lowercase of original_tick
@@ -126,6 +125,7 @@ CREATE TABLE IF NOT EXISTS "brc20_event_transfer_transfers" (
"to_pkscript" TEXT NOT NULL, "to_pkscript" TEXT NOT NULL,
"to_satpoint" TEXT NOT NULL, "to_satpoint" TEXT NOT NULL,
"to_output_index" INT NOT NULL, "to_output_index" INT NOT NULL,
"spent_as_fee" BOOLEAN NOT NULL,
"amount" DECIMAL NOT NULL "amount" DECIMAL NOT NULL
); );
CREATE INDEX IF NOT EXISTS brc20_event_transfer_transfers_block_height_idx ON "brc20_event_transfer_transfers" USING BTREE ("block_height"); CREATE INDEX IF NOT EXISTS brc20_event_transfer_transfers_block_height_idx ON "brc20_event_transfer_transfers" USING BTREE ("block_height");
@@ -156,6 +156,7 @@ CREATE TABLE IF NOT EXISTS "brc20_inscription_entries" (
"created_at" TIMESTAMP NOT NULL, "created_at" TIMESTAMP NOT NULL,
"created_at_height" INT NOT NULL "created_at_height" INT NOT NULL
); );
CREATE INDEX IF NOT EXISTS brc20_inscription_entries_id_number_idx ON "brc20_inscription_entries" USING BTREE ("id", "number");
CREATE TABLE IF NOT EXISTS "brc20_inscription_entry_states" ( CREATE TABLE IF NOT EXISTS "brc20_inscription_entry_states" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
@@ -168,6 +169,8 @@ CREATE TABLE IF NOT EXISTS "brc20_inscription_transfers" (
"inscription_id" TEXT NOT NULL, "inscription_id" TEXT NOT NULL,
"block_height" INT NOT NULL, "block_height" INT NOT NULL,
"tx_index" INT NOT NULL, "tx_index" INT NOT NULL,
"tx_hash" TEXT NOT NULL,
"from_input_index" INT NOT NULL,
"old_satpoint_tx_hash" TEXT, "old_satpoint_tx_hash" TEXT,
"old_satpoint_out_idx" INT, "old_satpoint_out_idx" INT,
"old_satpoint_offset" BIGINT, "old_satpoint_offset" BIGINT,
@@ -177,6 +180,7 @@ CREATE TABLE IF NOT EXISTS "brc20_inscription_transfers" (
"new_pkscript" TEXT NOT NULL, "new_pkscript" TEXT NOT NULL,
"new_output_value" BIGINT NOT NULL, "new_output_value" BIGINT NOT NULL,
"sent_as_fee" BOOLEAN NOT NULL, "sent_as_fee" BOOLEAN NOT NULL,
"transfer_count" INT NOT NULL,
PRIMARY KEY ("inscription_id", "block_height", "tx_index") PRIMARY KEY ("inscription_id", "block_height", "tx_index")
); );
CREATE INDEX IF NOT EXISTS brc20_inscription_transfers_block_height_tx_index_idx ON "brc20_inscription_transfers" USING BTREE ("block_height", "tx_index"); CREATE INDEX IF NOT EXISTS brc20_inscription_transfers_block_height_tx_index_idx ON "brc20_inscription_transfers" USING BTREE ("block_height", "tx_index");

View File

@@ -35,6 +35,43 @@ SELECT * FROM "brc20_tick_entries"
LEFT JOIN "states" ON "brc20_tick_entries"."tick" = "states"."tick" LEFT JOIN "states" ON "brc20_tick_entries"."tick" = "states"."tick"
WHERE "brc20_tick_entries"."tick" = ANY(@ticks::text[]); WHERE "brc20_tick_entries"."tick" = ANY(@ticks::text[]);
-- name: GetInscriptionNumbersByIds :many
SELECT id, number FROM "brc20_inscription_entries" WHERE "id" = ANY(@inscription_ids::text[]);
-- name: GetInscriptionParentsByIds :many
SELECT id, parents FROM "brc20_inscription_entries" WHERE "id" = ANY(@inscription_ids::text[]);
-- name: GetLatestEventIds :one
WITH "latest_deploy_id" AS (
SELECT "id" FROM "brc20_event_deploys" ORDER BY "id" DESC LIMIT 1
),
"latest_mint_id" AS (
SELECT "id" FROM "brc20_event_mints" ORDER BY "id" DESC LIMIT 1
),
"latest_inscribe_transfer_id" AS (
SELECT "id" FROM "brc20_event_inscribe_transfers" ORDER BY "id" DESC LIMIT 1
),
"latest_transfer_transfer_id" AS (
SELECT "id" FROM "brc20_event_transfer_transfers" ORDER BY "id" DESC LIMIT 1
)
SELECT
(SELECT "id" FROM "latest_deploy_id") AS "event_deploy_id",
(SELECT "id" FROM "latest_mint_id") AS "event_mint_id",
(SELECT "id" FROM "latest_inscribe_transfer_id") AS "event_inscribe_transfer_id",
(SELECT "id" FROM "latest_transfer_transfer_id") AS "event_transfer_transfer_id";
-- name: GetBalancesBatchAtHeight :many
SELECT DISTINCT ON ("brc20_balances"."pkscript", "brc20_balances"."tick") "brc20_balances".* FROM "brc20_balances"
INNER JOIN (
SELECT
unnest(@pkscript_arr::text[]) AS "pkscript",
unnest(@tick_arr::text[]) AS "tick"
) "queries" ON "brc20_balances"."pkscript" = "queries"."pkscript" AND "brc20_balances"."tick" = "queries"."tick" AND "brc20_balances"."block_height" <= @block_height
ORDER BY "brc20_balances"."pkscript", "brc20_balances"."tick", "block_height" DESC;
-- name: GetEventInscribeTransfersByInscriptionIds :many
SELECT * FROM "brc20_event_inscribe_transfers" WHERE "inscription_id" = ANY(@inscription_ids::text[]);
-- name: CreateIndexedBlock :exec -- name: CreateIndexedBlock :exec
INSERT INTO "brc20_indexed_blocks" ("height", "hash", "event_hash", "cumulative_event_hash") VALUES ($1, $2, $3, $4); INSERT INTO "brc20_indexed_blocks" ("height", "hash", "event_hash", "cumulative_event_hash") VALUES ($1, $2, $3, $4);
@@ -54,7 +91,7 @@ INSERT INTO "brc20_inscription_entries" ("id", "number", "sequence_number", "del
INSERT INTO "brc20_inscription_entry_states" ("id", "block_height", "transfer_count") VALUES ($1, $2, $3); INSERT INTO "brc20_inscription_entry_states" ("id", "block_height", "transfer_count") VALUES ($1, $2, $3);
-- name: CreateInscriptionTransfers :batchexec -- name: CreateInscriptionTransfers :batchexec
INSERT INTO "brc20_inscription_transfers" ("inscription_id", "block_height", "tx_index", "old_satpoint_tx_hash", "old_satpoint_out_idx", "old_satpoint_offset", "new_satpoint_tx_hash", "new_satpoint_out_idx", "new_satpoint_offset", "new_pkscript", "new_output_value", "sent_as_fee") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12); INSERT INTO "brc20_inscription_transfers" ("inscription_id", "block_height", "tx_index", "tx_hash", "from_input_index", "old_satpoint_tx_hash", "old_satpoint_out_idx", "old_satpoint_offset", "new_satpoint_tx_hash", "new_satpoint_out_idx", "new_satpoint_offset", "new_pkscript", "new_output_value", "sent_as_fee", "transfer_count") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);
-- name: CreateEventDeploys :batchexec -- name: CreateEventDeploys :batchexec
INSERT INTO "brc20_event_deploys" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "pkscript", "satpoint", "total_supply", "decimals", "limit_per_mint", "is_self_mint") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); INSERT INTO "brc20_event_deploys" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "pkscript", "satpoint", "total_supply", "decimals", "limit_per_mint", "is_self_mint") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14);
@@ -66,7 +103,7 @@ INSERT INTO "brc20_event_mints" ("inscription_id", "inscription_number", "tick",
INSERT INTO "brc20_event_inscribe_transfers" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "pkscript", "satpoint", "output_index", "sats_amount", "amount") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13); INSERT INTO "brc20_event_inscribe_transfers" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "pkscript", "satpoint", "output_index", "sats_amount", "amount") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);
-- name: CreateEventTransferTransfers :batchexec -- name: CreateEventTransferTransfers :batchexec
INSERT INTO "brc20_event_transfer_transfers" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "from_pkscript", "from_satpoint", "from_input_index", "to_pkscript", "to_satpoint", "to_output_index", "amount") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15); INSERT INTO "brc20_event_transfer_transfers" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "from_pkscript", "from_satpoint", "from_input_index", "to_pkscript", "to_satpoint", "to_output_index", "spent_as_fee", "amount") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);
-- name: DeleteIndexedBlocksSinceHeight :exec -- name: DeleteIndexedBlocksSinceHeight :exec
DELETE FROM "brc20_indexed_blocks" WHERE "height" >= $1; DELETE FROM "brc20_indexed_blocks" WHERE "height" >= $1;

View File

@@ -28,7 +28,12 @@ type BRC20ReaderDataGateway interface {
GetProcessorStats(ctx context.Context) (*entity.ProcessorStats, error) GetProcessorStats(ctx context.Context) (*entity.ProcessorStats, error)
GetInscriptionTransfersInOutPoints(ctx context.Context, outPoints []wire.OutPoint) (map[ordinals.SatPoint][]*entity.InscriptionTransfer, error) GetInscriptionTransfersInOutPoints(ctx context.Context, outPoints []wire.OutPoint) (map[ordinals.SatPoint][]*entity.InscriptionTransfer, error)
GetInscriptionEntriesByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]*ordinals.InscriptionEntry, error) GetInscriptionEntriesByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]*ordinals.InscriptionEntry, error)
GetInscriptionNumbersByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]int64, error)
GetInscriptionParentsByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]ordinals.InscriptionId, error)
GetBalancesBatchAtHeight(ctx context.Context, blockHeight uint64, queries []GetBalancesBatchAtHeightQuery) (map[string]map[string]*entity.Balance, error)
GetTickEntriesByTicks(ctx context.Context, ticks []string) (map[string]*entity.TickEntry, error) GetTickEntriesByTicks(ctx context.Context, ticks []string) (map[string]*entity.TickEntry, error)
GetEventInscribeTransfersByInscriptionIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]*entity.EventInscribeTransfer, error)
GetLatestEventId(ctx context.Context) (uint64, error)
} }
type BRC20WriterDataGateway interface { type BRC20WriterDataGateway interface {
@@ -58,3 +63,9 @@ type BRC20WriterDataGateway interface {
DeleteInscriptionEntryStatesSinceHeight(ctx context.Context, height uint64) error DeleteInscriptionEntryStatesSinceHeight(ctx context.Context, height uint64) error
DeleteInscriptionTransfersSinceHeight(ctx context.Context, height uint64) error DeleteInscriptionTransfersSinceHeight(ctx context.Context, height uint64) error
} }
type GetBalancesBatchAtHeightQuery struct {
PkScriptHex string
Tick string
BlockHeight uint64
}

View File

@@ -0,0 +1,11 @@
package entity
import "github.com/shopspring/decimal"
type Balance struct {
PkScript []byte
Tick string
BlockHeight uint64
OverallBalance decimal.Decimal
AvailableBalance decimal.Decimal
}

View File

@@ -11,7 +11,7 @@ import (
type EventDeploy struct { type EventDeploy struct {
Id uint64 Id uint64
InscriptionId ordinals.InscriptionId InscriptionId ordinals.InscriptionId
InscriptionNumber uint64 InscriptionNumber int64
Tick string Tick string
OriginalTick string OriginalTick string
TxHash chainhash.Hash TxHash chainhash.Hash

View File

@@ -11,7 +11,7 @@ import (
type EventInscribeTransfer struct { type EventInscribeTransfer struct {
Id uint64 Id uint64
InscriptionId ordinals.InscriptionId InscriptionId ordinals.InscriptionId
InscriptionNumber uint64 InscriptionNumber int64
Tick string Tick string
OriginalTick string OriginalTick string
TxHash chainhash.Hash TxHash chainhash.Hash

View File

@@ -11,7 +11,7 @@ import (
type EventMint struct { type EventMint struct {
Id uint64 Id uint64
InscriptionId ordinals.InscriptionId InscriptionId ordinals.InscriptionId
InscriptionNumber uint64 InscriptionNumber int64
Tick string Tick string
OriginalTick string OriginalTick string
TxHash chainhash.Hash TxHash chainhash.Hash

View File

@@ -11,7 +11,7 @@ import (
type EventTransferTransfer struct { type EventTransferTransfer struct {
Id uint64 Id uint64
InscriptionId ordinals.InscriptionId InscriptionId ordinals.InscriptionId
InscriptionNumber uint64 InscriptionNumber int64
Tick string Tick string
OriginalTick string OriginalTick string
TxHash chainhash.Hash TxHash chainhash.Hash
@@ -25,5 +25,6 @@ type EventTransferTransfer struct {
ToPkScript []byte ToPkScript []byte
ToSatPoint ordinals.SatPoint ToSatPoint ordinals.SatPoint
ToOutputIndex uint32 ToOutputIndex uint32
SpentAsFee bool
Amount decimal.Decimal Amount decimal.Decimal
} }

View File

@@ -8,6 +8,7 @@ import (
type OriginOld struct { type OriginOld struct {
Content []byte Content []byte
OldSatPoint ordinals.SatPoint OldSatPoint ordinals.SatPoint
InputIndex uint32
} }
type OriginNew struct { type OriginNew struct {
Inscription ordinals.Inscription Inscription ordinals.Inscription

View File

@@ -1,15 +1,21 @@
package entity package entity
import "github.com/gaze-network/indexer-network/modules/brc20/internal/ordinals" import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/gaze-network/indexer-network/modules/brc20/internal/ordinals"
)
type InscriptionTransfer struct { type InscriptionTransfer struct {
InscriptionId ordinals.InscriptionId InscriptionId ordinals.InscriptionId
BlockHeight uint64 BlockHeight uint64
TxIndex uint32 TxIndex uint32
TxHash chainhash.Hash
Content []byte Content []byte
FromInputIndex uint32
OldSatPoint ordinals.SatPoint OldSatPoint ordinals.SatPoint
NewSatPoint ordinals.SatPoint NewSatPoint ordinals.SatPoint
NewPkScript []byte NewPkScript []byte
NewOutputValue uint64 NewOutputValue uint64
SentAsFee bool SentAsFee bool
TransferCount uint32
} }

View File

@@ -110,6 +110,110 @@ func (r *Repository) GetInscriptionEntriesByIds(ctx context.Context, ids []ordin
return result, nil return result, nil
} }
func (r *Repository) GetInscriptionNumbersByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]int64, error) {
idStrs := lo.Map(ids, func(id ordinals.InscriptionId, _ int) string { return id.String() })
models, err := r.queries.GetInscriptionNumbersByIds(ctx, idStrs)
if err != nil {
return nil, errors.WithStack(err)
}
result := make(map[ordinals.InscriptionId]int64)
for _, model := range models {
inscriptionId, err := ordinals.NewInscriptionIdFromString(model.Id)
if err != nil {
return nil, errors.Wrap(err, "failed to parse inscription id")
}
result[inscriptionId] = model.Number
}
return result, nil
}
func (r *Repository) GetInscriptionParentsByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]ordinals.InscriptionId, error) {
idStrs := lo.Map(ids, func(id ordinals.InscriptionId, _ int) string { return id.String() })
models, err := r.queries.GetInscriptionParentsByIds(ctx, idStrs)
if err != nil {
return nil, errors.WithStack(err)
}
result := make(map[ordinals.InscriptionId]ordinals.InscriptionId)
for _, model := range models {
if len(model.Parents) == 0 {
// no parent
continue
}
if len(model.Parents) > 1 {
// sanity check, should not happen since 0.14 ord supports only 1 parent
continue
}
inscriptionId, err := ordinals.NewInscriptionIdFromString(model.Id)
if err != nil {
return nil, errors.Wrap(err, "failed to parse inscription id")
}
parentId, err := ordinals.NewInscriptionIdFromString(model.Parents[0])
if err != nil {
return nil, errors.Wrap(err, "failed to parse parent id")
}
result[inscriptionId] = parentId
}
return result, nil
}
func (r *Repository) GetLatestEventId(ctx context.Context) (uint64, error) {
row, err := r.queries.GetLatestEventIds(ctx)
if err != nil {
return 0, errors.WithStack(err)
}
return uint64(max(row.EventDeployID, row.EventMintID, row.EventInscribeTransferID, row.EventTransferTransferID)), nil
}
func (r *Repository) GetBalancesBatchAtHeight(ctx context.Context, blockHeight uint64, queries []datagateway.GetBalancesBatchAtHeightQuery) (map[string]map[string]*entity.Balance, error) {
pkScripts := make([]string, 0)
ticks := make([]string, 0)
for _, query := range queries {
pkScripts = append(pkScripts, query.PkScriptHex)
ticks = append(ticks, query.Tick)
}
models, err := r.queries.GetBalancesBatchAtHeight(ctx, gen.GetBalancesBatchAtHeightParams{
PkscriptArr: pkScripts,
TickArr: ticks,
BlockHeight: int32(blockHeight),
})
if err != nil {
return nil, errors.WithStack(err)
}
result := make(map[string]map[string]*entity.Balance)
for _, model := range models {
balance, err := mapBalanceModelToType(model)
if err != nil {
return nil, errors.Wrap(err, "failed to parse balance model")
}
if _, ok := result[model.Pkscript]; !ok {
result[model.Pkscript] = make(map[string]*entity.Balance)
}
result[model.Pkscript][model.Tick] = &balance
}
return result, nil
}
func (r *Repository) GetEventInscribeTransfersByInscriptionIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]*entity.EventInscribeTransfer, error) {
idStrs := lo.Map(ids, func(id ordinals.InscriptionId, _ int) string { return id.String() })
models, err := r.queries.GetEventInscribeTransfersByInscriptionIds(ctx, idStrs)
if err != nil {
return nil, errors.WithStack(err)
}
result := make(map[ordinals.InscriptionId]*entity.EventInscribeTransfer)
for _, model := range models {
event, err := mapEventInscribeTransferModelToType(model)
if err != nil {
return nil, errors.Wrap(err, "failed to parse event inscribe transfer model")
}
result[event.InscriptionId] = &event
}
return result, nil
}
func (r *Repository) GetTickEntriesByTicks(ctx context.Context, ticks []string) (map[string]*entity.TickEntry, error) { func (r *Repository) GetTickEntriesByTicks(ctx context.Context, ticks []string) (map[string]*entity.TickEntry, error) {
models, err := r.queries.GetTickEntriesByTicks(ctx, ticks) models, err := r.queries.GetTickEntriesByTicks(ctx, ticks)
if err != nil { if err != nil {

View File

@@ -231,7 +231,7 @@ func (b *CreateEventMintsBatchResults) Close() error {
} }
const createEventTransferTransfers = `-- name: CreateEventTransferTransfers :batchexec const createEventTransferTransfers = `-- name: CreateEventTransferTransfers :batchexec
INSERT INTO "brc20_event_transfer_transfers" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "from_pkscript", "from_satpoint", "from_input_index", "to_pkscript", "to_satpoint", "to_output_index", "amount") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) INSERT INTO "brc20_event_transfer_transfers" ("inscription_id", "inscription_number", "tick", "original_tick", "tx_hash", "block_height", "tx_index", "timestamp", "from_pkscript", "from_satpoint", "from_input_index", "to_pkscript", "to_satpoint", "to_output_index", "spent_as_fee", "amount") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
` `
type CreateEventTransferTransfersBatchResults struct { type CreateEventTransferTransfersBatchResults struct {
@@ -255,6 +255,7 @@ type CreateEventTransferTransfersParams struct {
ToPkscript string ToPkscript string
ToSatpoint string ToSatpoint string
ToOutputIndex int32 ToOutputIndex int32
SpentAsFee bool
Amount pgtype.Numeric Amount pgtype.Numeric
} }
@@ -276,6 +277,7 @@ func (q *Queries) CreateEventTransferTransfers(ctx context.Context, arg []Create
a.ToPkscript, a.ToPkscript,
a.ToSatpoint, a.ToSatpoint,
a.ToOutputIndex, a.ToOutputIndex,
a.SpentAsFee,
a.Amount, a.Amount,
} }
batch.Queue(createEventTransferTransfers, vals...) batch.Queue(createEventTransferTransfers, vals...)
@@ -432,7 +434,7 @@ func (b *CreateInscriptionEntryStatesBatchResults) Close() error {
} }
const createInscriptionTransfers = `-- name: CreateInscriptionTransfers :batchexec const createInscriptionTransfers = `-- name: CreateInscriptionTransfers :batchexec
INSERT INTO "brc20_inscription_transfers" ("inscription_id", "block_height", "tx_index", "old_satpoint_tx_hash", "old_satpoint_out_idx", "old_satpoint_offset", "new_satpoint_tx_hash", "new_satpoint_out_idx", "new_satpoint_offset", "new_pkscript", "new_output_value", "sent_as_fee") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) INSERT INTO "brc20_inscription_transfers" ("inscription_id", "block_height", "tx_index", "tx_hash", "from_input_index", "old_satpoint_tx_hash", "old_satpoint_out_idx", "old_satpoint_offset", "new_satpoint_tx_hash", "new_satpoint_out_idx", "new_satpoint_offset", "new_pkscript", "new_output_value", "sent_as_fee", "transfer_count") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
` `
type CreateInscriptionTransfersBatchResults struct { type CreateInscriptionTransfersBatchResults struct {
@@ -445,6 +447,8 @@ type CreateInscriptionTransfersParams struct {
InscriptionID string InscriptionID string
BlockHeight int32 BlockHeight int32
TxIndex int32 TxIndex int32
TxHash string
FromInputIndex int32
OldSatpointTxHash pgtype.Text OldSatpointTxHash pgtype.Text
OldSatpointOutIdx pgtype.Int4 OldSatpointOutIdx pgtype.Int4
OldSatpointOffset pgtype.Int8 OldSatpointOffset pgtype.Int8
@@ -454,6 +458,7 @@ type CreateInscriptionTransfersParams struct {
NewPkscript string NewPkscript string
NewOutputValue int64 NewOutputValue int64
SentAsFee bool SentAsFee bool
TransferCount int32
} }
func (q *Queries) CreateInscriptionTransfers(ctx context.Context, arg []CreateInscriptionTransfersParams) *CreateInscriptionTransfersBatchResults { func (q *Queries) CreateInscriptionTransfers(ctx context.Context, arg []CreateInscriptionTransfersParams) *CreateInscriptionTransfersBatchResults {
@@ -463,6 +468,8 @@ func (q *Queries) CreateInscriptionTransfers(ctx context.Context, arg []CreateIn
a.InscriptionID, a.InscriptionID,
a.BlockHeight, a.BlockHeight,
a.TxIndex, a.TxIndex,
a.TxHash,
a.FromInputIndex,
a.OldSatpointTxHash, a.OldSatpointTxHash,
a.OldSatpointOutIdx, a.OldSatpointOutIdx,
a.OldSatpointOffset, a.OldSatpointOffset,
@@ -472,6 +479,7 @@ func (q *Queries) CreateInscriptionTransfers(ctx context.Context, arg []CreateIn
a.NewPkscript, a.NewPkscript,
a.NewOutputValue, a.NewOutputValue,
a.SentAsFee, a.SentAsFee,
a.TransferCount,
} }
batch.Queue(createInscriptionTransfers, vals...) batch.Queue(createInscriptionTransfers, vals...)
} }

View File

@@ -161,6 +161,87 @@ func (q *Queries) DeleteTickEntryStatesSinceHeight(ctx context.Context, blockHei
return err return err
} }
const getBalancesBatchAtHeight = `-- name: GetBalancesBatchAtHeight :many
SELECT DISTINCT ON ("brc20_balances"."pkscript", "brc20_balances"."tick") brc20_balances.pkscript, brc20_balances.block_height, brc20_balances.tick, brc20_balances.overall_balance, brc20_balances.available_balance FROM "brc20_balances"
INNER JOIN (
SELECT
unnest($1::text[]) AS "pkscript",
unnest($2::text[]) AS "tick"
) "queries" ON "brc20_balances"."pkscript" = "queries"."pkscript" AND "brc20_balances"."tick" = "queries"."tick" AND "brc20_balances"."block_height" <= $3
ORDER BY "brc20_balances"."pkscript", "brc20_balances"."tick", "block_height" DESC
`
type GetBalancesBatchAtHeightParams struct {
PkscriptArr []string
TickArr []string
BlockHeight int32
}
func (q *Queries) GetBalancesBatchAtHeight(ctx context.Context, arg GetBalancesBatchAtHeightParams) ([]Brc20Balance, error) {
rows, err := q.db.Query(ctx, getBalancesBatchAtHeight, arg.PkscriptArr, arg.TickArr, arg.BlockHeight)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Brc20Balance
for rows.Next() {
var i Brc20Balance
if err := rows.Scan(
&i.Pkscript,
&i.BlockHeight,
&i.Tick,
&i.OverallBalance,
&i.AvailableBalance,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getEventInscribeTransfersByInscriptionIds = `-- name: GetEventInscribeTransfersByInscriptionIds :many
SELECT id, inscription_id, inscription_number, tick, original_tick, tx_hash, block_height, tx_index, timestamp, pkscript, satpoint, output_index, sats_amount, amount FROM "brc20_event_inscribe_transfers" WHERE "inscription_id" = ANY($1::text[])
`
func (q *Queries) GetEventInscribeTransfersByInscriptionIds(ctx context.Context, inscriptionIds []string) ([]Brc20EventInscribeTransfer, error) {
rows, err := q.db.Query(ctx, getEventInscribeTransfersByInscriptionIds, inscriptionIds)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Brc20EventInscribeTransfer
for rows.Next() {
var i Brc20EventInscribeTransfer
if err := rows.Scan(
&i.Id,
&i.InscriptionID,
&i.InscriptionNumber,
&i.Tick,
&i.OriginalTick,
&i.TxHash,
&i.BlockHeight,
&i.TxIndex,
&i.Timestamp,
&i.Pkscript,
&i.Satpoint,
&i.OutputIndex,
&i.SatsAmount,
&i.Amount,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getIndexedBlockByHeight = `-- name: GetIndexedBlockByHeight :one const getIndexedBlockByHeight = `-- name: GetIndexedBlockByHeight :one
SELECT height, hash, event_hash, cumulative_event_hash FROM "brc20_indexed_blocks" WHERE "height" = $1 SELECT height, hash, event_hash, cumulative_event_hash FROM "brc20_indexed_blocks" WHERE "height" = $1
` `
@@ -247,8 +328,66 @@ func (q *Queries) GetInscriptionEntriesByIds(ctx context.Context, inscriptionIds
return items, nil return items, nil
} }
const getInscriptionNumbersByIds = `-- name: GetInscriptionNumbersByIds :many
SELECT id, number FROM "brc20_inscription_entries" WHERE "id" = ANY($1::text[])
`
type GetInscriptionNumbersByIdsRow struct {
Id string
Number int64
}
func (q *Queries) GetInscriptionNumbersByIds(ctx context.Context, inscriptionIds []string) ([]GetInscriptionNumbersByIdsRow, error) {
rows, err := q.db.Query(ctx, getInscriptionNumbersByIds, inscriptionIds)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetInscriptionNumbersByIdsRow
for rows.Next() {
var i GetInscriptionNumbersByIdsRow
if err := rows.Scan(&i.Id, &i.Number); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getInscriptionParentsByIds = `-- name: GetInscriptionParentsByIds :many
SELECT id, parents FROM "brc20_inscription_entries" WHERE "id" = ANY($1::text[])
`
type GetInscriptionParentsByIdsRow struct {
Id string
Parents []string
}
func (q *Queries) GetInscriptionParentsByIds(ctx context.Context, inscriptionIds []string) ([]GetInscriptionParentsByIdsRow, error) {
rows, err := q.db.Query(ctx, getInscriptionParentsByIds, inscriptionIds)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetInscriptionParentsByIdsRow
for rows.Next() {
var i GetInscriptionParentsByIdsRow
if err := rows.Scan(&i.Id, &i.Parents); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getInscriptionTransfersInOutPoints = `-- name: GetInscriptionTransfersInOutPoints :many const getInscriptionTransfersInOutPoints = `-- name: GetInscriptionTransfersInOutPoints :many
SELECT it.inscription_id, it.block_height, it.tx_index, it.old_satpoint_tx_hash, it.old_satpoint_out_idx, it.old_satpoint_offset, it.new_satpoint_tx_hash, it.new_satpoint_out_idx, it.new_satpoint_offset, it.new_pkscript, it.new_output_value, it.sent_as_fee, "ie"."content" FROM ( SELECT it.inscription_id, it.block_height, it.tx_index, it.tx_hash, it.from_input_index, it.old_satpoint_tx_hash, it.old_satpoint_out_idx, it.old_satpoint_offset, it.new_satpoint_tx_hash, it.new_satpoint_out_idx, it.new_satpoint_offset, it.new_pkscript, it.new_output_value, it.sent_as_fee, it.transfer_count, "ie"."content" FROM (
SELECT SELECT
unnest($1::text[]) AS "tx_hash", unnest($1::text[]) AS "tx_hash",
unnest($2::int[]) AS "tx_out_idx" unnest($2::int[]) AS "tx_out_idx"
@@ -266,6 +405,8 @@ type GetInscriptionTransfersInOutPointsRow struct {
InscriptionID string InscriptionID string
BlockHeight int32 BlockHeight int32
TxIndex int32 TxIndex int32
TxHash string
FromInputIndex int32
OldSatpointTxHash pgtype.Text OldSatpointTxHash pgtype.Text
OldSatpointOutIdx pgtype.Int4 OldSatpointOutIdx pgtype.Int4
OldSatpointOffset pgtype.Int8 OldSatpointOffset pgtype.Int8
@@ -275,6 +416,7 @@ type GetInscriptionTransfersInOutPointsRow struct {
NewPkscript string NewPkscript string
NewOutputValue int64 NewOutputValue int64
SentAsFee bool SentAsFee bool
TransferCount int32
Content []byte Content []byte
} }
@@ -291,6 +433,8 @@ func (q *Queries) GetInscriptionTransfersInOutPoints(ctx context.Context, arg Ge
&i.InscriptionID, &i.InscriptionID,
&i.BlockHeight, &i.BlockHeight,
&i.TxIndex, &i.TxIndex,
&i.TxHash,
&i.FromInputIndex,
&i.OldSatpointTxHash, &i.OldSatpointTxHash,
&i.OldSatpointOutIdx, &i.OldSatpointOutIdx,
&i.OldSatpointOffset, &i.OldSatpointOffset,
@@ -300,6 +444,7 @@ func (q *Queries) GetInscriptionTransfersInOutPoints(ctx context.Context, arg Ge
&i.NewPkscript, &i.NewPkscript,
&i.NewOutputValue, &i.NewOutputValue,
&i.SentAsFee, &i.SentAsFee,
&i.TransferCount,
&i.Content, &i.Content,
); err != nil { ); err != nil {
return nil, err return nil, err
@@ -312,6 +457,45 @@ func (q *Queries) GetInscriptionTransfersInOutPoints(ctx context.Context, arg Ge
return items, nil return items, nil
} }
const getLatestEventIds = `-- name: GetLatestEventIds :one
WITH "latest_deploy_id" AS (
SELECT "id" FROM "brc20_event_deploys" ORDER BY "id" DESC LIMIT 1
),
"latest_mint_id" AS (
SELECT "id" FROM "brc20_event_mints" ORDER BY "id" DESC LIMIT 1
),
"latest_inscribe_transfer_id" AS (
SELECT "id" FROM "brc20_event_inscribe_transfers" ORDER BY "id" DESC LIMIT 1
),
"latest_transfer_transfer_id" AS (
SELECT "id" FROM "brc20_event_transfer_transfers" ORDER BY "id" DESC LIMIT 1
)
SELECT
(SELECT "id" FROM "latest_deploy_id") AS "event_deploy_id",
(SELECT "id" FROM "latest_mint_id") AS "event_mint_id",
(SELECT "id" FROM "latest_inscribe_transfer_id") AS "event_inscribe_transfer_id",
(SELECT "id" FROM "latest_transfer_transfer_id") AS "event_transfer_transfer_id"
`
type GetLatestEventIdsRow struct {
EventDeployID int64
EventMintID int64
EventInscribeTransferID int64
EventTransferTransferID int64
}
func (q *Queries) GetLatestEventIds(ctx context.Context) (GetLatestEventIdsRow, error) {
row := q.db.QueryRow(ctx, getLatestEventIds)
var i GetLatestEventIdsRow
err := row.Scan(
&i.EventDeployID,
&i.EventMintID,
&i.EventInscribeTransferID,
&i.EventTransferTransferID,
)
return i, err
}
const getLatestIndexedBlock = `-- name: GetLatestIndexedBlock :one const getLatestIndexedBlock = `-- name: GetLatestIndexedBlock :one
SELECT height, hash, event_hash, cumulative_event_hash FROM "brc20_indexed_blocks" ORDER BY "height" DESC LIMIT 1 SELECT height, hash, event_hash, cumulative_event_hash FROM "brc20_indexed_blocks" ORDER BY "height" DESC LIMIT 1
` `

View File

@@ -83,6 +83,7 @@ type Brc20EventTransferTransfer struct {
ToPkscript string ToPkscript string
ToSatpoint string ToSatpoint string
ToOutputIndex int32 ToOutputIndex int32
SpentAsFee bool
Amount pgtype.Numeric Amount pgtype.Numeric
} }
@@ -130,6 +131,8 @@ type Brc20InscriptionTransfer struct {
InscriptionID string InscriptionID string
BlockHeight int32 BlockHeight int32
TxIndex int32 TxIndex int32
TxHash string
FromInputIndex int32
OldSatpointTxHash pgtype.Text OldSatpointTxHash pgtype.Text
OldSatpointOutIdx pgtype.Int4 OldSatpointOutIdx pgtype.Int4
OldSatpointOffset pgtype.Int8 OldSatpointOffset pgtype.Int8
@@ -139,6 +142,7 @@ type Brc20InscriptionTransfer struct {
NewPkscript string NewPkscript string
NewOutputValue int64 NewOutputValue int64
SentAsFee bool SentAsFee bool
TransferCount int32
} }
type Brc20ProcessorStat struct { type Brc20ProcessorStat struct {

View File

@@ -257,6 +257,10 @@ func mapInscriptionTransferModelToType(src gen.GetInscriptionTransfersInOutPoint
if err != nil { if err != nil {
return entity.InscriptionTransfer{}, errors.Wrap(err, "invalid inscription id") return entity.InscriptionTransfer{}, errors.Wrap(err, "invalid inscription id")
} }
txHash, err := chainhash.NewHashFromStr(src.TxHash)
if err != nil {
return entity.InscriptionTransfer{}, errors.Wrap(err, "invalid tx hash")
}
var oldSatPoint, newSatPoint ordinals.SatPoint var oldSatPoint, newSatPoint ordinals.SatPoint
if src.OldSatpointTxHash.Valid { if src.OldSatpointTxHash.Valid {
if !src.OldSatpointOutIdx.Valid || !src.OldSatpointOffset.Valid { if !src.OldSatpointOutIdx.Valid || !src.OldSatpointOffset.Valid {
@@ -299,12 +303,15 @@ func mapInscriptionTransferModelToType(src gen.GetInscriptionTransfersInOutPoint
InscriptionId: inscriptionId, InscriptionId: inscriptionId,
BlockHeight: uint64(src.BlockHeight), BlockHeight: uint64(src.BlockHeight),
TxIndex: uint32(src.TxIndex), TxIndex: uint32(src.TxIndex),
TxHash: *txHash,
FromInputIndex: uint32(src.FromInputIndex),
Content: src.Content, Content: src.Content,
OldSatPoint: oldSatPoint, OldSatPoint: oldSatPoint,
NewSatPoint: newSatPoint, NewSatPoint: newSatPoint,
NewPkScript: newPkScript, NewPkScript: newPkScript,
NewOutputValue: uint64(src.NewOutputValue), NewOutputValue: uint64(src.NewOutputValue),
SentAsFee: src.SentAsFee, SentAsFee: src.SentAsFee,
TransferCount: uint32(src.TransferCount),
}, nil }, nil
} }
@@ -313,6 +320,8 @@ func mapInscriptionTransferTypeToParams(src entity.InscriptionTransfer) gen.Crea
InscriptionID: src.InscriptionId.String(), InscriptionID: src.InscriptionId.String(),
BlockHeight: int32(src.BlockHeight), BlockHeight: int32(src.BlockHeight),
TxIndex: int32(src.TxIndex), TxIndex: int32(src.TxIndex),
TxHash: src.TxHash.String(),
FromInputIndex: int32(src.FromInputIndex),
OldSatpointTxHash: lo.Ternary(src.OldSatPoint != ordinals.SatPoint{}, pgtype.Text{String: src.OldSatPoint.OutPoint.Hash.String(), Valid: true}, pgtype.Text{}), OldSatpointTxHash: lo.Ternary(src.OldSatPoint != ordinals.SatPoint{}, pgtype.Text{String: src.OldSatPoint.OutPoint.Hash.String(), Valid: true}, pgtype.Text{}),
OldSatpointOutIdx: lo.Ternary(src.OldSatPoint != ordinals.SatPoint{}, pgtype.Int4{Int32: int32(src.OldSatPoint.OutPoint.Index), Valid: true}, pgtype.Int4{}), OldSatpointOutIdx: lo.Ternary(src.OldSatPoint != ordinals.SatPoint{}, pgtype.Int4{Int32: int32(src.OldSatPoint.OutPoint.Index), Valid: true}, pgtype.Int4{}),
OldSatpointOffset: lo.Ternary(src.OldSatPoint != ordinals.SatPoint{}, pgtype.Int8{Int64: int64(src.OldSatPoint.Offset), Valid: true}, pgtype.Int8{}), OldSatpointOffset: lo.Ternary(src.OldSatPoint != ordinals.SatPoint{}, pgtype.Int8{Int64: int64(src.OldSatPoint.Offset), Valid: true}, pgtype.Int8{}),
@@ -322,6 +331,7 @@ func mapInscriptionTransferTypeToParams(src entity.InscriptionTransfer) gen.Crea
NewPkscript: hex.EncodeToString(src.NewPkScript), NewPkscript: hex.EncodeToString(src.NewPkScript),
NewOutputValue: int64(src.NewOutputValue), NewOutputValue: int64(src.NewOutputValue),
SentAsFee: src.SentAsFee, SentAsFee: src.SentAsFee,
TransferCount: int32(src.TransferCount),
} }
} }
@@ -345,7 +355,7 @@ func mapEventDeployModelToType(src gen.Brc20EventDeploy) (entity.EventDeploy, er
return entity.EventDeploy{ return entity.EventDeploy{
Id: uint64(src.Id), Id: uint64(src.Id),
InscriptionId: inscriptionId, InscriptionId: inscriptionId,
InscriptionNumber: uint64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: *txHash, TxHash: *txHash,
@@ -368,7 +378,7 @@ func mapEventDeployTypeToParams(src entity.EventDeploy) (gen.CreateEventDeploysP
} }
return gen.CreateEventDeploysParams{ return gen.CreateEventDeploysParams{
InscriptionID: src.InscriptionId.String(), InscriptionID: src.InscriptionId.String(),
InscriptionNumber: int64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: src.TxHash.String(), TxHash: src.TxHash.String(),
@@ -412,7 +422,7 @@ func mapEventMintModelToType(src gen.Brc20EventMint) (entity.EventMint, error) {
return entity.EventMint{ return entity.EventMint{
Id: uint64(src.Id), Id: uint64(src.Id),
InscriptionId: inscriptionId, InscriptionId: inscriptionId,
InscriptionNumber: uint64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: *txHash, TxHash: *txHash,
@@ -437,7 +447,7 @@ func mapEventMintTypeToParams(src entity.EventMint) (gen.CreateEventMintsParams,
} }
return gen.CreateEventMintsParams{ return gen.CreateEventMintsParams{
InscriptionID: src.InscriptionId.String(), InscriptionID: src.InscriptionId.String(),
InscriptionNumber: int64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: src.TxHash.String(), TxHash: src.TxHash.String(),
@@ -471,7 +481,7 @@ func mapEventInscribeTransferModelToType(src gen.Brc20EventInscribeTransfer) (en
return entity.EventInscribeTransfer{ return entity.EventInscribeTransfer{
Id: uint64(src.Id), Id: uint64(src.Id),
InscriptionId: inscriptionId, InscriptionId: inscriptionId,
InscriptionNumber: uint64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: *txHash, TxHash: *txHash,
@@ -493,7 +503,7 @@ func mapEventInscribeTransferTypeToParams(src entity.EventInscribeTransfer) (gen
} }
return gen.CreateEventInscribeTransfersParams{ return gen.CreateEventInscribeTransfersParams{
InscriptionID: src.InscriptionId.String(), InscriptionID: src.InscriptionId.String(),
InscriptionNumber: int64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: src.TxHash.String(), TxHash: src.TxHash.String(),
@@ -536,7 +546,7 @@ func mapEventTransferTransferModelToType(src gen.Brc20EventTransferTransfer) (en
return entity.EventTransferTransfer{ return entity.EventTransferTransfer{
Id: uint64(src.Id), Id: uint64(src.Id),
InscriptionId: inscriptionId, InscriptionId: inscriptionId,
InscriptionNumber: uint64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: *txHash, TxHash: *txHash,
@@ -549,6 +559,7 @@ func mapEventTransferTransferModelToType(src gen.Brc20EventTransferTransfer) (en
ToPkScript: toPkScript, ToPkScript: toPkScript,
ToSatPoint: toSatPoint, ToSatPoint: toSatPoint,
ToOutputIndex: uint32(src.ToOutputIndex), ToOutputIndex: uint32(src.ToOutputIndex),
SpentAsFee: src.SpentAsFee,
Amount: decimalFromNumeric(src.Amount).Decimal, Amount: decimalFromNumeric(src.Amount).Decimal,
}, nil }, nil
} }
@@ -560,7 +571,7 @@ func mapEventTransferTransferTypeToParams(src entity.EventTransferTransfer) (gen
} }
return gen.CreateEventTransferTransfersParams{ return gen.CreateEventTransferTransfersParams{
InscriptionID: src.InscriptionId.String(), InscriptionID: src.InscriptionId.String(),
InscriptionNumber: int64(src.InscriptionNumber), InscriptionNumber: src.InscriptionNumber,
Tick: src.Tick, Tick: src.Tick,
OriginalTick: src.OriginalTick, OriginalTick: src.OriginalTick,
TxHash: src.TxHash.String(), TxHash: src.TxHash.String(),
@@ -573,6 +584,21 @@ func mapEventTransferTransferTypeToParams(src entity.EventTransferTransfer) (gen
ToPkscript: hex.EncodeToString(src.ToPkScript), ToPkscript: hex.EncodeToString(src.ToPkScript),
ToSatpoint: src.ToSatPoint.String(), ToSatpoint: src.ToSatPoint.String(),
ToOutputIndex: int32(src.ToOutputIndex), ToOutputIndex: int32(src.ToOutputIndex),
SpentAsFee: src.SpentAsFee,
Amount: numericFromDecimal(src.Amount), Amount: numericFromDecimal(src.Amount),
}, nil }, nil
} }
func mapBalanceModelToType(src gen.Brc20Balance) (entity.Balance, error) {
pkScript, err := hex.DecodeString(src.Pkscript)
if err != nil {
return entity.Balance{}, errors.Wrap(err, "failed to parse pkscript")
}
return entity.Balance{
PkScript: pkScript,
Tick: src.Tick,
BlockHeight: uint64(src.BlockHeight),
OverallBalance: decimalFromNumeric(src.OverallBalance).Decimal,
AvailableBalance: decimalFromNumeric(src.AvailableBalance).Decimal,
}, nil
}

View File

@@ -40,10 +40,18 @@ type Processor struct {
// cache // cache
outPointValueCache *lru.Cache[wire.OutPoint, uint64] outPointValueCache *lru.Cache[wire.OutPoint, uint64]
// flush buffers // flush buffers - inscription states
newInscriptionTransfers []*entity.InscriptionTransfer newInscriptionTransfers []*entity.InscriptionTransfer
newInscriptionEntries map[ordinals.InscriptionId]*ordinals.InscriptionEntry newInscriptionEntries map[ordinals.InscriptionId]*ordinals.InscriptionEntry
newInscriptionEntryStates map[ordinals.InscriptionId]*ordinals.InscriptionEntry newInscriptionEntryStates map[ordinals.InscriptionId]*ordinals.InscriptionEntry
// flush buffers - brc20 states
newTickEntries map[string]*entity.TickEntry
newTickEntryStates map[string]*entity.TickEntry
newEventDeploys []*entity.EventDeploy
newEventMints []*entity.EventMint
newEventInscribeTransfers []*entity.EventInscribeTransfer
newEventTransferTransfers []*entity.EventTransferTransfer
newBalances map[string]map[string]*entity.Balance
} }
// TODO: move this to config // TODO: move this to config
@@ -73,6 +81,14 @@ func NewProcessor(brc20Dg datagateway.BRC20DataGateway, indexerInfoDg datagatewa
newInscriptionTransfers: make([]*entity.InscriptionTransfer, 0), newInscriptionTransfers: make([]*entity.InscriptionTransfer, 0),
newInscriptionEntries: make(map[ordinals.InscriptionId]*ordinals.InscriptionEntry), newInscriptionEntries: make(map[ordinals.InscriptionId]*ordinals.InscriptionEntry),
newInscriptionEntryStates: make(map[ordinals.InscriptionId]*ordinals.InscriptionEntry), newInscriptionEntryStates: make(map[ordinals.InscriptionId]*ordinals.InscriptionEntry),
newTickEntries: make(map[string]*entity.TickEntry),
newTickEntryStates: make(map[string]*entity.TickEntry),
newEventDeploys: make([]*entity.EventDeploy, 0),
newEventMints: make([]*entity.EventMint, 0),
newEventInscribeTransfers: make([]*entity.EventInscribeTransfer, 0),
newEventTransferTransfers: make([]*entity.EventTransferTransfer, 0),
newBalances: make(map[string]map[string]*entity.Balance),
}, nil }, nil
} }

View File

@@ -1,13 +1,17 @@
package brc20 package brc20
import ( import (
"bytes"
"context" "context"
"encoding/hex"
"time" "time"
"github.com/cockroachdb/errors" "github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/core/types" "github.com/gaze-network/indexer-network/core/types"
"github.com/gaze-network/indexer-network/modules/brc20/internal/brc20" "github.com/gaze-network/indexer-network/modules/brc20/internal/brc20"
"github.com/gaze-network/indexer-network/modules/brc20/internal/datagateway"
"github.com/gaze-network/indexer-network/modules/brc20/internal/entity" "github.com/gaze-network/indexer-network/modules/brc20/internal/entity"
"github.com/gaze-network/indexer-network/modules/brc20/internal/ordinals"
"github.com/gaze-network/indexer-network/pkg/logger" "github.com/gaze-network/indexer-network/pkg/logger"
"github.com/gaze-network/indexer-network/pkg/logger/slogx" "github.com/gaze-network/indexer-network/pkg/logger/slogx"
"github.com/samber/lo" "github.com/samber/lo"
@@ -25,26 +29,93 @@ func (p *Processor) processBRC20States(ctx context.Context, transfers []*entity.
payloads = append(payloads, payload) payloads = append(payloads, payload)
ticks[payload.Tick] = struct{}{} ticks[payload.Tick] = struct{}{}
} }
entries, err := p.getTickEntriesByTicks(ctx, lo.Keys(ticks)) // TODO: concurrently fetch from db to optimize speed
tickEntries, err := p.brc20Dg.GetTickEntriesByTicks(ctx, lo.Keys(ticks))
if err != nil { if err != nil {
return errors.Wrap(err, "failed to get inscription entries by ids") return errors.Wrap(err, "failed to get inscription entries by ids")
} }
// preload required data to reduce individual data fetching during process
inscriptionIds := make([]ordinals.InscriptionId, 0)
inscriptionIdsToFetchParent := make([]ordinals.InscriptionId, 0)
inscriptionIdsToFetchEventInscribeTransfer := make([]ordinals.InscriptionId, 0)
balancesToFetch := make([]datagateway.GetBalancesBatchAtHeightQuery, 0) // pkscript -> tick -> struct{}
for _, payload := range payloads {
inscriptionIds = append(inscriptionIds, payload.Transfer.InscriptionId)
if payload.Op == brc20.OperationMint {
// preload parent id to validate mint events with self mint
if entry := tickEntries[payload.Tick]; entry.IsSelfMint {
inscriptionIdsToFetchParent = append(inscriptionIdsToFetchParent, payload.Transfer.InscriptionId)
}
}
if payload.Op == brc20.OperationTransfer {
if payload.Transfer.OldSatPoint == (ordinals.SatPoint{}) {
// preload balance to validate inscribe transfer event
balancesToFetch = append(balancesToFetch, datagateway.GetBalancesBatchAtHeightQuery{
PkScriptHex: hex.EncodeToString(payload.Transfer.NewPkScript),
Tick: payload.Tick,
})
} else {
// preload inscribe-transfer events to validate transfer-transfer event
inscriptionIdsToFetchEventInscribeTransfer = append(inscriptionIdsToFetchEventInscribeTransfer, payload.Transfer.InscriptionId)
}
}
}
inscriptionIdsToNumber, err := p.getInscriptionNumbersByIds(ctx, lo.Uniq(inscriptionIds))
if err != nil {
return errors.Wrap(err, "failed to get inscription numbers by ids")
}
inscriptionIdsToParent, err := p.getInscriptionParentsByIds(ctx, lo.Uniq(inscriptionIdsToFetchParent))
if err != nil {
return errors.Wrap(err, "failed to get inscription parents by ids")
}
latestEventId, err := p.brc20Dg.GetLatestEventId(ctx)
if err != nil {
return errors.Wrap(err, "failed to get latest event id")
}
// pkscript -> tick -> balance
balances, err := p.brc20Dg.GetBalancesBatchAtHeight(ctx, uint64(blockHeader.Height-1), balancesToFetch)
if err != nil {
return errors.Wrap(err, "failed to get balances batch at height")
}
eventInscribeTransfers, err := p.brc20Dg.GetEventInscribeTransfersByInscriptionIds(ctx, lo.Uniq(inscriptionIdsToFetchEventInscribeTransfer))
if err != nil {
return errors.Wrap(err, "failed to get event inscribe transfers by inscription ids")
}
newTickEntries := make(map[string]*entity.TickEntry) newTickEntries := make(map[string]*entity.TickEntry)
newTickEntryStates := make(map[string]*entity.TickEntry) newTickEntryStates := make(map[string]*entity.TickEntry)
// newDeployEvents := make([]*entity.EventDeploy, 0) newEventDeploys := make([]*entity.EventDeploy, 0)
// newMintEvents := make([]*entity.EventMint, 0) newEventMints := make([]*entity.EventMint, 0)
// newTransferEvents := make([]*entity.EventTransfer, 0) newEventInscribeTransfers := make([]*entity.EventInscribeTransfer, 0)
newEventTransferTransfers := make([]*entity.EventTransferTransfer, 0)
newBalances := make(map[string]map[string]*entity.Balance)
for _, payload := range payloads { for _, payload := range payloads {
entry := entries[payload.Tick] tickEntry := tickEntries[payload.Tick]
if payload.Transfer.SentAsFee && payload.Transfer.OldSatPoint == (ordinals.SatPoint{}) {
logger.DebugContext(ctx, "found inscription inscribed as fee, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
)
continue
}
switch payload.Op { switch payload.Op {
case brc20.OperationDeploy: case brc20.OperationDeploy:
if entry != nil { if payload.Transfer.TransferCount > 1 {
logger.DebugContext(ctx, "found deploy inscription but it is already used, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Uint32("transferCount", payload.Transfer.TransferCount),
)
continue
}
if tickEntry != nil {
logger.DebugContext(ctx, "found deploy inscription but tick already exists, skipping...", logger.DebugContext(ctx, "found deploy inscription but tick already exists, skipping...",
slogx.String("tick", payload.Tick), slogx.String("tick", payload.Tick),
slogx.Stringer("entryInscriptionId", entry.DeployInscriptionId), slogx.Stringer("entryInscriptionId", tickEntry.DeployInscriptionId),
slogx.Stringer("currentInscriptionId", payload.Transfer.InscriptionId), slogx.Stringer("currentInscriptionId", payload.Transfer.InscriptionId),
) )
continue continue
@@ -58,7 +129,7 @@ func (p *Processor) processBRC20States(ctx context.Context, transfers []*entity.
IsSelfMint: payload.SelfMint, IsSelfMint: payload.SelfMint,
DeployInscriptionId: payload.Transfer.InscriptionId, DeployInscriptionId: payload.Transfer.InscriptionId,
DeployedAt: blockHeader.Timestamp, DeployedAt: blockHeader.Timestamp,
DeployedAtHeight: uint64(blockHeader.Height), DeployedAtHeight: payload.Transfer.BlockHeight,
MintedAmount: decimal.Zero, MintedAmount: decimal.Zero,
BurnedAmount: decimal.Zero, BurnedAmount: decimal.Zero,
CompletedAt: time.Time{}, CompletedAt: time.Time{},
@@ -67,57 +138,335 @@ func (p *Processor) processBRC20States(ctx context.Context, transfers []*entity.
newTickEntries[payload.Tick] = tickEntry newTickEntries[payload.Tick] = tickEntry
newTickEntryStates[payload.Tick] = tickEntry newTickEntryStates[payload.Tick] = tickEntry
// update entries for other operations in same block // update entries for other operations in same block
entries[payload.Tick] = tickEntry tickEntries[payload.Tick] = tickEntry
// TODO: handle deploy action newEventDeploys = append(newEventDeploys, &entity.EventDeploy{
Id: latestEventId + 1,
InscriptionId: payload.Transfer.InscriptionId,
InscriptionNumber: inscriptionIdsToNumber[payload.Transfer.InscriptionId],
Tick: payload.Tick,
OriginalTick: payload.OriginalTick,
TxHash: payload.Transfer.TxHash,
BlockHeight: payload.Transfer.BlockHeight,
TxIndex: payload.Transfer.TxIndex,
Timestamp: blockHeader.Timestamp,
PkScript: payload.Transfer.NewPkScript,
SatPoint: payload.Transfer.NewSatPoint,
TotalSupply: payload.Max,
Decimals: payload.Dec,
LimitPerMint: payload.Lim,
IsSelfMint: payload.SelfMint,
})
latestEventId++
case brc20.OperationMint: case brc20.OperationMint:
if entry == nil { if payload.Transfer.TransferCount > 1 {
logger.DebugContext(ctx, "found mint inscription but it is already used, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Uint32("transferCount", payload.Transfer.TransferCount),
)
continue
}
if tickEntry == nil {
logger.DebugContext(ctx, "found mint inscription but tick does not exist, skipping...", logger.DebugContext(ctx, "found mint inscription but tick does not exist, skipping...",
slogx.String("tick", payload.Tick), slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId), slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
) )
continue continue
} }
if -payload.Amt.Exponent() > int32(entry.Decimals) { if -payload.Amt.Exponent() > int32(tickEntry.Decimals) {
logger.DebugContext(ctx, "found mint inscription but amount has invalid decimals, skipping...", logger.DebugContext(ctx, "found mint inscription but amount has invalid decimals, skipping...",
slogx.String("tick", payload.Tick), slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId), slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Stringer("amount", payload.Amt), slogx.Stringer("amount", payload.Amt),
slogx.Uint16("entryDecimals", entry.Decimals), slogx.Uint16("entryDecimals", tickEntry.Decimals),
slogx.Int32("payloadDecimals", -payload.Amt.Exponent()), slogx.Int32("payloadDecimals", -payload.Amt.Exponent()),
) )
continue continue
} }
// TODO: handle mint action if tickEntry.MintedAmount.GreaterThanOrEqual(tickEntry.TotalSupply) {
logger.DebugContext(ctx, "found mint inscription but total supply is reached, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Stringer("mintedAmount", tickEntry.MintedAmount),
slogx.Stringer("totalSupply", tickEntry.TotalSupply),
)
continue
}
if payload.Amt.GreaterThan(tickEntry.LimitPerMint) {
logger.DebugContext(ctx, "found mint inscription but amount exceeds limit per mint, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Stringer("amount", payload.Amt),
slogx.Stringer("limitPerMint", tickEntry.LimitPerMint),
)
continue
}
mintableAmount := tickEntry.TotalSupply.Sub(tickEntry.MintedAmount)
if payload.Amt.GreaterThan(mintableAmount) {
payload.Amt = mintableAmount
}
var parentId *ordinals.InscriptionId
if tickEntry.IsSelfMint {
parentIdValue, ok := inscriptionIdsToParent[payload.Transfer.InscriptionId]
if !ok {
logger.DebugContext(ctx, "found mint inscription for self mint tick, but it does not have a parent, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
)
continue
}
if parentIdValue != tickEntry.DeployInscriptionId {
logger.DebugContext(ctx, "found mint inscription for self mint tick, but parent id does not match deploy inscription id, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Stringer("parentId", parentId),
slogx.Stringer("deployInscriptionId", tickEntry.DeployInscriptionId),
)
}
parentId = &parentIdValue
}
tickEntry.MintedAmount = tickEntry.MintedAmount.Add(payload.Amt)
if tickEntry.MintedAmount.GreaterThanOrEqual(tickEntry.TotalSupply) {
tickEntry.CompletedAt = blockHeader.Timestamp
tickEntry.CompletedAtHeight = payload.Transfer.BlockHeight
}
newTickEntryStates[payload.Tick] = tickEntry
newEventMints = append(newEventMints, &entity.EventMint{
Id: latestEventId + 1,
InscriptionId: payload.Transfer.InscriptionId,
InscriptionNumber: inscriptionIdsToNumber[payload.Transfer.InscriptionId],
Tick: payload.Tick,
OriginalTick: payload.OriginalTick,
TxHash: payload.Transfer.TxHash,
BlockHeight: payload.Transfer.BlockHeight,
TxIndex: payload.Transfer.TxIndex,
Timestamp: blockHeader.Timestamp,
PkScript: payload.Transfer.NewPkScript,
SatPoint: payload.Transfer.NewSatPoint,
Amount: payload.Amt,
ParentId: parentId,
})
latestEventId++
case brc20.OperationTransfer: case brc20.OperationTransfer:
if entry == nil { if payload.Transfer.TransferCount > 2 {
logger.DebugContext(ctx, "found mint inscription but it is already used, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Uint32("transferCount", payload.Transfer.TransferCount),
)
continue
}
if tickEntry == nil {
logger.DebugContext(ctx, "found transfer inscription but tick does not exist, skipping...", logger.DebugContext(ctx, "found transfer inscription but tick does not exist, skipping...",
slogx.String("tick", payload.Tick), slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId), slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
) )
continue continue
} }
if -payload.Amt.Exponent() > int32(entry.Decimals) { if -payload.Amt.Exponent() > int32(tickEntry.Decimals) {
logger.DebugContext(ctx, "found transfer inscription but amount has invalid decimals, skipping...", logger.DebugContext(ctx, "found transfer inscription but amount has invalid decimals, skipping...",
slogx.String("tick", payload.Tick), slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId), slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Stringer("amount", payload.Amt), slogx.Stringer("amount", payload.Amt),
slogx.Uint16("entryDecimals", entry.Decimals), slogx.Uint16("entryDecimals", tickEntry.Decimals),
slogx.Int32("payloadDecimals", -payload.Amt.Exponent()), slogx.Int32("payloadDecimals", -payload.Amt.Exponent()),
) )
continue continue
} }
// TODO: handle transfer action
if payload.Transfer.OldSatPoint == (ordinals.SatPoint{}) {
// inscribe transfer event
pkScriptHex := hex.EncodeToString(payload.Transfer.NewPkScript)
balance, ok := balances[pkScriptHex][payload.Tick]
if !ok {
balance = &entity.Balance{
PkScript: payload.Transfer.NewPkScript,
Tick: payload.Tick,
BlockHeight: uint64(blockHeader.Height - 1),
OverallBalance: decimal.Zero, // defaults balance to zero if not found
AvailableBalance: decimal.Zero,
}
}
if payload.Amt.GreaterThan(balance.AvailableBalance) {
logger.DebugContext(ctx, "found transfer inscription but amount exceeds available balance, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.Stringer("amount", payload.Amt),
slogx.Stringer("availableBalance", balance.AvailableBalance),
)
continue
}
// update balance state
balance.BlockHeight = uint64(blockHeader.Height)
balance.AvailableBalance = balance.AvailableBalance.Sub(payload.Amt)
if _, ok := balances[pkScriptHex]; !ok {
balances[pkScriptHex] = make(map[string]*entity.Balance)
}
balances[pkScriptHex][payload.Tick] = balance
if _, ok := newBalances[pkScriptHex]; !ok {
newBalances[pkScriptHex] = make(map[string]*entity.Balance)
}
newBalances[pkScriptHex][payload.Tick] = &entity.Balance{}
event := &entity.EventInscribeTransfer{
Id: latestEventId + 1,
InscriptionId: payload.Transfer.InscriptionId,
InscriptionNumber: inscriptionIdsToNumber[payload.Transfer.InscriptionId],
Tick: payload.Tick,
OriginalTick: payload.OriginalTick,
TxHash: payload.Transfer.TxHash,
BlockHeight: payload.Transfer.BlockHeight,
TxIndex: payload.Transfer.TxIndex,
Timestamp: blockHeader.Timestamp,
PkScript: payload.Transfer.NewPkScript,
SatPoint: payload.Transfer.NewSatPoint,
OutputIndex: payload.Transfer.NewSatPoint.OutPoint.Index,
SatsAmount: payload.Transfer.NewOutputValue,
Amount: payload.Amt,
}
latestEventId++
eventInscribeTransfers[payload.Transfer.InscriptionId] = event
newEventInscribeTransfers = append(newEventInscribeTransfers, event)
} else {
// transfer transfer event
inscribeTransfer, ok := eventInscribeTransfers[payload.Transfer.InscriptionId]
if !ok {
logger.DebugContext(ctx, "found transfer transfer event but inscribe transfer does not exist, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
)
continue
}
if payload.Transfer.SentAsFee {
// return balance to sender
fromPkScriptHex := hex.EncodeToString(inscribeTransfer.PkScript)
fromBalance, ok := balances[fromPkScriptHex][payload.Tick]
if !ok {
logger.DebugContext(ctx, "found transfer transfer event but from balance does not exist, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.String("pkScript", fromPkScriptHex),
)
continue
}
fromBalance.BlockHeight = uint64(blockHeader.Height)
fromBalance.AvailableBalance = fromBalance.AvailableBalance.Sub(payload.Amt)
if _, ok := balances[fromPkScriptHex]; !ok {
balances[fromPkScriptHex] = make(map[string]*entity.Balance)
}
balances[fromPkScriptHex][payload.Tick] = fromBalance
if _, ok := newBalances[fromPkScriptHex]; !ok {
newBalances[fromPkScriptHex] = make(map[string]*entity.Balance)
}
newBalances[fromPkScriptHex][payload.Tick] = fromBalance
newEventTransferTransfers = append(newEventTransferTransfers, &entity.EventTransferTransfer{
Id: latestEventId + 1,
InscriptionId: payload.Transfer.InscriptionId,
InscriptionNumber: inscriptionIdsToNumber[payload.Transfer.InscriptionId],
Tick: payload.Tick,
OriginalTick: payload.OriginalTick,
TxHash: payload.Transfer.TxHash,
BlockHeight: payload.Transfer.BlockHeight,
TxIndex: payload.Transfer.TxIndex,
Timestamp: blockHeader.Timestamp,
FromPkScript: inscribeTransfer.PkScript,
FromSatPoint: inscribeTransfer.SatPoint,
FromInputIndex: payload.Transfer.FromInputIndex,
ToPkScript: payload.Transfer.NewPkScript,
ToSatPoint: payload.Transfer.NewSatPoint,
ToOutputIndex: payload.Transfer.NewSatPoint.OutPoint.Index,
SpentAsFee: true,
Amount: payload.Amt,
})
} else {
// subtract balance from sender
fromPkScriptHex := hex.EncodeToString(inscribeTransfer.PkScript)
fromBalance, ok := balances[fromPkScriptHex][payload.Tick]
if !ok {
logger.DebugContext(ctx, "found transfer transfer event but from balance does not exist, skipping...",
slogx.String("tick", payload.Tick),
slogx.Stringer("inscriptionId", payload.Transfer.InscriptionId),
slogx.String("pkScript", fromPkScriptHex),
)
continue
}
fromBalance.BlockHeight = uint64(blockHeader.Height)
fromBalance.OverallBalance = fromBalance.OverallBalance.Sub(payload.Amt)
if _, ok := balances[fromPkScriptHex]; !ok {
balances[fromPkScriptHex] = make(map[string]*entity.Balance)
}
balances[fromPkScriptHex][payload.Tick] = fromBalance
if _, ok := newBalances[fromPkScriptHex]; !ok {
newBalances[fromPkScriptHex] = make(map[string]*entity.Balance)
}
newBalances[fromPkScriptHex][payload.Tick] = fromBalance
// add balance to receiver
if bytes.Equal(payload.Transfer.NewPkScript, []byte{0x6a}) {
// burn if sent to OP_RETURN
tickEntry.BurnedAmount = tickEntry.BurnedAmount.Add(payload.Amt)
tickEntries[payload.Tick] = tickEntry
newTickEntryStates[payload.Tick] = tickEntry
} else {
toPkScriptHex := hex.EncodeToString(payload.Transfer.NewPkScript)
toBalance, ok := balances[toPkScriptHex][payload.Tick]
if !ok {
toBalance = &entity.Balance{
PkScript: payload.Transfer.NewPkScript,
Tick: payload.Tick,
BlockHeight: uint64(blockHeader.Height),
OverallBalance: decimal.Zero, // defaults balance to zero if not found
AvailableBalance: decimal.Zero,
}
}
toBalance.BlockHeight = uint64(blockHeader.Height)
toBalance.OverallBalance = toBalance.OverallBalance.Add(payload.Amt)
toBalance.AvailableBalance = toBalance.AvailableBalance.Add(payload.Amt)
if _, ok := balances[toPkScriptHex]; !ok {
balances[toPkScriptHex] = make(map[string]*entity.Balance)
}
balances[toPkScriptHex][payload.Tick] = toBalance
if _, ok := newBalances[toPkScriptHex]; !ok {
newBalances[toPkScriptHex] = make(map[string]*entity.Balance)
}
newBalances[toPkScriptHex][payload.Tick] = toBalance
}
newEventTransferTransfers = append(newEventTransferTransfers, &entity.EventTransferTransfer{
Id: latestEventId + 1,
InscriptionId: payload.Transfer.InscriptionId,
InscriptionNumber: inscriptionIdsToNumber[payload.Transfer.InscriptionId],
Tick: payload.Tick,
OriginalTick: payload.OriginalTick,
TxHash: payload.Transfer.TxHash,
BlockHeight: payload.Transfer.BlockHeight,
TxIndex: payload.Transfer.TxIndex,
Timestamp: blockHeader.Timestamp,
FromPkScript: inscribeTransfer.PkScript,
FromSatPoint: inscribeTransfer.SatPoint,
FromInputIndex: payload.Transfer.FromInputIndex,
ToPkScript: payload.Transfer.NewPkScript,
ToSatPoint: payload.Transfer.NewSatPoint,
ToOutputIndex: payload.Transfer.NewSatPoint.OutPoint.Index,
SpentAsFee: false,
Amount: payload.Amt,
})
}
}
} }
} }
p.newTickEntries = newTickEntries
p.newTickEntryStates = newTickEntryStates
p.newEventDeploys = newEventDeploys
p.newEventMints = newEventMints
p.newEventInscribeTransfers = newEventInscribeTransfers
p.newEventTransferTransfers = newEventTransferTransfers
p.newBalances = newBalances
return nil return nil
} }
func (p *Processor) getTickEntriesByTicks(ctx context.Context, ticks []string) (map[string]*entity.TickEntry, error) {
// TODO: get from buffer if exists
result, err := p.brc20Dg.GetTickEntriesByTicks(ctx, ticks)
if err != nil {
return nil, errors.Wrap(err, "failed to get tick entries by ticks")
}
return result, nil
}

View File

@@ -80,6 +80,7 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
OriginOld: &entity.OriginOld{ OriginOld: &entity.OriginOld{
OldSatPoint: satPoint, OldSatPoint: satPoint,
Content: transfer.Content, Content: transfer.Content,
InputIndex: uint32(i),
}, },
}) })
if _, ok := inscribeOffsets[offset]; !ok { if _, ok := inscribeOffsets[offset]; !ok {
@@ -289,17 +290,6 @@ func (p *Processor) processInscriptionTx(ctx context.Context, tx *types.Transact
func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint ordinals.SatPoint, flotsam *entity.Flotsam, sentAsFee bool, tx *types.Transaction, blockHeader types.BlockHeader) error { func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint ordinals.SatPoint, flotsam *entity.Flotsam, sentAsFee bool, tx *types.Transaction, blockHeader types.BlockHeader) error {
txOut := tx.TxOut[newSatPoint.OutPoint.Index] txOut := tx.TxOut[newSatPoint.OutPoint.Index]
if flotsam.OriginOld != nil { if flotsam.OriginOld != nil {
transfer := &entity.InscriptionTransfer{
InscriptionId: flotsam.InscriptionId,
BlockHeight: uint64(flotsam.Tx.BlockHeight), // use flotsam's tx to track tx that initiated the transfer
TxIndex: flotsam.Tx.Index, // use flotsam's tx to track tx that initiated the transfer
Content: flotsam.OriginOld.Content,
OldSatPoint: flotsam.OriginOld.OldSatPoint,
NewSatPoint: newSatPoint,
NewPkScript: txOut.PkScript,
NewOutputValue: uint64(txOut.Value),
SentAsFee: sentAsFee,
}
entry, err := p.getInscriptionEntryById(ctx, flotsam.InscriptionId) entry, err := p.getInscriptionEntryById(ctx, flotsam.InscriptionId)
if err != nil { if err != nil {
// skip inscriptions without entry (likely non-brc20 inscriptions) // skip inscriptions without entry (likely non-brc20 inscriptions)
@@ -309,6 +299,20 @@ func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint o
return errors.Wrap(err, "failed to get inscription entry") return errors.Wrap(err, "failed to get inscription entry")
} }
entry.TransferCount++ entry.TransferCount++
transfer := &entity.InscriptionTransfer{
InscriptionId: flotsam.InscriptionId,
BlockHeight: uint64(flotsam.Tx.BlockHeight), // use flotsam's tx to track tx that initiated the transfer
TxIndex: flotsam.Tx.Index, // use flotsam's tx to track tx that initiated the transfer
TxHash: flotsam.Tx.TxHash,
Content: flotsam.OriginOld.Content,
FromInputIndex: flotsam.OriginOld.InputIndex,
OldSatPoint: flotsam.OriginOld.OldSatPoint,
NewSatPoint: newSatPoint,
NewPkScript: txOut.PkScript,
NewOutputValue: uint64(txOut.Value),
SentAsFee: sentAsFee,
TransferCount: entry.TransferCount,
}
// track transfers even if transfer count exceeds 2 (because we need to check for reinscriptions) // track transfers even if transfer count exceeds 2 (because we need to check for reinscriptions)
p.newInscriptionTransfers = append(p.newInscriptionTransfers, transfer) p.newInscriptionTransfers = append(p.newInscriptionTransfers, transfer)
@@ -337,12 +341,15 @@ func (p *Processor) updateInscriptionLocation(ctx context.Context, newSatPoint o
InscriptionId: flotsam.InscriptionId, InscriptionId: flotsam.InscriptionId,
BlockHeight: uint64(flotsam.Tx.BlockHeight), // use flotsam's tx to track tx that initiated the transfer BlockHeight: uint64(flotsam.Tx.BlockHeight), // use flotsam's tx to track tx that initiated the transfer
TxIndex: flotsam.Tx.Index, // use flotsam's tx to track tx that initiated the transfer TxIndex: flotsam.Tx.Index, // use flotsam's tx to track tx that initiated the transfer
TxHash: flotsam.Tx.TxHash,
Content: origin.Inscription.Content, Content: origin.Inscription.Content,
FromInputIndex: 0, // unused
OldSatPoint: ordinals.SatPoint{}, OldSatPoint: ordinals.SatPoint{},
NewSatPoint: newSatPoint, NewSatPoint: newSatPoint,
NewPkScript: txOut.PkScript, NewPkScript: txOut.PkScript,
NewOutputValue: uint64(txOut.Value), NewOutputValue: uint64(txOut.Value),
SentAsFee: sentAsFee, SentAsFee: sentAsFee,
TransferCount: 1, // count inscription as first transfer
} }
entry := &ordinals.InscriptionEntry{ entry := &ordinals.InscriptionEntry{
Id: flotsam.InscriptionId, Id: flotsam.InscriptionId,
@@ -510,6 +517,58 @@ func (p *Processor) getInscriptionEntriesByIds(ctx context.Context, ids []ordina
return result, nil return result, nil
} }
func (p *Processor) getInscriptionNumbersByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]int64, error) {
// try to get from cache if exists
result := make(map[ordinals.InscriptionId]int64)
idsToFetch := make([]ordinals.InscriptionId, 0)
for _, id := range ids {
if entry, ok := p.newInscriptionEntryStates[id]; ok {
result[id] = int64(entry.Number)
} else {
idsToFetch = append(idsToFetch, id)
}
}
if len(idsToFetch) == 0 {
inscriptions, err := p.brc20Dg.GetInscriptionNumbersByIds(ctx, idsToFetch)
if err != nil {
return nil, errors.Wrap(err, "failed to get inscriptions by outpoint")
}
for id, number := range inscriptions {
result[id] = number
}
}
return result, nil
}
func (p *Processor) getInscriptionParentsByIds(ctx context.Context, ids []ordinals.InscriptionId) (map[ordinals.InscriptionId]ordinals.InscriptionId, error) {
// try to get from cache if exists
result := make(map[ordinals.InscriptionId]ordinals.InscriptionId)
idsToFetch := make([]ordinals.InscriptionId, 0)
for _, id := range ids {
if entry, ok := p.newInscriptionEntryStates[id]; ok {
if entry.Inscription.Parent != nil {
result[id] = *entry.Inscription.Parent
}
} else {
idsToFetch = append(idsToFetch, id)
}
}
if len(idsToFetch) == 0 {
inscriptions, err := p.brc20Dg.GetInscriptionParentsByIds(ctx, idsToFetch)
if err != nil {
return nil, errors.Wrap(err, "failed to get inscriptions by outpoint")
}
for id, parent := range inscriptions {
result[id] = parent
}
}
return result, nil
}
func (p *Processor) getBlockSubsidy(blockHeight uint64) uint64 { func (p *Processor) getBlockSubsidy(blockHeight uint64) uint64 {
return uint64(blockchain.CalcBlockSubsidy(int32(blockHeight), p.network.ChainParams())) return uint64(blockchain.CalcBlockSubsidy(int32(blockHeight), p.network.ChainParams()))
} }