mirror of
https://github.com/trezor/blockbook.git
synced 2026-02-20 09:02:06 +01:00
* feat: add longTermFeeRate websocket endpoint * chore: regenerate blockbook-api.ts with longTermFeeRate
628 lines
37 KiB
Go
628 lines
37 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/trezor/blockbook/bchain"
|
|
"github.com/trezor/blockbook/bchain/coins/eth"
|
|
"github.com/trezor/blockbook/common"
|
|
"github.com/trezor/blockbook/db"
|
|
)
|
|
|
|
const maxUint32 = ^uint32(0)
|
|
const maxInt = int(^uint(0) >> 1)
|
|
const maxInt64 = int64(^uint64(0) >> 1)
|
|
|
|
// AccountDetails specifies what data returns GetAddress and GetXpub calls
|
|
type AccountDetails int
|
|
|
|
const (
|
|
// AccountDetailsBasic - only that address is indexed and some basic info
|
|
AccountDetailsBasic AccountDetails = iota
|
|
// AccountDetailsTokens - basic info + tokens
|
|
AccountDetailsTokens
|
|
// AccountDetailsTokenBalances - basic info + token with balance
|
|
AccountDetailsTokenBalances
|
|
// AccountDetailsTxidHistory - basic + token balances + txids, subject to paging
|
|
AccountDetailsTxidHistory
|
|
// AccountDetailsTxHistoryLight - basic + tokens + easily obtained tx data (not requiring requests to backend), subject to paging
|
|
AccountDetailsTxHistoryLight
|
|
// AccountDetailsTxHistory - basic + tokens + full tx data, subject to paging
|
|
AccountDetailsTxHistory
|
|
)
|
|
|
|
// ErrUnsupportedXpub is returned when coin type does not support xpub address derivation or provided string is not an xpub
|
|
var ErrUnsupportedXpub = errors.New("XPUB not supported")
|
|
|
|
// APIError extends error by information if the error details should be returned to the end user
|
|
type APIError struct {
|
|
Text string `ts_doc:"Human-readable error message describing the issue."`
|
|
Public bool `ts_doc:"Whether the error message can safely be shown to the end user."`
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
return e.Text
|
|
}
|
|
|
|
// NewAPIError creates ApiError
|
|
func NewAPIError(s string, public bool) error {
|
|
return &APIError{
|
|
Text: s,
|
|
Public: public,
|
|
}
|
|
}
|
|
|
|
// Amount is a datatype holding amounts
|
|
type Amount big.Int
|
|
|
|
// IsZeroBigInt checks if big int has zero value
|
|
func IsZeroBigInt(b *big.Int) bool {
|
|
return len(b.Bits()) == 0
|
|
}
|
|
|
|
// Compare returns an integer comparing two Amounts. The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
|
|
// Nil Amount is always less then non-nil amount, two nil Amounts are equal
|
|
func (a *Amount) Compare(b *Amount) int {
|
|
if b == nil {
|
|
if a == nil {
|
|
return 0
|
|
}
|
|
return 1
|
|
}
|
|
if a == nil {
|
|
return -1
|
|
}
|
|
return (*big.Int)(a).Cmp((*big.Int)(b))
|
|
}
|
|
|
|
// MarshalJSON Amount serialization
|
|
func (a *Amount) MarshalJSON() (out []byte, err error) {
|
|
if a == nil {
|
|
return []byte(`"0"`), nil
|
|
}
|
|
return []byte(`"` + (*big.Int)(a).String() + `"`), nil
|
|
}
|
|
|
|
func (a *Amount) UnmarshalJSON(data []byte) error {
|
|
s := strings.Trim(string(data), "\"")
|
|
if len(s) > 0 {
|
|
bigValue, parsed := new(big.Int).SetString(s, 10)
|
|
if !parsed {
|
|
return fmt.Errorf("couldn't parse number: %s", s)
|
|
}
|
|
*a = Amount(*bigValue)
|
|
} else {
|
|
// assuming empty string means zero
|
|
*a = Amount{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *Amount) String() string {
|
|
if a == nil {
|
|
return ""
|
|
}
|
|
return (*big.Int)(a).String()
|
|
}
|
|
|
|
// DecimalString returns amount with decimal point placed according to parameter d
|
|
func (a *Amount) DecimalString(d int) string {
|
|
return bchain.AmountToDecimalString((*big.Int)(a), d)
|
|
}
|
|
|
|
// AsBigInt returns big.Int type for the Amount (empty if Amount is nil)
|
|
func (a *Amount) AsBigInt() big.Int {
|
|
if a == nil {
|
|
return *new(big.Int)
|
|
}
|
|
return big.Int(*a)
|
|
}
|
|
|
|
// AsInt64 returns Amount as int64 (0 if Amount is nil).
|
|
// It is used only for legacy interfaces (socket.io)
|
|
// and generally not recommended to use for possible loss of precision.
|
|
func (a *Amount) AsInt64() int64 {
|
|
if a == nil {
|
|
return 0
|
|
}
|
|
return (*big.Int)(a).Int64()
|
|
}
|
|
|
|
// Vin contains information about single transaction input
|
|
type Vin struct {
|
|
Txid string `json:"txid,omitempty" ts_doc:"ID/hash of the originating transaction (where the UTXO comes from)."`
|
|
Vout uint32 `json:"vout,omitempty" ts_doc:"Index of the output in the referenced transaction."`
|
|
Sequence int64 `json:"sequence,omitempty" ts_doc:"Sequence number for this input (e.g. 4294967293)."`
|
|
N int `json:"n" ts_doc:"Relative index of this input within the transaction."`
|
|
AddrDesc bchain.AddressDescriptor `json:"-" ts_doc:"Internal address descriptor for backend usage (not exposed via JSON)."`
|
|
Addresses []string `json:"addresses,omitempty" ts_doc:"List of addresses associated with this input."`
|
|
IsAddress bool `json:"isAddress" ts_doc:"Indicates if this input is from a known address."`
|
|
IsOwn bool `json:"isOwn,omitempty" ts_doc:"Indicates if this input belongs to the wallet in context."`
|
|
ValueSat *Amount `json:"value,omitempty" ts_doc:"Amount (in satoshi or base units) of the input."`
|
|
Hex string `json:"hex,omitempty" ts_doc:"Raw script hex data for this input."`
|
|
Asm string `json:"asm,omitempty" ts_doc:"Disassembled script for this input."`
|
|
Coinbase string `json:"coinbase,omitempty" ts_doc:"Data for coinbase inputs (when mining)."`
|
|
}
|
|
|
|
// Vout contains information about single transaction output
|
|
type Vout struct {
|
|
ValueSat *Amount `json:"value,omitempty" ts_doc:"Amount (in satoshi or base units) of the output."`
|
|
N int `json:"n" ts_doc:"Relative index of this output within the transaction."`
|
|
Spent bool `json:"spent,omitempty" ts_doc:"Indicates whether this output has been spent."`
|
|
SpentTxID string `json:"spentTxId,omitempty" ts_doc:"Transaction ID in which this output was spent."`
|
|
SpentIndex int `json:"spentIndex,omitempty" ts_doc:"Index of the input that spent this output."`
|
|
SpentHeight int `json:"spentHeight,omitempty" ts_doc:"Block height at which this output was spent."`
|
|
Hex string `json:"hex,omitempty" ts_doc:"Raw script hex data for this output - aka ScriptPubKey."`
|
|
Asm string `json:"asm,omitempty" ts_doc:"Disassembled script for this output."`
|
|
AddrDesc bchain.AddressDescriptor `json:"-" ts_doc:"Internal address descriptor for backend usage (not exposed via JSON)."`
|
|
Addresses []string `json:"addresses" ts_doc:"List of addresses associated with this output."`
|
|
IsAddress bool `json:"isAddress" ts_doc:"Indicates whether this output is owned by valid address."`
|
|
IsOwn bool `json:"isOwn,omitempty" ts_doc:"Indicates if this output belongs to the wallet in context."`
|
|
Type string `json:"type,omitempty" ts_doc:"Output script type (e.g., 'P2PKH', 'P2SH')."`
|
|
}
|
|
|
|
// MultiTokenValue contains values for contracts with multiple token IDs
|
|
type MultiTokenValue struct {
|
|
Id *Amount `json:"id,omitempty" ts_doc:"Token ID (for ERC1155)."`
|
|
Value *Amount `json:"value,omitempty" ts_doc:"Amount of that specific token ID."`
|
|
}
|
|
|
|
// Token contains info about tokens held by an address
|
|
type Token struct {
|
|
// Deprecated: Use Standard instead.
|
|
Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."`
|
|
Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"`
|
|
Name string `json:"name" ts_doc:"Readable name of the token."`
|
|
Path string `json:"path,omitempty" ts_doc:"Derivation path if this token is derived from an XPUB-based address."`
|
|
Contract string `json:"contract,omitempty" ts_doc:"Contract address on-chain."`
|
|
Transfers int `json:"transfers" ts_doc:"Total number of token transfers for this address."`
|
|
Symbol string `json:"symbol,omitempty" ts_doc:"Symbol for the token (e.g., 'ETH', 'USDT')."`
|
|
Decimals int `json:"decimals,omitempty" ts_doc:"Number of decimals for this token."`
|
|
BalanceSat *Amount `json:"balance,omitempty" ts_doc:"Current token balance (in minimal base units)."`
|
|
BaseValue float64 `json:"baseValue,omitempty" ts_doc:"Value in the base currency (e.g. ETH for ERC20 tokens)."`
|
|
SecondaryValue float64 `json:"secondaryValue,omitempty" ts_doc:"Value in a secondary currency (e.g. fiat), if available."`
|
|
Ids []Amount `json:"ids,omitempty" ts_doc:"List of token IDs (for ERC721, each ID is a unique collectible)."`
|
|
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty" ts_doc:"Multiple ERC1155 token balances (id + value)."`
|
|
TotalReceivedSat *Amount `json:"totalReceived,omitempty" ts_doc:"Total amount of tokens received."`
|
|
TotalSentSat *Amount `json:"totalSent,omitempty" ts_doc:"Total amount of tokens sent."`
|
|
ContractIndex string `json:"-"`
|
|
}
|
|
|
|
// Tokens is array of Token
|
|
type Tokens []Token
|
|
|
|
func (a Tokens) Len() int { return len(a) }
|
|
func (a Tokens) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a Tokens) Less(i, j int) bool {
|
|
ti := &a[i]
|
|
tj := &a[j]
|
|
// sort by BaseValue descending and then Name and then by Contract
|
|
if ti.BaseValue < tj.BaseValue {
|
|
return false
|
|
} else if ti.BaseValue > tj.BaseValue {
|
|
return true
|
|
}
|
|
if ti.Name == "" {
|
|
if tj.Name != "" {
|
|
return false
|
|
}
|
|
} else {
|
|
if tj.Name == "" {
|
|
return true
|
|
}
|
|
return ti.Name < tj.Name
|
|
}
|
|
return ti.Contract < tj.Contract
|
|
}
|
|
|
|
// TokenTransfer contains info about a token transfer done in a transaction
|
|
type TokenTransfer struct {
|
|
// Deprecated: Use Standard instead.
|
|
Type bchain.TokenStandardName `json:"type" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'" ts_doc:"@deprecated: Use standard instead."`
|
|
Standard bchain.TokenStandardName `json:"standard" ts_type:"'' | 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155' | 'BEP20' | 'BEP721' | 'BEP1155'"`
|
|
From string `json:"from" ts_doc:"Source address of the token transfer."`
|
|
To string `json:"to" ts_doc:"Destination address of the token transfer."`
|
|
Contract string `json:"contract" ts_doc:"Contract address of the token."`
|
|
Name string `json:"name,omitempty" ts_doc:"Token name."`
|
|
Symbol string `json:"symbol,omitempty" ts_doc:"Token symbol."`
|
|
Decimals int `json:"decimals,omitempty" ts_doc:"Number of decimals for this token (if applicable)."`
|
|
Value *Amount `json:"value,omitempty" ts_doc:"Amount (in base units) of tokens transferred."`
|
|
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty" ts_doc:"List of multiple ID-value pairs for ERC1155 transfers."`
|
|
}
|
|
|
|
// EthereumInternalTransfer represents internal transaction data in Ethereum-like blockchains
|
|
type EthereumInternalTransfer struct {
|
|
Type bchain.EthereumInternalTransactionType `json:"type" ts_doc:"Type of internal transfer (CALL, CREATE, etc.)."`
|
|
From string `json:"from" ts_doc:"Address from which the transfer originated."`
|
|
To string `json:"to" ts_doc:"Address to which the transfer was sent."`
|
|
Value *Amount `json:"value" ts_doc:"Value transferred internally (in Wei or base units)."`
|
|
}
|
|
|
|
// EthereumSpecific contains ethereum-specific transaction data
|
|
type EthereumSpecific struct {
|
|
Type bchain.EthereumInternalTransactionType `json:"type,omitempty" ts_doc:"High-level type of the Ethereum tx (e.g., 'call', 'create')."`
|
|
CreatedContract string `json:"createdContract,omitempty" ts_doc:"Address of contract created by this transaction, if any."`
|
|
Status eth.TxStatus `json:"status" ts_doc:"Execution status of the transaction (1: success, 0: fail, -1: pending)."`
|
|
Error string `json:"error,omitempty" ts_doc:"Error encountered during execution, if any."`
|
|
Nonce uint64 `json:"nonce" ts_doc:"Transaction nonce (sequential number from the sender)."`
|
|
GasLimit *big.Int `json:"gasLimit" ts_doc:"Maximum gas allowed by the sender for this transaction."`
|
|
GasUsed *big.Int `json:"gasUsed,omitempty" ts_doc:"Actual gas consumed by the transaction execution."`
|
|
GasPrice *Amount `json:"gasPrice,omitempty" ts_doc:"Price (in Wei or base units) per gas unit."`
|
|
MaxPriorityFeePerGas *Amount `json:"maxPriorityFeePerGas,omitempty"`
|
|
MaxFeePerGas *Amount `json:"maxFeePerGas,omitempty"`
|
|
BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"`
|
|
L1Fee *big.Int `json:"l1Fee,omitempty" ts_doc:"Fee used for L1 part in rollups (e.g. Optimism)."`
|
|
L1FeeScalar string `json:"l1FeeScalar,omitempty" ts_doc:"Scaling factor for L1 fees in certain Layer 2 solutions."`
|
|
L1GasPrice *Amount `json:"l1GasPrice,omitempty" ts_doc:"Gas price for L1 component, if applicable."`
|
|
L1GasUsed *big.Int `json:"l1GasUsed,omitempty" ts_doc:"Amount of gas used in L1 for this tx, if applicable."`
|
|
Data string `json:"data,omitempty" ts_doc:"Hex-encoded input data for the transaction."`
|
|
ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty" ts_doc:"Decoded transaction data (function name, params, etc.)."`
|
|
InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty" ts_doc:"List of internal (sub-call) transfers."`
|
|
}
|
|
|
|
// AddressAlias holds a specialized alias for an address
|
|
type AddressAlias struct {
|
|
Type string `ts_doc:"Type of alias, e.g., user-defined name or contract name."`
|
|
Alias string `ts_doc:"Alias string for the address."`
|
|
}
|
|
|
|
// AddressAliasesMap is a map of address strings to their alias definitions
|
|
type AddressAliasesMap map[string]AddressAlias
|
|
|
|
// Tx holds information about a transaction
|
|
type Tx struct {
|
|
Txid string `json:"txid" ts_doc:"Transaction ID (hash)."`
|
|
Version int32 `json:"version,omitempty" ts_doc:"Version of the transaction (if applicable)."`
|
|
Locktime uint32 `json:"lockTime,omitempty" ts_doc:"Locktime indicating earliest time/height transaction can be mined."`
|
|
Vin []Vin `json:"vin" ts_doc:"Array of inputs for this transaction."`
|
|
Vout []Vout `json:"vout" ts_doc:"Array of outputs for this transaction."`
|
|
Blockhash string `json:"blockHash,omitempty" ts_doc:"Hash of the block containing this transaction."`
|
|
Blockheight int `json:"blockHeight" ts_doc:"Block height in which this transaction was included."`
|
|
Confirmations uint32 `json:"confirmations" ts_doc:"Number of confirmations (blocks mined after this tx's block)."`
|
|
ConfirmationETABlocks uint32 `json:"confirmationETABlocks,omitempty" ts_doc:"Estimated blocks remaining until confirmation (if unconfirmed)."`
|
|
ConfirmationETASeconds int64 `json:"confirmationETASeconds,omitempty" ts_doc:"Estimated seconds remaining until confirmation (if unconfirmed)."`
|
|
Blocktime int64 `json:"blockTime" ts_doc:"Unix timestamp of the block in which this transaction was included. 0 if unconfirmed."`
|
|
Size int `json:"size,omitempty" ts_doc:"Transaction size in bytes."`
|
|
VSize int `json:"vsize,omitempty" ts_doc:"Virtual size in bytes, for SegWit-enabled chains."`
|
|
ValueOutSat *Amount `json:"value" ts_doc:"Total value of all outputs (in satoshi or base units)."`
|
|
ValueInSat *Amount `json:"valueIn,omitempty" ts_doc:"Total value of all inputs (in satoshi or base units)."`
|
|
FeesSat *Amount `json:"fees,omitempty" ts_doc:"Transaction fee (inputs - outputs)."`
|
|
Hex string `json:"hex,omitempty" ts_doc:"Raw hex-encoded transaction data."`
|
|
Rbf bool `json:"rbf,omitempty" ts_doc:"Indicates if this transaction is replace-by-fee (RBF) enabled."`
|
|
CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty" ts_type:"any" ts_doc:"Blockchain-specific extended data."`
|
|
TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty" ts_doc:"List of token transfers that occurred in this transaction."`
|
|
EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty" ts_doc:"Ethereum-like blockchain specific data (if applicable)."`
|
|
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty" ts_doc:"Aliases for addresses involved in this transaction."`
|
|
}
|
|
|
|
// FeeStats contains detailed block fee statistics
|
|
type FeeStats struct {
|
|
TxCount int `json:"txCount" ts_doc:"Number of transactions in the given block."`
|
|
TotalFeesSat *Amount `json:"totalFeesSat" ts_doc:"Sum of all fees in satoshi or base units."`
|
|
AverageFeePerKb int64 `json:"averageFeePerKb" ts_doc:"Average fee per kilobyte in satoshi or base units."`
|
|
DecilesFeePerKb [11]int64 `json:"decilesFeePerKb" ts_doc:"Fee distribution deciles (0%..100%) in satoshi or base units per kB."`
|
|
}
|
|
|
|
// Paging contains information about paging for address, blocks and block
|
|
type Paging struct {
|
|
Page int `json:"page,omitempty" ts_doc:"Current page index."`
|
|
TotalPages int `json:"totalPages,omitempty" ts_doc:"Total number of pages available."`
|
|
ItemsOnPage int `json:"itemsOnPage,omitempty" ts_doc:"Number of items returned on this page."`
|
|
}
|
|
|
|
// TokensToReturn specifies what tokens are returned by GetAddress and GetXpubAddress
|
|
type TokensToReturn int
|
|
|
|
const (
|
|
// AddressFilterVoutOff disables filtering of transactions by vout
|
|
AddressFilterVoutOff = -1
|
|
// AddressFilterVoutInputs specifies that only txs where the address is as input are returned
|
|
AddressFilterVoutInputs = -2
|
|
// AddressFilterVoutOutputs specifies that only txs where the address is as output are returned
|
|
AddressFilterVoutOutputs = -3
|
|
// AddressFilterVoutQueryNotNecessary signals that query for transactions is not necessary as there are no transactions for specified contract filter
|
|
AddressFilterVoutQueryNotNecessary = -4
|
|
|
|
// TokensToReturnNonzeroBalance - return only tokens with nonzero balance
|
|
TokensToReturnNonzeroBalance TokensToReturn = 0
|
|
// TokensToReturnUsed - return tokens with some transfers (even if they have zero balance now)
|
|
TokensToReturnUsed TokensToReturn = 1
|
|
// TokensToReturnDerived - return all derived tokens
|
|
TokensToReturnDerived TokensToReturn = 2
|
|
)
|
|
|
|
// AddressFilter is used to filter data returned from GetAddress api method
|
|
type AddressFilter struct {
|
|
Vout int `ts_doc:"Specifies which output index we are interested in filtering (or use the special constants)."`
|
|
Contract string `ts_doc:"Contract address to filter by, if applicable."`
|
|
FromHeight uint32 `ts_doc:"Starting block height for filtering transactions."`
|
|
ToHeight uint32 `ts_doc:"Ending block height for filtering transactions."`
|
|
TokensToReturn TokensToReturn `ts_doc:"Which tokens to include in the result set."`
|
|
// OnlyConfirmed set to true will ignore mempool transactions; mempool is also ignored if FromHeight/ToHeight filter is specified
|
|
OnlyConfirmed bool `ts_doc:"If true, ignores mempool (unconfirmed) transactions."`
|
|
}
|
|
|
|
// StakingPool holds data about address participation in a staking pool contract
|
|
type StakingPool struct {
|
|
Contract string `json:"contract" ts_doc:"Staking pool contract address on-chain."`
|
|
Name string `json:"name" ts_doc:"Name of the staking pool contract."`
|
|
PendingBalance *Amount `json:"pendingBalance" ts_doc:"Balance pending deposit or withdrawal, if any."`
|
|
PendingDepositedBalance *Amount `json:"pendingDepositedBalance" ts_doc:"Any pending deposit that is not yet finalized."`
|
|
DepositedBalance *Amount `json:"depositedBalance" ts_doc:"Currently deposited/staked balance."`
|
|
WithdrawTotalAmount *Amount `json:"withdrawTotalAmount" ts_doc:"Total amount withdrawn from this pool by the address."`
|
|
ClaimableAmount *Amount `json:"claimableAmount" ts_doc:"Rewards or principal currently claimable by the address."`
|
|
RestakedReward *Amount `json:"restakedReward" ts_doc:"Total rewards that have been restaked automatically."`
|
|
AutocompoundBalance *Amount `json:"autocompoundBalance" ts_doc:"Any balance automatically reinvested into the pool."`
|
|
}
|
|
|
|
// Address holds information about an address and its transactions
|
|
type Address struct {
|
|
Paging
|
|
AddrStr string `json:"address" ts_doc:"The address string in standard format."`
|
|
BalanceSat *Amount `json:"balance" ts_doc:"Current confirmed balance (in satoshi or base units)."`
|
|
TotalReceivedSat *Amount `json:"totalReceived,omitempty" ts_doc:"Total amount ever received by this address."`
|
|
TotalSentSat *Amount `json:"totalSent,omitempty" ts_doc:"Total amount ever sent by this address."`
|
|
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance" ts_doc:"Unconfirmed balance for this address."`
|
|
UnconfirmedTxs int `json:"unconfirmedTxs" ts_doc:"Number of unconfirmed transactions for this address."`
|
|
UnconfirmedSending *Amount `json:"unconfirmedSending,omitempty" ts_doc:"Unconfirmed outgoing balance for this address."`
|
|
UnconfirmedReceiving *Amount `json:"unconfirmedReceiving,omitempty" ts_doc:"Unconfirmed incoming balance for this address."`
|
|
Txs int `json:"txs" ts_doc:"Number of transactions for this address (including confirmed)."`
|
|
AddrTxCount int `json:"addrTxCount,omitempty" ts_doc:"Historical total count of transactions, if known."`
|
|
NonTokenTxs int `json:"nonTokenTxs,omitempty" ts_doc:"Number of transactions not involving tokens (pure coin transfers)."`
|
|
InternalTxs int `json:"internalTxs,omitempty" ts_doc:"Number of internal transactions (e.g., Ethereum calls)."`
|
|
Transactions []*Tx `json:"transactions,omitempty" ts_doc:"List of transaction details (if requested)."`
|
|
Txids []string `json:"txids,omitempty" ts_doc:"List of transaction IDs (if detailed data is not requested)."`
|
|
Nonce string `json:"nonce,omitempty" ts_doc:"Current transaction nonce for Ethereum-like addresses."`
|
|
UsedTokens int `json:"usedTokens,omitempty" ts_doc:"Number of tokens with any historical usage at this address."`
|
|
Tokens Tokens `json:"tokens,omitempty" ts_doc:"List of tokens associated with this address."`
|
|
SecondaryValue float64 `json:"secondaryValue,omitempty" ts_doc:"Total value of the address in secondary currency (e.g. fiat)."`
|
|
TokensBaseValue float64 `json:"tokensBaseValue,omitempty" ts_doc:"Sum of token values in base currency."`
|
|
TokensSecondaryValue float64 `json:"tokensSecondaryValue,omitempty" ts_doc:"Sum of token values in secondary currency (fiat)."`
|
|
TotalBaseValue float64 `json:"totalBaseValue,omitempty" ts_doc:"Address's entire value in base currency, including tokens."`
|
|
TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty" ts_doc:"Address's entire value in secondary currency, including tokens."`
|
|
ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty" ts_doc:"Extra info if the address is a contract (ABI, type)."`
|
|
// Deprecated: replaced by ContractInfo
|
|
Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty" ts_doc:"@deprecated: replaced by contractInfo"`
|
|
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty" ts_doc:"Aliases assigned to this address."`
|
|
StakingPools []StakingPool `json:"stakingPools,omitempty" ts_doc:"List of staking pool data if address interacts with staking."`
|
|
// helpers for explorer
|
|
Filter string `json:"-" ts_doc:"Filter used internally for data retrieval."`
|
|
XPubAddresses map[string]struct{} `json:"-" ts_doc:"Set of derived XPUB addresses (internal usage)."`
|
|
}
|
|
|
|
// Utxo is one unspent transaction output
|
|
type Utxo struct {
|
|
Txid string `json:"txid" ts_doc:"Transaction ID in which this UTXO was created."`
|
|
Vout int32 `json:"vout" ts_doc:"Index of the output in that transaction."`
|
|
AmountSat *Amount `json:"value" ts_doc:"Value of this UTXO (in satoshi or base units)."`
|
|
Height int `json:"height,omitempty" ts_doc:"Block height in which the UTXO was confirmed."`
|
|
Confirmations int `json:"confirmations" ts_doc:"Number of confirmations for this UTXO."`
|
|
Address string `json:"address,omitempty" ts_doc:"Address to which this UTXO belongs."`
|
|
Path string `json:"path,omitempty" ts_doc:"Derivation path for XPUB-based wallets, if applicable."`
|
|
Locktime uint32 `json:"lockTime,omitempty" ts_doc:"If non-zero, locktime required before spending this UTXO."`
|
|
Coinbase bool `json:"coinbase,omitempty" ts_doc:"Indicates if this UTXO originated from a coinbase transaction."`
|
|
}
|
|
|
|
// Utxos is array of Utxo
|
|
type Utxos []Utxo
|
|
|
|
func (a Utxos) Len() int { return len(a) }
|
|
func (a Utxos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a Utxos) Less(i, j int) bool {
|
|
// sort in reverse order, unconfirmed (height==0) utxos on top
|
|
hi := a[i].Height
|
|
hj := a[j].Height
|
|
if hi == 0 {
|
|
hi = maxInt
|
|
}
|
|
if hj == 0 {
|
|
hj = maxInt
|
|
}
|
|
return hi >= hj
|
|
}
|
|
|
|
// BalanceHistory contains info about one point in time of balance history
|
|
type BalanceHistory struct {
|
|
Time uint32 `json:"time" ts_doc:"Unix timestamp for this point in the balance history."`
|
|
Txs uint32 `json:"txs" ts_doc:"Number of transactions in this interval."`
|
|
ReceivedSat *Amount `json:"received" ts_doc:"Amount received in this interval (in satoshi or base units)."`
|
|
SentSat *Amount `json:"sent" ts_doc:"Amount sent in this interval (in satoshi or base units)."`
|
|
SentToSelfSat *Amount `json:"sentToSelf" ts_doc:"Amount sent to the same address (self-transfer)."`
|
|
FiatRates map[string]float32 `json:"rates,omitempty" ts_doc:"Exchange rates at this point in time, if available."`
|
|
Txid string `json:"txid,omitempty" ts_doc:"Transaction ID if the time corresponds to a specific tx."`
|
|
}
|
|
|
|
// BalanceHistories is array of BalanceHistory
|
|
type BalanceHistories []BalanceHistory
|
|
|
|
func (a BalanceHistories) Len() int { return len(a) }
|
|
func (a BalanceHistories) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a BalanceHistories) Less(i, j int) bool {
|
|
ti := a[i].Time
|
|
tj := a[j].Time
|
|
if ti == tj {
|
|
return a[i].Txid < a[j].Txid
|
|
}
|
|
return ti < tj
|
|
}
|
|
|
|
// SortAndAggregate sums BalanceHistories to groups defined by parameter groupByTime
|
|
func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories {
|
|
bhs := make(BalanceHistories, 0)
|
|
if len(a) > 0 {
|
|
bha := BalanceHistory{
|
|
ReceivedSat: &Amount{},
|
|
SentSat: &Amount{},
|
|
SentToSelfSat: &Amount{},
|
|
}
|
|
sort.Sort(a)
|
|
for i := range a {
|
|
bh := &a[i]
|
|
time := bh.Time - bh.Time%groupByTime
|
|
if bha.Time != time {
|
|
if bha.Time != 0 {
|
|
// in aggregate, do not return txid as it could multiple of them
|
|
bha.Txid = ""
|
|
bhs = append(bhs, bha)
|
|
}
|
|
bha = BalanceHistory{
|
|
Time: time,
|
|
ReceivedSat: &Amount{},
|
|
SentSat: &Amount{},
|
|
SentToSelfSat: &Amount{},
|
|
}
|
|
}
|
|
if bha.Txid != bh.Txid {
|
|
bha.Txs += bh.Txs
|
|
bha.Txid = bh.Txid
|
|
}
|
|
(*big.Int)(bha.ReceivedSat).Add((*big.Int)(bha.ReceivedSat), (*big.Int)(bh.ReceivedSat))
|
|
(*big.Int)(bha.SentSat).Add((*big.Int)(bha.SentSat), (*big.Int)(bh.SentSat))
|
|
(*big.Int)(bha.SentToSelfSat).Add((*big.Int)(bha.SentToSelfSat), (*big.Int)(bh.SentToSelfSat))
|
|
}
|
|
if bha.Txs > 0 {
|
|
bha.Txid = ""
|
|
bhs = append(bhs, bha)
|
|
}
|
|
}
|
|
return bhs
|
|
}
|
|
|
|
// Blocks is list of blocks with paging information
|
|
type Blocks struct {
|
|
Paging
|
|
Blocks []db.BlockInfo `json:"blocks" ts_doc:"List of blocks."`
|
|
}
|
|
|
|
// BlockInfo contains extended block header data and a list of block txids
|
|
type BlockInfo struct {
|
|
Hash string `json:"hash" ts_doc:"Block hash."`
|
|
Prev string `json:"previousBlockHash,omitempty" ts_doc:"Hash of the previous block in the chain."`
|
|
Next string `json:"nextBlockHash,omitempty" ts_doc:"Hash of the next block, if known."`
|
|
Height uint32 `json:"height" ts_doc:"Block height (0-based index in the chain)."`
|
|
Confirmations int `json:"confirmations" ts_doc:"Number of confirmations of this block (distance from best chain tip)."`
|
|
Size int `json:"size" ts_doc:"Size of the block in bytes."`
|
|
Time int64 `json:"time,omitempty" ts_doc:"Timestamp of when this block was mined."`
|
|
Version common.JSONNumber `json:"version" ts_doc:"Block version (chain-specific meaning)."`
|
|
MerkleRoot string `json:"merkleRoot" ts_doc:"Merkle root of the block's transactions."`
|
|
Nonce string `json:"nonce" ts_doc:"Nonce used in the mining process."`
|
|
Bits string `json:"bits" ts_doc:"Compact representation of the target threshold."`
|
|
Difficulty string `json:"difficulty" ts_doc:"Difficulty target for mining this block."`
|
|
Txids []string `json:"tx,omitempty" ts_doc:"List of transaction IDs included in this block."`
|
|
}
|
|
|
|
// Block contains information about block
|
|
type Block struct {
|
|
Paging
|
|
BlockInfo
|
|
TxCount int `json:"txCount" ts_doc:"Total count of transactions in this block."`
|
|
Transactions []*Tx `json:"txs,omitempty" ts_doc:"List of full transaction details (if requested)."`
|
|
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty" ts_doc:"Optional aliases for addresses found in this block."`
|
|
}
|
|
|
|
// BlockRaw contains raw block in hex
|
|
type BlockRaw struct {
|
|
Hex string `json:"hex" ts_doc:"Hex-encoded block data."`
|
|
}
|
|
|
|
// BlockbookInfo contains information about the running blockbook instance
|
|
type BlockbookInfo struct {
|
|
Coin string `json:"coin" ts_doc:"Coin name, e.g. 'Bitcoin'."`
|
|
Network string `json:"network" ts_doc:"Network shortcut, e.g. 'BTC'."`
|
|
Host string `json:"host" ts_doc:"Hostname of the blockbook instance, e.g. 'backend5'."`
|
|
Version string `json:"version" ts_doc:"Running blockbook version, e.g. '0.4.0'."`
|
|
GitCommit string `json:"gitCommit" ts_doc:"Git commit hash of the running blockbook, e.g. 'a0960c8e'."`
|
|
BuildTime string `json:"buildTime" ts_doc:"Build time of running blockbook, e.g. '2024-08-08T12:32:50+00:00'."`
|
|
SyncMode bool `json:"syncMode" ts_doc:"If true, blockbook is syncing from scratch or in a special sync mode."`
|
|
InitialSync bool `json:"initialSync" ts_doc:"Indicates if blockbook is in its initial sync phase."`
|
|
InSync bool `json:"inSync" ts_doc:"Indicates if the backend is fully synced with the blockchain."`
|
|
BestHeight uint32 `json:"bestHeight" ts_doc:"Best (latest) block height according to this instance."`
|
|
LastBlockTime time.Time `json:"lastBlockTime" ts_doc:"Timestamp of the latest block in the chain."`
|
|
InSyncMempool bool `json:"inSyncMempool" ts_doc:"Indicates if mempool info is synced as well."`
|
|
LastMempoolTime time.Time `json:"lastMempoolTime" ts_doc:"Timestamp of the last mempool update."`
|
|
MempoolSize int `json:"mempoolSize" ts_doc:"Number of unconfirmed transactions in the mempool."`
|
|
Decimals int `json:"decimals" ts_doc:"Number of decimals for this coin's base unit."`
|
|
DbSize int64 `json:"dbSize" ts_doc:"Size of the underlying database in bytes."`
|
|
HasFiatRates bool `json:"hasFiatRates,omitempty" ts_doc:"Whether this instance provides fiat exchange rates."`
|
|
HasTokenFiatRates bool `json:"hasTokenFiatRates,omitempty" ts_doc:"Whether this instance provides fiat exchange rates for tokens."`
|
|
CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty" ts_doc:"Timestamp of the latest fiat rates update."`
|
|
HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty" ts_doc:"Timestamp of the latest historical fiat rates update."`
|
|
HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty" ts_doc:"Timestamp of the latest historical token fiat rates update."`
|
|
SupportedStakingPools []string `json:"supportedStakingPools,omitempty" ts_doc:"List of contract addresses supported for staking."`
|
|
DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty" ts_doc:"Optional calculated DB size from columns."`
|
|
DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty" ts_doc:"List of columns/tables in the DB for internal state."`
|
|
About string `json:"about" ts_doc:"Additional human-readable info about this blockbook instance."`
|
|
}
|
|
|
|
// SystemInfo contains information about the running blockbook and backend instance
|
|
type SystemInfo struct {
|
|
Blockbook *BlockbookInfo `json:"blockbook" ts_doc:"Blockbook instance information."`
|
|
Backend *common.BackendInfo `json:"backend" ts_doc:"Information about the connected backend node."`
|
|
}
|
|
|
|
// MempoolTxid contains information about a transaction in mempool
|
|
type MempoolTxid struct {
|
|
Time int64 `json:"time" ts_doc:"Timestamp when the transaction was received in the mempool."`
|
|
Txid string `json:"txid" ts_doc:"Transaction hash for this mempool entry."`
|
|
}
|
|
|
|
// MempoolTxids contains a list of mempool txids with paging information
|
|
type MempoolTxids struct {
|
|
Paging
|
|
Mempool []MempoolTxid `json:"mempool" ts_doc:"List of transactions currently in the mempool."`
|
|
MempoolSize int `json:"mempoolSize" ts_doc:"Number of unconfirmed transactions in the mempool."`
|
|
}
|
|
|
|
// FiatTicker contains formatted CurrencyRatesTicker data
|
|
type FiatTicker struct {
|
|
Timestamp int64 `json:"ts,omitempty" ts_doc:"Unix timestamp for these fiat rates."`
|
|
Rates map[string]float32 `json:"rates" ts_doc:"Map of currency codes to their exchange rate."`
|
|
Error string `json:"error,omitempty" ts_doc:"Any error message encountered while fetching rates."`
|
|
}
|
|
|
|
// FiatTickers contains a formatted CurrencyRatesTicker list
|
|
type FiatTickers struct {
|
|
Tickers []FiatTicker `json:"tickers" ts_doc:"List of fiat tickers with timestamps and rates."`
|
|
}
|
|
|
|
// AvailableVsCurrencies contains formatted data about available versus currencies for exchange rates
|
|
type AvailableVsCurrencies struct {
|
|
Timestamp int64 `json:"ts,omitempty" ts_doc:"Timestamp for the available currency list."`
|
|
Tickers []string `json:"available_currencies" ts_doc:"List of currency codes (e.g., USD, EUR) supported by the rates."`
|
|
Error string `json:"error,omitempty" ts_doc:"Error message, if any, when fetching the available currencies."`
|
|
}
|
|
|
|
// Eip1559Fee
|
|
type Eip1559Fee struct {
|
|
MaxFeePerGas *Amount `json:"maxFeePerGas"`
|
|
MaxPriorityFeePerGas *Amount `json:"maxPriorityFeePerGas"`
|
|
MinWaitTimeEstimate int `json:"minWaitTimeEstimate,omitempty"`
|
|
MaxWaitTimeEstimate int `json:"maxWaitTimeEstimate,omitempty"`
|
|
}
|
|
|
|
// Eip1559Fees
|
|
type Eip1559Fees struct {
|
|
BaseFeePerGas *Amount `json:"baseFeePerGas,omitempty"`
|
|
Low *Eip1559Fee `json:"low,omitempty"`
|
|
Medium *Eip1559Fee `json:"medium,omitempty"`
|
|
High *Eip1559Fee `json:"high,omitempty"`
|
|
Instant *Eip1559Fee `json:"instant,omitempty"`
|
|
NetworkCongestion float64 `json:"networkCongestion,omitempty"`
|
|
LatestPriorityFeeRange []*Amount `json:"latestPriorityFeeRange,omitempty"`
|
|
HistoricalPriorityFeeRange []*Amount `json:"historicalPriorityFeeRange,omitempty"`
|
|
HistoricalBaseFeeRange []*Amount `json:"historicalBaseFeeRange,omitempty"`
|
|
PriorityFeeTrend string `json:"priorityFeeTrend,omitempty" ts_type:"'up' | 'down'"`
|
|
BaseFeeTrend string `json:"baseFeeTrend,omitempty" ts_type:"'up' | 'down'"`
|
|
}
|
|
|
|
type LongTermFeeRate struct {
|
|
FeePerUnit string `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."`
|
|
Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."`
|
|
}
|