mirror of
https://github.com/alexgo-io/gaze-brc20-indexer.git
synced 2026-04-30 20:43:11 +08:00
feat: implement brc20 inscription dgs and repos
This commit is contained in:
@@ -24,14 +24,15 @@ CREATE TABLE IF NOT EXISTS "brc20_indexed_blocks" (
|
||||
"hash" TEXT NOT NULL,
|
||||
"prev_hash" TEXT NOT NULL,
|
||||
"event_hash" TEXT NOT NULL,
|
||||
"cumulative_event_hash" TEXT NOT NULL,
|
||||
"cumulative_event_hash" TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "brc20_processor_stats" (
|
||||
"block_height" INT NOT NULL PRIMARY KEY,
|
||||
"cursed_inscription_count" INT NOT NULL,
|
||||
"blessed_inscription_count" INT NOT NULL,
|
||||
)
|
||||
"lost_sats" BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "brc20_tickers" (
|
||||
"tick" TEXT NOT NULL PRIMARY KEY, -- lowercase of original_tick
|
||||
@@ -124,30 +125,32 @@ CREATE TABLE IF NOT EXISTS "brc20_inscription_entries" (
|
||||
"parents" TEXT[], -- parent inscription id, 0.14 only supports 1 parent per inscription
|
||||
"pointer" BIGINT,
|
||||
"content" JSONB NOT NULL, -- can use jsonb because we only track brc20 inscriptions
|
||||
"content_type" TEXT NOT NULL,
|
||||
"content_encoding" TEXT,
|
||||
"content_type" TEXT,
|
||||
"cursed" BOOLEAN NOT NULL, -- inscriptions after jubilee are no longer cursed in 0.14, which affects inscription number
|
||||
"cursed_for_brc20" BOOLEAN NOT NULL, -- however, inscriptions that would normally be cursed are still considered cursed for brc20
|
||||
"created_at" TIMESTAMP NOT NULL,
|
||||
"created_at_height" INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "brc20_inscription_states" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
CREATE TABLE IF NOT EXISTS "brc20_inscription_entry_states" (
|
||||
"id" TEXT NOT NULL,
|
||||
"block_height" INT NOT NULL,
|
||||
"transfer_count" INT NOT NULL,
|
||||
PRIMARY KEY ("id", "block_height")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "brc20_inscription_transfers" (
|
||||
"inscription_id" TEXT NOT NULL,
|
||||
"block_height" INT NOT NULL,
|
||||
"old_satpoint_tx_hash" TEXT NOT NULL,
|
||||
"old_satpoint_out_idx" INT NOT NULL,
|
||||
"old_satpoint_offset" BIGINT NOT NULL,
|
||||
"new_satpoint_tx_hash" TEXT NOT NULL,
|
||||
"new_satpoint_out_idx" INT NOT NULL,
|
||||
"new_satpoint_offset" BIGINT NOT NULL,
|
||||
"old_satpoint_tx_hash" TEXT,
|
||||
"old_satpoint_out_idx" INT,
|
||||
"old_satpoint_offset" BIGINT,
|
||||
"new_satpoint_tx_hash" TEXT,
|
||||
"new_satpoint_out_idx" INT,
|
||||
"new_satpoint_offset" BIGINT,
|
||||
"new_pkscript" TEXT NOT NULL,
|
||||
"new_output_value" DECIMAL NOT NULL
|
||||
"new_output_value" BIGINT NOT NULL,
|
||||
"sent_as_fee" BOOLEAN NOT NULL,
|
||||
PRIMARY KEY ("inscription_id", "block_height")
|
||||
);
|
||||
|
||||
26
modules/brc20/database/postgresql/queries/data.sql
Normal file
26
modules/brc20/database/postgresql/queries/data.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- name: GetLatestProcessorStats :one
|
||||
SELECT * FROM "brc20_processor_stats" ORDER BY block_height DESC LIMIT 1;
|
||||
|
||||
-- name: GetInscriptionsInOutPoint :many
|
||||
SELECT * FROM "brc20_inscription_transfers" WHERE "new_satpoint_tx_hash" = @tx_hash AND "new_satpoint_out_idx" = @tx_out_idx;
|
||||
|
||||
-- name: GetInscriptionEntriesByIds :many
|
||||
WITH "states" AS (
|
||||
-- select latest state
|
||||
SELECT DISTINCT ON ("id") * FROM "brc20_inscription_entry_states" WHERE "id" = ANY(@inscription_ids::text[]) ORDER BY "id", "block_height" DESC
|
||||
)
|
||||
SELECT * FROM "brc20_inscription_entries"
|
||||
LEFT JOIN "states" ON "brc20_inscription_entries"."id" = states."id"
|
||||
WHERE "brc20_inscription_entries"."id" = ANY(@inscription_ids::text[]);
|
||||
|
||||
-- name: CreateProcessorStats :exec
|
||||
INSERT INTO "brc20_processor_stats" (block_height, cursed_inscription_count, blessed_inscription_count, lost_sats) VALUES ($1, $2, $3, $4);
|
||||
|
||||
-- name: CreateInscriptionEntries :batchexec
|
||||
INSERT INTO "brc20_inscription_entries" ("id", "number", "sequence_number", "delegate", "metadata", "metaprotocol", "parents", "pointer", "content", "content_encoding", "content_type", "cursed", "cursed_for_brc20", "created_at", "created_at_height") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);
|
||||
|
||||
-- name: CreateInscriptionEntryStates :batchexec
|
||||
INSERT INTO "brc20_inscription_entry_states" ("id", "block_height", "transfer_count") VALUES ($1, $2, $3);
|
||||
|
||||
-- name: CreateInscriptionTransfers :batchexec
|
||||
INSERT INTO "brc20_inscription_transfers" ("inscription_id", "block_height", "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);
|
||||
@@ -23,14 +23,13 @@ type BRC20DataGatewayWithTx interface {
|
||||
|
||||
type BRC20ReaderDataGateway interface {
|
||||
GetProcessorStats(ctx context.Context) (*entity.ProcessorStats, error)
|
||||
|
||||
GetInscriptionsInOutPoint(ctx context.Context, outPoint wire.OutPoint) (map[ordinals.SatPoint]ordinals.InscriptionId, error)
|
||||
GetInscriptionIdsInOutPoint(ctx context.Context, outPoint wire.OutPoint) (map[ordinals.SatPoint][]ordinals.InscriptionId, error)
|
||||
GetInscriptionEntryById(ctx context.Context, id ordinals.InscriptionId) (*ordinals.InscriptionEntry, error)
|
||||
}
|
||||
|
||||
type BRC20WriterDataGateway interface {
|
||||
CreateProcessorStats(ctx context.Context, stats *entity.ProcessorStats) error
|
||||
CreateInscriptionEntries(ctx context.Context, blockHeight uint64, entries []*ordinals.InscriptionEntry) error
|
||||
CreateInscriptionEntryStates(ctx context.Context, blockHeight uint64, entryStates []*ordinals.InscriptionEntry) error
|
||||
CreateInscriptionTransfers(ctx context.Context, transfers []*entity.InscriptionTransfer) error
|
||||
CreateProcessorStats(ctx context.Context, stats *entity.ProcessorStats) error
|
||||
}
|
||||
|
||||
@@ -48,27 +48,29 @@ func (p *Processor) processTx(ctx context.Context, tx *types.Transaction, blockH
|
||||
continue
|
||||
}
|
||||
|
||||
inscriptions, err := p.getInscriptionsInOutPoint(ctx, inputOutPoint)
|
||||
inscriptions, err := p.getInscriptionIdsInOutPoint(ctx, inputOutPoint)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get inscriptions in outpoint")
|
||||
}
|
||||
for satPoint, inscriptionId := range inscriptions {
|
||||
for satPoint, inscriptionIds := range inscriptions {
|
||||
offset := totalInputValue + satPoint.Offset
|
||||
floatingInscriptions = append(floatingInscriptions, &Flotsam{
|
||||
Offset: offset,
|
||||
InscriptionId: inscriptionId,
|
||||
Tx: tx,
|
||||
OriginOld: &OriginOld{
|
||||
OldSatPoint: satPoint,
|
||||
},
|
||||
})
|
||||
if _, ok := inscribeOffsets[offset]; !ok {
|
||||
inscribeOffsets[offset] = &struct {
|
||||
inscriptionId ordinals.InscriptionId
|
||||
count int
|
||||
}{inscriptionId, 0}
|
||||
for _, inscriptionId := range inscriptionIds {
|
||||
floatingInscriptions = append(floatingInscriptions, &Flotsam{
|
||||
Offset: offset,
|
||||
InscriptionId: inscriptionId,
|
||||
Tx: tx,
|
||||
OriginOld: &OriginOld{
|
||||
OldSatPoint: satPoint,
|
||||
},
|
||||
})
|
||||
if _, ok := inscribeOffsets[offset]; !ok {
|
||||
inscribeOffsets[offset] = &struct {
|
||||
inscriptionId ordinals.InscriptionId
|
||||
count int
|
||||
}{inscriptionId, 0}
|
||||
}
|
||||
inscribeOffsets[offset].count++
|
||||
}
|
||||
inscribeOffsets[offset].count++
|
||||
}
|
||||
// offset on output to inscribe new inscriptions from this input
|
||||
offset := totalInputValue
|
||||
@@ -404,8 +406,8 @@ func (p *Processor) getOutPointValues(ctx context.Context, outPoints []wire.OutP
|
||||
return outPointValues, nil
|
||||
}
|
||||
|
||||
func (p *Processor) getInscriptionsInOutPoint(ctx context.Context, outPoint wire.OutPoint) (map[ordinals.SatPoint]ordinals.InscriptionId, error) {
|
||||
inscriptions, err := p.brc20Dg.GetInscriptionsInOutPoint(ctx, outPoint)
|
||||
func (p *Processor) getInscriptionIdsInOutPoint(ctx context.Context, outPoint wire.OutPoint) (map[ordinals.SatPoint][]ordinals.InscriptionId, error) {
|
||||
inscriptions, err := p.brc20Dg.GetInscriptionIdsInOutPoint(ctx, outPoint)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get inscriptions by outpoint")
|
||||
}
|
||||
|
||||
153
modules/brc20/internal/repository/postgres/brc20.go
Normal file
153
modules/brc20/internal/repository/postgres/brc20.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/common/errs"
|
||||
"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/ordinals"
|
||||
"github.com/gaze-network/indexer-network/modules/brc20/internal/repository/postgres/gen"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
var _ datagateway.BRC20DataGateway = (*Repository)(nil)
|
||||
|
||||
func (r *Repository) GetProcessorStats(ctx context.Context) (*entity.ProcessorStats, error) {
|
||||
model, err := r.queries.GetLatestProcessorStats(ctx)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, errors.WithStack(errs.NotFound)
|
||||
}
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
stats := mapProcessorStatsModelToType(model)
|
||||
return &stats, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetInscriptionIdsInOutPoint(ctx context.Context, outPoint wire.OutPoint) (map[ordinals.SatPoint][]ordinals.InscriptionId, error) {
|
||||
models, err := r.queries.GetInscriptionsInOutPoint(ctx, gen.GetInscriptionsInOutPointParams{
|
||||
TxHash: pgtype.Text{String: outPoint.Hash.String(), Valid: true},
|
||||
TxOutIdx: pgtype.Int4{Int32: int32(outPoint.Index), Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
inscriptionIds := make(map[ordinals.SatPoint][]ordinals.InscriptionId)
|
||||
for _, model := range models {
|
||||
// sanity check
|
||||
if !model.NewSatpointTxHash.Valid || !model.NewSatpointOutIdx.Valid || !model.NewSatpointOffset.Valid {
|
||||
return nil, errors.New("invalid satpoint: missing required satpoint fields")
|
||||
}
|
||||
txHash, err := chainhash.NewHashFromStr(model.NewSatpointTxHash.String)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid satpoint: cannot parse txHash")
|
||||
}
|
||||
satPoint := ordinals.SatPoint{
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: *txHash,
|
||||
Index: uint32(model.NewSatpointOutIdx.Int32),
|
||||
},
|
||||
Offset: uint64(model.NewSatpointOffset.Int64),
|
||||
}
|
||||
inscriptionId, err := ordinals.NewInscriptionIdFromString(model.InscriptionID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid inscription id")
|
||||
}
|
||||
inscriptionIds[satPoint] = append(inscriptionIds[satPoint], inscriptionId)
|
||||
}
|
||||
return inscriptionIds, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetInscriptionEntryById(ctx context.Context, id ordinals.InscriptionId) (*ordinals.InscriptionEntry, error) {
|
||||
models, err := r.queries.GetInscriptionEntriesByIds(ctx, []string{id.String()})
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
if len(models) == 0 {
|
||||
return nil, errors.WithStack(errs.NotFound)
|
||||
}
|
||||
if len(models) > 1 {
|
||||
// sanity check
|
||||
panic("multiple inscription entries found for the same id")
|
||||
}
|
||||
inscriptionEntry, err := mapInscriptionEntryModelToType(models[0])
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return &inscriptionEntry, nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateProcessorStats(ctx context.Context, stats *entity.ProcessorStats) error {
|
||||
params := mapProcessorStatsTypeToParams(*stats)
|
||||
if err := r.queries.CreateProcessorStats(ctx, params); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateInscriptionEntries(ctx context.Context, blockHeight uint64, entries []*ordinals.InscriptionEntry) error {
|
||||
inscriptionEntryParams := make([]gen.CreateInscriptionEntriesParams, 0)
|
||||
for _, entry := range entries {
|
||||
params, _, err := mapInscriptionEntryTypeToParams(*entry, blockHeight)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot map inscription entry to create params")
|
||||
}
|
||||
inscriptionEntryParams = append(inscriptionEntryParams, params)
|
||||
}
|
||||
results := r.queries.CreateInscriptionEntries(ctx, inscriptionEntryParams)
|
||||
var execErrors []error
|
||||
results.Exec(func(i int, err error) {
|
||||
if err != nil {
|
||||
execErrors = append(execErrors, err)
|
||||
}
|
||||
})
|
||||
if len(execErrors) > 0 {
|
||||
return errors.Wrap(errors.Join(execErrors...), "error during exec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateInscriptionEntryStates(ctx context.Context, blockHeight uint64, entryStates []*ordinals.InscriptionEntry) error {
|
||||
inscriptionEntryStatesParams := make([]gen.CreateInscriptionEntryStatesParams, 0)
|
||||
for _, entry := range entryStates {
|
||||
_, params, err := mapInscriptionEntryTypeToParams(*entry, blockHeight)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot map inscription entry to create params")
|
||||
}
|
||||
inscriptionEntryStatesParams = append(inscriptionEntryStatesParams, params)
|
||||
}
|
||||
results := r.queries.CreateInscriptionEntryStates(ctx, inscriptionEntryStatesParams)
|
||||
var execErrors []error
|
||||
results.Exec(func(i int, err error) {
|
||||
if err != nil {
|
||||
execErrors = append(execErrors, err)
|
||||
}
|
||||
})
|
||||
if len(execErrors) > 0 {
|
||||
return errors.Wrap(errors.Join(execErrors...), "error during exec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) CreateInscriptionTransfers(ctx context.Context, transfers []*entity.InscriptionTransfer) error {
|
||||
params := lo.Map(transfers, func(transfer *entity.InscriptionTransfer, _ int) gen.CreateInscriptionTransfersParams {
|
||||
return mapInscriptionTransferTypeToParams(*transfer)
|
||||
})
|
||||
results := r.queries.CreateInscriptionTransfers(ctx, params)
|
||||
var execErrors []error
|
||||
results.Exec(func(i int, err error) {
|
||||
if err != nil {
|
||||
execErrors = append(execErrors, err)
|
||||
}
|
||||
})
|
||||
if len(execErrors) > 0 {
|
||||
return errors.Wrap(errors.Join(execErrors...), "error during exec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
211
modules/brc20/internal/repository/postgres/gen/batch.go
Normal file
211
modules/brc20/internal/repository/postgres/gen/batch.go
Normal file
@@ -0,0 +1,211 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.26.0
|
||||
// source: batch.go
|
||||
|
||||
package gen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBatchAlreadyClosed = errors.New("batch already closed")
|
||||
)
|
||||
|
||||
const createInscriptionEntries = `-- name: CreateInscriptionEntries :batchexec
|
||||
INSERT INTO "brc20_inscription_entries" ("id", "number", "sequence_number", "delegate", "metadata", "metaprotocol", "parents", "pointer", "content", "content_encoding", "content_type", "cursed", "cursed_for_brc20", "created_at", "created_at_height") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
||||
`
|
||||
|
||||
type CreateInscriptionEntriesBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
type CreateInscriptionEntriesParams struct {
|
||||
Id string
|
||||
Number int64
|
||||
SequenceNumber int64
|
||||
Delegate pgtype.Text
|
||||
Metadata []byte
|
||||
Metaprotocol pgtype.Text
|
||||
Parents []string
|
||||
Pointer pgtype.Int8
|
||||
Content []byte
|
||||
ContentEncoding pgtype.Text
|
||||
ContentType pgtype.Text
|
||||
Cursed bool
|
||||
CursedForBrc20 bool
|
||||
CreatedAt pgtype.Timestamp
|
||||
CreatedAtHeight int32
|
||||
}
|
||||
|
||||
func (q *Queries) CreateInscriptionEntries(ctx context.Context, arg []CreateInscriptionEntriesParams) *CreateInscriptionEntriesBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range arg {
|
||||
vals := []interface{}{
|
||||
a.Id,
|
||||
a.Number,
|
||||
a.SequenceNumber,
|
||||
a.Delegate,
|
||||
a.Metadata,
|
||||
a.Metaprotocol,
|
||||
a.Parents,
|
||||
a.Pointer,
|
||||
a.Content,
|
||||
a.ContentEncoding,
|
||||
a.ContentType,
|
||||
a.Cursed,
|
||||
a.CursedForBrc20,
|
||||
a.CreatedAt,
|
||||
a.CreatedAtHeight,
|
||||
}
|
||||
batch.Queue(createInscriptionEntries, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &CreateInscriptionEntriesBatchResults{br, len(arg), false}
|
||||
}
|
||||
|
||||
func (b *CreateInscriptionEntriesBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *CreateInscriptionEntriesBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const createInscriptionEntryStates = `-- name: CreateInscriptionEntryStates :batchexec
|
||||
INSERT INTO "brc20_inscription_entry_states" ("id", "block_height", "transfer_count") VALUES ($1, $2, $3)
|
||||
`
|
||||
|
||||
type CreateInscriptionEntryStatesBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
type CreateInscriptionEntryStatesParams struct {
|
||||
Id string
|
||||
BlockHeight int32
|
||||
TransferCount int32
|
||||
}
|
||||
|
||||
func (q *Queries) CreateInscriptionEntryStates(ctx context.Context, arg []CreateInscriptionEntryStatesParams) *CreateInscriptionEntryStatesBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range arg {
|
||||
vals := []interface{}{
|
||||
a.Id,
|
||||
a.BlockHeight,
|
||||
a.TransferCount,
|
||||
}
|
||||
batch.Queue(createInscriptionEntryStates, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &CreateInscriptionEntryStatesBatchResults{br, len(arg), false}
|
||||
}
|
||||
|
||||
func (b *CreateInscriptionEntryStatesBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *CreateInscriptionEntryStatesBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
|
||||
const createInscriptionTransfers = `-- name: CreateInscriptionTransfers :batchexec
|
||||
INSERT INTO "brc20_inscription_transfers" ("inscription_id", "block_height", "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)
|
||||
`
|
||||
|
||||
type CreateInscriptionTransfersBatchResults struct {
|
||||
br pgx.BatchResults
|
||||
tot int
|
||||
closed bool
|
||||
}
|
||||
|
||||
type CreateInscriptionTransfersParams struct {
|
||||
InscriptionID string
|
||||
BlockHeight int32
|
||||
OldSatpointTxHash pgtype.Text
|
||||
OldSatpointOutIdx pgtype.Int4
|
||||
OldSatpointOffset pgtype.Int8
|
||||
NewSatpointTxHash pgtype.Text
|
||||
NewSatpointOutIdx pgtype.Int4
|
||||
NewSatpointOffset pgtype.Int8
|
||||
NewPkscript string
|
||||
NewOutputValue int64
|
||||
SentAsFee bool
|
||||
}
|
||||
|
||||
func (q *Queries) CreateInscriptionTransfers(ctx context.Context, arg []CreateInscriptionTransfersParams) *CreateInscriptionTransfersBatchResults {
|
||||
batch := &pgx.Batch{}
|
||||
for _, a := range arg {
|
||||
vals := []interface{}{
|
||||
a.InscriptionID,
|
||||
a.BlockHeight,
|
||||
a.OldSatpointTxHash,
|
||||
a.OldSatpointOutIdx,
|
||||
a.OldSatpointOffset,
|
||||
a.NewSatpointTxHash,
|
||||
a.NewSatpointOutIdx,
|
||||
a.NewSatpointOffset,
|
||||
a.NewPkscript,
|
||||
a.NewOutputValue,
|
||||
a.SentAsFee,
|
||||
}
|
||||
batch.Queue(createInscriptionTransfers, vals...)
|
||||
}
|
||||
br := q.db.SendBatch(ctx, batch)
|
||||
return &CreateInscriptionTransfersBatchResults{br, len(arg), false}
|
||||
}
|
||||
|
||||
func (b *CreateInscriptionTransfersBatchResults) Exec(f func(int, error)) {
|
||||
defer b.br.Close()
|
||||
for t := 0; t < b.tot; t++ {
|
||||
if b.closed {
|
||||
if f != nil {
|
||||
f(t, ErrBatchAlreadyClosed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := b.br.Exec()
|
||||
if f != nil {
|
||||
f(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *CreateInscriptionTransfersBatchResults) Close() error {
|
||||
b.closed = true
|
||||
return b.br.Close()
|
||||
}
|
||||
160
modules/brc20/internal/repository/postgres/gen/data.sql.go
Normal file
160
modules/brc20/internal/repository/postgres/gen/data.sql.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.26.0
|
||||
// source: data.sql
|
||||
|
||||
package gen
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createProcessorStats = `-- name: CreateProcessorStats :exec
|
||||
INSERT INTO "brc20_processor_stats" (block_height, cursed_inscription_count, blessed_inscription_count, lost_sats) VALUES ($1, $2, $3, $4)
|
||||
`
|
||||
|
||||
type CreateProcessorStatsParams struct {
|
||||
BlockHeight int32
|
||||
CursedInscriptionCount int32
|
||||
BlessedInscriptionCount int32
|
||||
LostSats int64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateProcessorStats(ctx context.Context, arg CreateProcessorStatsParams) error {
|
||||
_, err := q.db.Exec(ctx, createProcessorStats,
|
||||
arg.BlockHeight,
|
||||
arg.CursedInscriptionCount,
|
||||
arg.BlessedInscriptionCount,
|
||||
arg.LostSats,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const getInscriptionEntriesByIds = `-- name: GetInscriptionEntriesByIds :many
|
||||
WITH "states" AS (
|
||||
-- select latest state
|
||||
SELECT DISTINCT ON ("id") id, block_height, transfer_count FROM "brc20_inscription_entry_states" WHERE "id" = ANY($1::text[]) ORDER BY "id", "block_height" DESC
|
||||
)
|
||||
SELECT brc20_inscription_entries.id, number, sequence_number, delegate, metadata, metaprotocol, parents, pointer, content, content_encoding, content_type, cursed, cursed_for_brc20, created_at, created_at_height, states.id, block_height, transfer_count FROM "brc20_inscription_entries"
|
||||
LEFT JOIN "states" ON "brc20_inscription_entries"."id" = states."id"
|
||||
WHERE "brc20_inscription_entries"."id" = ANY($1::text[])
|
||||
`
|
||||
|
||||
type GetInscriptionEntriesByIdsRow struct {
|
||||
Id string
|
||||
Number int64
|
||||
SequenceNumber int64
|
||||
Delegate pgtype.Text
|
||||
Metadata []byte
|
||||
Metaprotocol pgtype.Text
|
||||
Parents []string
|
||||
Pointer pgtype.Int8
|
||||
Content []byte
|
||||
ContentEncoding pgtype.Text
|
||||
ContentType pgtype.Text
|
||||
Cursed bool
|
||||
CursedForBrc20 bool
|
||||
CreatedAt pgtype.Timestamp
|
||||
CreatedAtHeight int32
|
||||
Id_2 pgtype.Text
|
||||
BlockHeight pgtype.Int4
|
||||
TransferCount pgtype.Int4
|
||||
}
|
||||
|
||||
func (q *Queries) GetInscriptionEntriesByIds(ctx context.Context, inscriptionIds []string) ([]GetInscriptionEntriesByIdsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getInscriptionEntriesByIds, inscriptionIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetInscriptionEntriesByIdsRow
|
||||
for rows.Next() {
|
||||
var i GetInscriptionEntriesByIdsRow
|
||||
if err := rows.Scan(
|
||||
&i.Id,
|
||||
&i.Number,
|
||||
&i.SequenceNumber,
|
||||
&i.Delegate,
|
||||
&i.Metadata,
|
||||
&i.Metaprotocol,
|
||||
&i.Parents,
|
||||
&i.Pointer,
|
||||
&i.Content,
|
||||
&i.ContentEncoding,
|
||||
&i.ContentType,
|
||||
&i.Cursed,
|
||||
&i.CursedForBrc20,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedAtHeight,
|
||||
&i.Id_2,
|
||||
&i.BlockHeight,
|
||||
&i.TransferCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getInscriptionsInOutPoint = `-- name: GetInscriptionsInOutPoint :many
|
||||
SELECT inscription_id, block_height, 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 FROM "brc20_inscription_transfers" WHERE "new_satpoint_tx_hash" = $1 AND "new_satpoint_out_idx" = $2
|
||||
`
|
||||
|
||||
type GetInscriptionsInOutPointParams struct {
|
||||
TxHash pgtype.Text
|
||||
TxOutIdx pgtype.Int4
|
||||
}
|
||||
|
||||
func (q *Queries) GetInscriptionsInOutPoint(ctx context.Context, arg GetInscriptionsInOutPointParams) ([]Brc20InscriptionTransfer, error) {
|
||||
rows, err := q.db.Query(ctx, getInscriptionsInOutPoint, arg.TxHash, arg.TxOutIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Brc20InscriptionTransfer
|
||||
for rows.Next() {
|
||||
var i Brc20InscriptionTransfer
|
||||
if err := rows.Scan(
|
||||
&i.InscriptionID,
|
||||
&i.BlockHeight,
|
||||
&i.OldSatpointTxHash,
|
||||
&i.OldSatpointOutIdx,
|
||||
&i.OldSatpointOffset,
|
||||
&i.NewSatpointTxHash,
|
||||
&i.NewSatpointOutIdx,
|
||||
&i.NewSatpointOffset,
|
||||
&i.NewPkscript,
|
||||
&i.NewOutputValue,
|
||||
&i.SentAsFee,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getLatestProcessorStats = `-- name: GetLatestProcessorStats :one
|
||||
SELECT block_height, cursed_inscription_count, blessed_inscription_count, lost_sats FROM "brc20_processor_stats" ORDER BY block_height DESC LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLatestProcessorStats(ctx context.Context) (Brc20ProcessorStat, error) {
|
||||
row := q.db.QueryRow(ctx, getLatestProcessorStats)
|
||||
var i Brc20ProcessorStat
|
||||
err := row.Scan(
|
||||
&i.BlockHeight,
|
||||
&i.CursedInscriptionCount,
|
||||
&i.BlessedInscriptionCount,
|
||||
&i.LostSats,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -15,6 +15,7 @@ type DBTX interface {
|
||||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||
SendBatch(context.Context, *pgx.Batch) pgx.BatchResults
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
|
||||
@@ -54,28 +54,42 @@ type Brc20IndexerState struct {
|
||||
CreatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type Brc20Inscription struct {
|
||||
type Brc20InscriptionEntry struct {
|
||||
Id string
|
||||
Number int64
|
||||
SequenceNumber int64
|
||||
Delegate pgtype.Text
|
||||
Metadata []byte
|
||||
Metaprotocol pgtype.Text
|
||||
Parent pgtype.Text
|
||||
Parents []string
|
||||
Pointer pgtype.Int8
|
||||
Content []byte
|
||||
ContentType string
|
||||
TransferCount int32
|
||||
ContentEncoding pgtype.Text
|
||||
ContentType pgtype.Text
|
||||
Cursed bool
|
||||
CursedForBrc20 bool
|
||||
CreatedAt pgtype.Timestamp
|
||||
CreatedAtHeight int32
|
||||
}
|
||||
|
||||
type Brc20InscriptionLocation struct {
|
||||
InscriptionID string
|
||||
type Brc20InscriptionEntryState struct {
|
||||
Id string
|
||||
BlockHeight int32
|
||||
TxHash string
|
||||
TxIdx int32
|
||||
SatOffset int64
|
||||
TransferCount int32
|
||||
}
|
||||
|
||||
type Brc20InscriptionTransfer struct {
|
||||
InscriptionID string
|
||||
BlockHeight int32
|
||||
OldSatpointTxHash pgtype.Text
|
||||
OldSatpointOutIdx pgtype.Int4
|
||||
OldSatpointOffset pgtype.Int8
|
||||
NewSatpointTxHash pgtype.Text
|
||||
NewSatpointOutIdx pgtype.Int4
|
||||
NewSatpointOffset pgtype.Int8
|
||||
NewPkscript string
|
||||
NewOutputValue int64
|
||||
SentAsFee bool
|
||||
}
|
||||
|
||||
type Brc20MintEvent struct {
|
||||
@@ -92,6 +106,13 @@ type Brc20MintEvent struct {
|
||||
ParentID pgtype.Text
|
||||
}
|
||||
|
||||
type Brc20ProcessorStat struct {
|
||||
BlockHeight int32
|
||||
CursedInscriptionCount int32
|
||||
BlessedInscriptionCount int32
|
||||
LostSats int64
|
||||
}
|
||||
|
||||
type Brc20Ticker struct {
|
||||
Tick string
|
||||
OriginalTick string
|
||||
|
||||
195
modules/brc20/internal/repository/postgres/mapper.go
Normal file
195
modules/brc20/internal/repository/postgres/mapper.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/cockroachdb/errors"
|
||||
"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/modules/brc20/internal/repository/postgres/gen"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func mapProcessorStatsModelToType(src gen.Brc20ProcessorStat) entity.ProcessorStats {
|
||||
return entity.ProcessorStats{
|
||||
BlockHeight: uint64(src.BlockHeight),
|
||||
CursedInscriptionCount: uint64(src.CursedInscriptionCount),
|
||||
BlessedInscriptionCount: uint64(src.BlessedInscriptionCount),
|
||||
LostSats: uint64(src.LostSats),
|
||||
}
|
||||
}
|
||||
|
||||
func mapProcessorStatsTypeToParams(src entity.ProcessorStats) gen.CreateProcessorStatsParams {
|
||||
return gen.CreateProcessorStatsParams{
|
||||
BlockHeight: int32(src.BlockHeight),
|
||||
CursedInscriptionCount: int32(src.CursedInscriptionCount),
|
||||
BlessedInscriptionCount: int32(src.BlessedInscriptionCount),
|
||||
LostSats: int64(src.LostSats),
|
||||
}
|
||||
}
|
||||
|
||||
func mapInscriptionEntryModelToType(src gen.GetInscriptionEntriesByIdsRow) (ordinals.InscriptionEntry, error) {
|
||||
inscriptionId, err := ordinals.NewInscriptionIdFromString(src.Id)
|
||||
if err != nil {
|
||||
return ordinals.InscriptionEntry{}, errors.Wrap(err, "invalid inscription id")
|
||||
}
|
||||
|
||||
var delegate, parent *ordinals.InscriptionId
|
||||
if src.Delegate.Valid {
|
||||
delegateValue, err := ordinals.NewInscriptionIdFromString(src.Delegate.String)
|
||||
if err != nil {
|
||||
return ordinals.InscriptionEntry{}, errors.Wrap(err, "invalid delegate id")
|
||||
}
|
||||
delegate = &delegateValue
|
||||
}
|
||||
// ord 0.14.0 supports only one parent
|
||||
if len(src.Parents) > 0 {
|
||||
parentValue, err := ordinals.NewInscriptionIdFromString(src.Parents[0])
|
||||
if err != nil {
|
||||
return ordinals.InscriptionEntry{}, errors.Wrap(err, "invalid parent id")
|
||||
}
|
||||
parent = &parentValue
|
||||
}
|
||||
|
||||
inscription := ordinals.Inscription{
|
||||
Content: src.Content,
|
||||
ContentEncoding: lo.Ternary(src.ContentEncoding.Valid, src.ContentEncoding.String, ""),
|
||||
ContentType: lo.Ternary(src.ContentType.Valid, src.ContentType.String, ""),
|
||||
Delegate: delegate,
|
||||
Metadata: src.Metadata,
|
||||
Metaprotocol: lo.Ternary(src.Metaprotocol.Valid, src.Metaprotocol.String, ""),
|
||||
Parent: parent,
|
||||
Pointer: lo.Ternary(src.Pointer.Valid, lo.ToPtr(uint64(src.Pointer.Int64)), nil),
|
||||
}
|
||||
|
||||
return ordinals.InscriptionEntry{
|
||||
Id: inscriptionId,
|
||||
Number: src.Number,
|
||||
SequenceNumber: uint64(src.SequenceNumber),
|
||||
Cursed: src.Cursed,
|
||||
CursedForBRC20: src.CursedForBrc20,
|
||||
CreatedAt: lo.Ternary(src.CreatedAt.Valid, src.CreatedAt.Time, time.Time{}),
|
||||
CreatedAtHeight: uint64(src.CreatedAtHeight),
|
||||
Inscription: inscription,
|
||||
TransferCount: lo.Ternary(src.TransferCount.Valid, uint32(src.TransferCount.Int32), 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapInscriptionEntryTypeToParams(src ordinals.InscriptionEntry, blockHeight uint64) (gen.CreateInscriptionEntriesParams, gen.CreateInscriptionEntryStatesParams, error) {
|
||||
var delegate, metaprotocol, contentEncoding, contentType pgtype.Text
|
||||
if src.Inscription.Delegate != nil {
|
||||
delegate = pgtype.Text{String: src.Inscription.Delegate.String(), Valid: true}
|
||||
}
|
||||
if src.Inscription.Metaprotocol != "" {
|
||||
metaprotocol = pgtype.Text{String: src.Inscription.Metaprotocol, Valid: true}
|
||||
}
|
||||
if src.Inscription.ContentEncoding != "" {
|
||||
contentEncoding = pgtype.Text{String: src.Inscription.ContentEncoding, Valid: true}
|
||||
}
|
||||
if src.Inscription.ContentType != "" {
|
||||
contentType = pgtype.Text{String: src.Inscription.ContentType, Valid: true}
|
||||
}
|
||||
var parents []string
|
||||
if src.Inscription.Parent != nil {
|
||||
parents = append(parents, src.Inscription.Parent.String())
|
||||
}
|
||||
var pointer pgtype.Int8
|
||||
if src.Inscription.Pointer != nil {
|
||||
pointer = pgtype.Int8{Int64: int64(*src.Inscription.Pointer), Valid: true}
|
||||
}
|
||||
return gen.CreateInscriptionEntriesParams{
|
||||
Id: src.Id.String(),
|
||||
Number: src.Number,
|
||||
SequenceNumber: int64(src.SequenceNumber),
|
||||
Delegate: delegate,
|
||||
Metadata: src.Inscription.Metadata,
|
||||
Metaprotocol: metaprotocol,
|
||||
Parents: parents,
|
||||
Pointer: pointer,
|
||||
Content: src.Inscription.Content,
|
||||
ContentEncoding: contentEncoding,
|
||||
ContentType: contentType,
|
||||
Cursed: src.Cursed,
|
||||
CursedForBrc20: src.CursedForBRC20,
|
||||
CreatedAt: lo.Ternary(!src.CreatedAt.IsZero(), pgtype.Timestamp{Time: src.CreatedAt, Valid: true}, pgtype.Timestamp{}),
|
||||
CreatedAtHeight: int32(src.CreatedAtHeight),
|
||||
}, gen.CreateInscriptionEntryStatesParams{
|
||||
Id: src.Id.String(),
|
||||
BlockHeight: int32(blockHeight),
|
||||
TransferCount: int32(src.TransferCount),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapInscriptionTransferModelToType(src gen.Brc20InscriptionTransfer) (entity.InscriptionTransfer, error) {
|
||||
inscriptionId, err := ordinals.NewInscriptionIdFromString(src.InscriptionID)
|
||||
if err != nil {
|
||||
return entity.InscriptionTransfer{}, errors.Wrap(err, "invalid inscription id")
|
||||
}
|
||||
var oldSatPoint, newSatPoint ordinals.SatPoint
|
||||
if src.OldSatpointTxHash.Valid {
|
||||
if !src.OldSatpointOutIdx.Valid || !src.OldSatpointOffset.Valid {
|
||||
return entity.InscriptionTransfer{}, errors.New("old satpoint out idx and offset must exist if hash exists")
|
||||
}
|
||||
txHash, err := chainhash.NewHashFromStr(src.OldSatpointTxHash.String)
|
||||
if err != nil {
|
||||
return entity.InscriptionTransfer{}, errors.Wrap(err, "invalid old satpoint tx hash")
|
||||
}
|
||||
oldSatPoint = ordinals.SatPoint{
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: *txHash,
|
||||
Index: uint32(src.OldSatpointOutIdx.Int32),
|
||||
},
|
||||
Offset: uint64(src.OldSatpointOffset.Int64),
|
||||
}
|
||||
}
|
||||
if src.NewSatpointTxHash.Valid {
|
||||
if !src.NewSatpointOutIdx.Valid || !src.NewSatpointOffset.Valid {
|
||||
return entity.InscriptionTransfer{}, errors.New("new satpoint out idx and offset must exist if hash exists")
|
||||
}
|
||||
txHash, err := chainhash.NewHashFromStr(src.NewSatpointTxHash.String)
|
||||
if err != nil {
|
||||
return entity.InscriptionTransfer{}, errors.Wrap(err, "invalid new satpoint tx hash")
|
||||
}
|
||||
newSatPoint = ordinals.SatPoint{
|
||||
OutPoint: wire.OutPoint{
|
||||
Hash: *txHash,
|
||||
Index: uint32(src.NewSatpointOutIdx.Int32),
|
||||
},
|
||||
Offset: uint64(src.NewSatpointOffset.Int64),
|
||||
}
|
||||
}
|
||||
newPkScript, err := hex.DecodeString(src.NewPkscript)
|
||||
if err != nil {
|
||||
return entity.InscriptionTransfer{}, errors.Wrap(err, "failed to parse pkscript")
|
||||
}
|
||||
|
||||
return entity.InscriptionTransfer{
|
||||
InscriptionId: inscriptionId,
|
||||
BlockHeight: uint64(src.BlockHeight),
|
||||
OldSatPoint: oldSatPoint,
|
||||
NewSatPoint: newSatPoint,
|
||||
NewPkScript: newPkScript,
|
||||
NewOutputValue: uint64(src.NewOutputValue),
|
||||
SentAsFee: src.SentAsFee,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mapInscriptionTransferTypeToParams(src entity.InscriptionTransfer) gen.CreateInscriptionTransfersParams {
|
||||
return gen.CreateInscriptionTransfersParams{
|
||||
InscriptionID: src.InscriptionId.String(),
|
||||
BlockHeight: int32(src.BlockHeight),
|
||||
OldSatpointTxHash: lo.Ternary(src.OldSatPoint.OutPoint.Hash != chainhash.Hash{}, pgtype.Text{String: src.OldSatPoint.OutPoint.Hash.String(), Valid: true}, pgtype.Text{}),
|
||||
OldSatpointOutIdx: lo.Ternary(src.OldSatPoint.OutPoint.Index != 0, pgtype.Int4{Int32: int32(src.OldSatPoint.OutPoint.Index), Valid: true}, pgtype.Int4{}),
|
||||
OldSatpointOffset: lo.Ternary(src.OldSatPoint.Offset != 0, pgtype.Int8{Int64: int64(src.OldSatPoint.Offset), Valid: true}, pgtype.Int8{}),
|
||||
NewSatpointTxHash: lo.Ternary(src.NewSatPoint.OutPoint.Hash != chainhash.Hash{}, pgtype.Text{String: src.NewSatPoint.OutPoint.Hash.String(), Valid: true}, pgtype.Text{}),
|
||||
NewSatpointOutIdx: lo.Ternary(src.NewSatPoint.OutPoint.Index != 0, pgtype.Int4{Int32: int32(src.NewSatPoint.OutPoint.Index), Valid: true}, pgtype.Int4{}),
|
||||
NewSatpointOffset: lo.Ternary(src.NewSatPoint.Offset != 0, pgtype.Int8{Int64: int64(src.NewSatPoint.Offset), Valid: true}, pgtype.Int8{}),
|
||||
NewPkscript: hex.EncodeToString(src.NewPkScript),
|
||||
NewOutputValue: int64(src.NewOutputValue),
|
||||
SentAsFee: src.SentAsFee,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user