Add type api.Amount for proper formatting of amounts in json
parent
d01081fd83
commit
9c142663ce
114
api/types.go
114
api/types.go
|
@ -2,7 +2,6 @@ package api
|
|||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/eth"
|
||||
"blockbook/common"
|
||||
"blockbook/db"
|
||||
"encoding/json"
|
||||
|
@ -42,6 +41,17 @@ func NewAPIError(s string, public bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Amount is datatype holding amounts
|
||||
type Amount big.Int
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ScriptSig contains input script
|
||||
type ScriptSig struct {
|
||||
Hex string `json:"hex"`
|
||||
|
@ -58,8 +68,7 @@ type Vin struct {
|
|||
AddrDesc bchain.AddressDescriptor `json:"-"`
|
||||
Addresses []string `json:"addresses"`
|
||||
Searchable bool `json:"-"`
|
||||
Value string `json:"value"`
|
||||
ValueSat big.Int `json:"-"`
|
||||
ValueSat *Amount `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// ScriptPubKey contains output script and addresses derived from it
|
||||
|
@ -74,8 +83,7 @@ type ScriptPubKey struct {
|
|||
|
||||
// Vout contains information about single transaction output
|
||||
type Vout struct {
|
||||
Value string `json:"value"`
|
||||
ValueSat big.Int `json:"-"`
|
||||
ValueSat *Amount `json:"value,omitempty"`
|
||||
N int `json:"n"`
|
||||
ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
|
||||
Spent bool `json:"spent"`
|
||||
|
@ -86,50 +94,57 @@ type Vout struct {
|
|||
|
||||
// Erc20Token contains info about ERC20 token held by an address
|
||||
type Erc20Token struct {
|
||||
Contract string `json:"contract"`
|
||||
Txs int `json:"txs"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Decimals int `json:"decimals"`
|
||||
Balance string `json:"balance,omitempty"`
|
||||
BalanceSat string `json:"balanceSat,omitempty"`
|
||||
ContractIndex string `json:"-"`
|
||||
Contract string `json:"contract"`
|
||||
Txs int `json:"txs"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Decimals int `json:"decimals"`
|
||||
BalanceSat *Amount `json:"balance,omitempty"`
|
||||
ContractIndex string `json:"-"`
|
||||
}
|
||||
|
||||
// Erc20Transfer contains info about ERC20 transfer done in a transaction
|
||||
type Erc20Transfer struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Contract string `json:"contract"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Tokens string `json:"tokens"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Contract string `json:"contract"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Decimals int `json:"decimals"`
|
||||
Tokens *Amount `json:"tokens"`
|
||||
}
|
||||
|
||||
// EthereumSpecific contains ethereum specific transaction data
|
||||
type EthereumSpecific struct {
|
||||
Status int `json:"status"` // 1 OK, 0 Fail, -1 pending
|
||||
Nonce uint64 `json:"nonce"`
|
||||
GasLimit *big.Int `json:"gaslimit"`
|
||||
GasUsed *big.Int `json:"gasused"`
|
||||
GasPrice *Amount `json:"gasprice"`
|
||||
}
|
||||
|
||||
// Tx holds information about a transaction
|
||||
type Tx struct {
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version,omitempty"`
|
||||
Locktime uint32 `json:"locktime,omitempty"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
Blockhash string `json:"blockhash,omitempty"`
|
||||
Blockheight int `json:"blockheight"`
|
||||
Confirmations uint32 `json:"confirmations"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime"`
|
||||
ValueOut string `json:"valueOut"`
|
||||
ValueOutSat big.Int `json:"-"`
|
||||
Size int `json:"size,omitempty"`
|
||||
ValueIn string `json:"valueIn"`
|
||||
ValueInSat big.Int `json:"-"`
|
||||
Fees string `json:"fees"`
|
||||
FeesSat big.Int `json:"-"`
|
||||
Hex string `json:"hex,omitempty"`
|
||||
CoinSpecificData interface{} `json:"-"`
|
||||
CoinSpecificJSON json.RawMessage `json:"-"`
|
||||
Erc20Transfers []Erc20Transfer `json:"erc20transfers,omitempty"`
|
||||
EthereumSpecific *eth.EthereumTxData `json:"ethereumspecific,omitempty"`
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version,omitempty"`
|
||||
Locktime uint32 `json:"locktime,omitempty"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
Blockhash string `json:"blockhash,omitempty"`
|
||||
Blockheight int `json:"blockheight"`
|
||||
Confirmations uint32 `json:"confirmations"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime"`
|
||||
ValueOut string `json:"valueOut"`
|
||||
ValueOutSat *Amount `json:"-"`
|
||||
Size int `json:"size,omitempty"`
|
||||
ValueInSat *Amount `json:"valueIn,omitempty"`
|
||||
FeesSat *Amount `json:"fees,omitempty"`
|
||||
Hex string `json:"hex,omitempty"`
|
||||
CoinSpecificData interface{} `json:"-"`
|
||||
CoinSpecificJSON json.RawMessage `json:"-"`
|
||||
Erc20Transfers []Erc20Transfer `json:"erc20transfers,omitempty"`
|
||||
EthereumSpecific *EthereumSpecific `json:"ethereumspecific,omitempty"`
|
||||
}
|
||||
|
||||
// Paging contains information about paging for address, blocks and block
|
||||
|
@ -152,16 +167,14 @@ const AddressFilterOutputs = -3
|
|||
type Address struct {
|
||||
Paging
|
||||
AddrStr string `json:"addrStr"`
|
||||
Balance string `json:"balance"`
|
||||
BalanceSat string `json:"balanceSat"`
|
||||
TotalReceived string `json:"totalReceived,omitempty"`
|
||||
TotalSent string `json:"totalSent,omitempty"`
|
||||
UnconfirmedBalance string `json:"unconfirmedBalance"`
|
||||
UnconfirmedBalanceSat string `json:"unconfirmedBalanceSat"`
|
||||
BalanceSat *Amount `json:"balance"`
|
||||
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
|
||||
TotalSentSat *Amount `json:"totalSent,omitempty"`
|
||||
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"`
|
||||
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
|
||||
TxApperances int `json:"txApperances"`
|
||||
Transactions []*Tx `json:"txs,omitempty"`
|
||||
Txids []string `json:"transactions,omitempty"`
|
||||
Transactions []*Tx `json:"transactions,omitempty"`
|
||||
Txids []string `json:"txids,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
|
||||
Erc20Tokens []Erc20Token `json:"erc20tokens,omitempty"`
|
||||
|
@ -172,8 +185,7 @@ type Address struct {
|
|||
type AddressUtxo struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout int32 `json:"vout"`
|
||||
Amount string `json:"amount"`
|
||||
AmountSat big.Int `json:"satoshis"`
|
||||
AmountSat *Amount `json:"value"`
|
||||
Height int `json:"height,omitempty"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// build unittest
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAmount_MarshalJSON(t *testing.T) {
|
||||
type amounts struct {
|
||||
A1 Amount `json:"a1"`
|
||||
A2 Amount `json:"a2,omitempty"`
|
||||
PA1 *Amount `json:"pa1"`
|
||||
PA2 *Amount `json:"pa2,omitempty"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
a amounts
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
want: `{"a1":"0","a2":"0","pa1":null}`,
|
||||
},
|
||||
{
|
||||
name: "1",
|
||||
a: amounts{
|
||||
A1: (Amount)(*big.NewInt(123456)),
|
||||
A2: (Amount)(*big.NewInt(787901)),
|
||||
PA1: (*Amount)(big.NewInt(234567)),
|
||||
PA2: (*Amount)(big.NewInt(890123)),
|
||||
},
|
||||
want: `{"a1":"123456","a2":"787901","pa1":"234567","pa2":"890123"}`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b, err := json.Marshal(&tt.a)
|
||||
if err != nil {
|
||||
t.Errorf("json.Marshal() error = %v", err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(string(b), tt.want) {
|
||||
t.Errorf("json.Marshal() = %v, want %v", string(b), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
109
api/worker.go
109
api/worker.go
|
@ -59,7 +59,7 @@ func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) err
|
|||
} else if tsp == nil {
|
||||
glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses")
|
||||
} else if len(tsp.Inputs) > int(index) {
|
||||
if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 {
|
||||
if tsp.Inputs[index].ValueSat.Cmp((*big.Int)(vout.ValueSat)) == 0 {
|
||||
spentTx, spentHeight, err := w.txCache.GetTransaction(t)
|
||||
if err != nil {
|
||||
glog.Warning("Tx ", t, ": not found")
|
||||
|
@ -113,7 +113,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
var err error
|
||||
var ta *db.TxAddresses
|
||||
var erc20t []Erc20Transfer
|
||||
var ethSpecific *eth.EthereumTxData
|
||||
var ethSpecific *EthereumSpecific
|
||||
var blockhash string
|
||||
if bchainTx.Confirmations > 0 {
|
||||
if w.chainType == bchain.ChainBitcoinType {
|
||||
|
@ -157,7 +157,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
}
|
||||
if len(otx.Vout) > int(vin.Vout) {
|
||||
vout := &otx.Vout[vin.Vout]
|
||||
vin.ValueSat = vout.ValueSat
|
||||
vin.ValueSat = (*Amount)(&vout.ValueSat)
|
||||
vin.AddrDesc, vin.Addresses, vin.Searchable, err = w.getAddressesFromVout(vout)
|
||||
if err != nil {
|
||||
glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout)
|
||||
|
@ -166,8 +166,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
} else {
|
||||
if len(tas.Outputs) > int(vin.Vout) {
|
||||
output := &tas.Outputs[vin.Vout]
|
||||
vin.ValueSat = output.ValueSat
|
||||
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
|
||||
vin.ValueSat = (*Amount)(&output.ValueSat)
|
||||
vin.AddrDesc = output.AddrDesc
|
||||
vin.Addresses, vin.Searchable, err = output.Addresses(w.chainParser)
|
||||
if err != nil {
|
||||
|
@ -175,8 +174,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
}
|
||||
}
|
||||
}
|
||||
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
|
||||
valInSat.Add(&valInSat, &vin.ValueSat)
|
||||
valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat))
|
||||
}
|
||||
} else if w.chainType == bchain.ChainEthereumType {
|
||||
if len(bchainVin.Addresses) > 0 {
|
||||
|
@ -194,8 +192,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
bchainVout := &bchainTx.Vout[i]
|
||||
vout := &vouts[i]
|
||||
vout.N = i
|
||||
vout.ValueSat = bchainVout.ValueSat
|
||||
vout.Value = w.chainParser.AmountToDecimalString(&bchainVout.ValueSat)
|
||||
vout.ValueSat = (*Amount)(&bchainVout.ValueSat)
|
||||
valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
|
||||
vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex
|
||||
vout.ScriptPubKey.AddrDesc, vout.ScriptPubKey.Addresses, vout.ScriptPubKey.Searchable, err = w.getAddressesFromVout(bchainVout)
|
||||
|
@ -242,20 +239,28 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
Contract: e.Contract,
|
||||
From: e.From,
|
||||
To: e.To,
|
||||
Tokens: bchain.AmountToDecimalString(&e.Tokens, erc20c.Decimals),
|
||||
Decimals: erc20c.Decimals,
|
||||
Tokens: (*Amount)(&e.Tokens),
|
||||
Name: erc20c.Name,
|
||||
Symbol: erc20c.Symbol,
|
||||
}
|
||||
}
|
||||
ethSpecific = eth.GetEthereumTxData(bchainTx)
|
||||
ethTxData := eth.GetEthereumTxData(bchainTx)
|
||||
// mempool txs do not have fees yet
|
||||
if ethSpecific.GasUsed != nil {
|
||||
feesSat.Mul(ethSpecific.GasPriceNum, ethSpecific.GasUsed)
|
||||
if ethTxData.GasUsed != nil {
|
||||
feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
|
||||
}
|
||||
if len(bchainTx.Vout) > 0 {
|
||||
valInSat = bchainTx.Vout[0].ValueSat
|
||||
}
|
||||
valOutSat = valInSat
|
||||
ethSpecific = &EthereumSpecific{
|
||||
GasLimit: ethTxData.GasLimit,
|
||||
GasPrice: (*Amount)(ethTxData.GasPrice),
|
||||
GasUsed: ethTxData.GasUsed,
|
||||
Nonce: ethTxData.Nonce,
|
||||
Status: ethTxData.Status,
|
||||
}
|
||||
}
|
||||
// for now do not return size, we would have to compute vsize of segwit transactions
|
||||
// size:=len(bchainTx.Hex) / 2
|
||||
|
@ -271,15 +276,12 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
|
|||
Blockheight: int(height),
|
||||
Blocktime: bchainTx.Blocktime,
|
||||
Confirmations: bchainTx.Confirmations,
|
||||
Fees: w.chainParser.AmountToDecimalString(&feesSat),
|
||||
FeesSat: feesSat,
|
||||
FeesSat: (*Amount)(&feesSat),
|
||||
Locktime: bchainTx.LockTime,
|
||||
Time: bchainTx.Time,
|
||||
Txid: bchainTx.Txid,
|
||||
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
|
||||
ValueInSat: valInSat,
|
||||
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
|
||||
ValueOutSat: valOutSat,
|
||||
ValueInSat: (*Amount)(&valInSat),
|
||||
ValueOutSat: (*Amount)(&valOutSat),
|
||||
Version: bchainTx.Version,
|
||||
Hex: bchainTx.Hex,
|
||||
Vin: vins,
|
||||
|
@ -331,7 +333,7 @@ func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
|
|||
var val big.Int
|
||||
for _, vout := range t.Vout {
|
||||
if bytes.Equal(vout.ScriptPubKey.AddrDesc, addrDesc) {
|
||||
val.Add(&val, &vout.ValueSat)
|
||||
val.Add(&val, (*big.Int)(vout.ValueSat))
|
||||
}
|
||||
}
|
||||
return &val
|
||||
|
@ -341,7 +343,7 @@ func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
|
|||
var val big.Int
|
||||
for _, vin := range t.Vin {
|
||||
if bytes.Equal(vin.AddrDesc, addrDesc) {
|
||||
val.Add(&val, &vin.ValueSat)
|
||||
val.Add(&val, (*big.Int)(vin.ValueSat))
|
||||
}
|
||||
}
|
||||
return &val
|
||||
|
@ -371,9 +373,8 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
|
|||
tai := &ta.Inputs[i]
|
||||
vin := &vins[i]
|
||||
vin.N = i
|
||||
vin.ValueSat = tai.ValueSat
|
||||
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
|
||||
valInSat.Add(&valInSat, &vin.ValueSat)
|
||||
vin.ValueSat = (*Amount)(&tai.ValueSat)
|
||||
valInSat.Add(&valInSat, &tai.ValueSat)
|
||||
vin.Addresses, vin.Searchable, err = tai.Addresses(w.chainParser)
|
||||
if err != nil {
|
||||
glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai)
|
||||
|
@ -384,9 +385,8 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
|
|||
tao := &ta.Outputs[i]
|
||||
vout := &vouts[i]
|
||||
vout.N = i
|
||||
vout.ValueSat = tao.ValueSat
|
||||
vout.Value = w.chainParser.AmountToDecimalString(&vout.ValueSat)
|
||||
valOutSat.Add(&valOutSat, &vout.ValueSat)
|
||||
vout.ValueSat = (*Amount)(&tao.ValueSat)
|
||||
valOutSat.Add(&valOutSat, &tao.ValueSat)
|
||||
vout.ScriptPubKey.Addresses, vout.ScriptPubKey.Searchable, err = tao.Addresses(w.chainParser)
|
||||
if err != nil {
|
||||
glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao)
|
||||
|
@ -403,11 +403,11 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
|
|||
Blockheight: int(ta.Height),
|
||||
Blocktime: bi.Time,
|
||||
Confirmations: bestheight - ta.Height + 1,
|
||||
Fees: w.chainParser.AmountToDecimalString(&feesSat),
|
||||
FeesSat: (*Amount)(&feesSat),
|
||||
Time: bi.Time,
|
||||
Txid: txid,
|
||||
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
|
||||
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
|
||||
ValueInSat: (*Amount)(&valInSat),
|
||||
ValueOutSat: (*Amount)(&valOutSat),
|
||||
Vin: vins,
|
||||
Vout: vouts,
|
||||
}
|
||||
|
@ -486,14 +486,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||
} else {
|
||||
b = nil
|
||||
}
|
||||
var balance, balanceSat string
|
||||
if b != nil {
|
||||
balance = bchain.AmountToDecimalString(b, ci.Decimals)
|
||||
balanceSat = b.String()
|
||||
}
|
||||
erc20t[i] = Erc20Token{
|
||||
Balance: balance,
|
||||
BalanceSat: balanceSat,
|
||||
BalanceSat: (*Amount)(b),
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
Symbol: ci.Symbol,
|
||||
|
@ -522,15 +516,16 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
return nil, NewAPIError(fmt.Sprintf("Invalid address, %v", err), true)
|
||||
}
|
||||
var (
|
||||
ba *db.AddrBalance
|
||||
erc20t []Erc20Token
|
||||
erc20c *bchain.Erc20Contract
|
||||
txm []string
|
||||
txs []*Tx
|
||||
txids []string
|
||||
pg Paging
|
||||
uBalSat big.Int
|
||||
totalReceived, totalSent, nonce string
|
||||
ba *db.AddrBalance
|
||||
erc20t []Erc20Token
|
||||
erc20c *bchain.Erc20Contract
|
||||
txm []string
|
||||
txs []*Tx
|
||||
txids []string
|
||||
pg Paging
|
||||
uBalSat big.Int
|
||||
totalReceived, totalSent *big.Int
|
||||
nonce string
|
||||
)
|
||||
if w.chainType == bchain.ChainEthereumType {
|
||||
var n uint64
|
||||
|
@ -646,19 +641,17 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
}
|
||||
}
|
||||
if w.chainType == bchain.ChainBitcoinType {
|
||||
totalReceived = w.chainParser.AmountToDecimalString(ba.ReceivedSat())
|
||||
totalSent = w.chainParser.AmountToDecimalString(&ba.SentSat)
|
||||
totalReceived = ba.ReceivedSat()
|
||||
totalSent = &ba.SentSat
|
||||
}
|
||||
r := &Address{
|
||||
Paging: pg,
|
||||
AddrStr: address,
|
||||
Balance: w.chainParser.AmountToDecimalString(&ba.BalanceSat),
|
||||
BalanceSat: ba.BalanceSat.String(),
|
||||
TotalReceived: totalReceived,
|
||||
TotalSent: totalSent,
|
||||
BalanceSat: (*Amount)(&ba.BalanceSat),
|
||||
TotalReceivedSat: (*Amount)(totalReceived),
|
||||
TotalSentSat: (*Amount)(totalSent),
|
||||
TxApperances: int(ba.Txs),
|
||||
UnconfirmedBalance: w.chainParser.AmountToDecimalString(&uBalSat),
|
||||
UnconfirmedBalanceSat: uBalSat.String(),
|
||||
UnconfirmedBalanceSat: (*Amount)(&uBalSat),
|
||||
UnconfirmedTxApperances: len(txm),
|
||||
Transactions: txs,
|
||||
Txids: txids,
|
||||
|
@ -714,8 +707,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
r = append(r, AddressUtxo{
|
||||
Txid: bchainTx.Txid,
|
||||
Vout: int32(i),
|
||||
AmountSat: vout.ValueSat,
|
||||
Amount: w.chainParser.AmountToDecimalString(&vout.ValueSat),
|
||||
AmountSat: (*Amount)(&vout.ValueSat),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -734,7 +726,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
outpoints := make([]bchain.Outpoint, 0, 8)
|
||||
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, vout int32, isOutput bool) error {
|
||||
if isOutput {
|
||||
outpoints = append(outpoints, bchain.Outpoint{txid, vout})
|
||||
outpoints = append(outpoints, bchain.Outpoint{Txid: txid, Vout: vout})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
@ -772,8 +764,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
r = append(r, AddressUtxo{
|
||||
Txid: o.Txid,
|
||||
Vout: o.Vout,
|
||||
AmountSat: v,
|
||||
Amount: w.chainParser.AmountToDecimalString(&v),
|
||||
AmountSat: (*Amount)(&v),
|
||||
Height: int(ta.Height),
|
||||
Confirmations: bestheight - int(ta.Height) + 1,
|
||||
})
|
||||
|
|
|
@ -414,12 +414,11 @@ func GetErc20FromTx(tx *bchain.Tx) ([]Erc20Transfer, error) {
|
|||
|
||||
// EthereumTxData contains ethereum specific transaction data
|
||||
type EthereumTxData struct {
|
||||
Status int `json:"status"` // 1 OK, 0 Fail, -1 pending
|
||||
Nonce uint64 `json:"nonce"`
|
||||
GasLimit *big.Int `json:"gaslimit"`
|
||||
GasUsed *big.Int `json:"gasused"`
|
||||
GasPrice string `json:"gasprice"`
|
||||
GasPriceNum *big.Int `json:"-"`
|
||||
Status int `json:"status"` // 1 OK, 0 Fail, -1 pending
|
||||
Nonce uint64 `json:"nonce"`
|
||||
GasLimit *big.Int `json:"gaslimit"`
|
||||
GasUsed *big.Int `json:"gasused"`
|
||||
GasPrice *big.Int `json:"gasprice"`
|
||||
}
|
||||
|
||||
// GetEthereumTxData returns EthereumTxData from bchain.Tx
|
||||
|
@ -430,8 +429,7 @@ func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData {
|
|||
if csd.Tx != nil {
|
||||
etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)
|
||||
etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit)
|
||||
etd.GasPriceNum, _ = hexutil.DecodeBig(csd.Tx.GasPrice)
|
||||
etd.GasPrice = bchain.AmountToDecimalString(etd.GasPriceNum, EtherAmountDecimalPoint)
|
||||
etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice)
|
||||
}
|
||||
if csd.Receipt != nil {
|
||||
if csd.Receipt.Status == "0x1" {
|
||||
|
|
|
@ -369,11 +369,12 @@ type TemplateData struct {
|
|||
|
||||
func (s *PublicServer) parseTemplates() []*template.Template {
|
||||
templateFuncMap := template.FuncMap{
|
||||
"formatTime": formatTime,
|
||||
"formatUnixTime": formatUnixTime,
|
||||
"formatAmount": formatAmount,
|
||||
"setTxToTemplateData": setTxToTemplateData,
|
||||
"stringInSlice": stringInSlice,
|
||||
"formatTime": formatTime,
|
||||
"formatUnixTime": formatUnixTime,
|
||||
"formatAmount": s.formatAmount,
|
||||
"formatAmountWithDecimals": formatAmountWithDecimals,
|
||||
"setTxToTemplateData": setTxToTemplateData,
|
||||
"stringInSlice": stringInSlice,
|
||||
}
|
||||
t := make([]*template.Template, tplCount)
|
||||
t[errorTpl] = template.Must(template.New("error").Funcs(templateFuncMap).ParseFiles("./static/templates/error.html", "./static/templates/base.html"))
|
||||
|
@ -403,11 +404,18 @@ func formatTime(t time.Time) string {
|
|||
|
||||
// for now return the string as it is
|
||||
// in future could be used to do coin specific formatting
|
||||
func formatAmount(a string) string {
|
||||
if a == "" {
|
||||
func (s *PublicServer) formatAmount(a *api.Amount) string {
|
||||
if a == nil {
|
||||
return "0"
|
||||
}
|
||||
return a
|
||||
return s.chainParser.AmountToDecimalString((*big.Int)(a))
|
||||
}
|
||||
|
||||
func formatAmountWithDecimals(a *api.Amount, d int) string {
|
||||
if a == nil {
|
||||
return "0"
|
||||
}
|
||||
return bchain.AmountToDecimalString((*big.Int)(a), d)
|
||||
}
|
||||
|
||||
// called from template to support txdetail.html functionality
|
||||
|
|
|
@ -312,7 +312,7 @@ func txToResTx(tx *api.Tx) resTx {
|
|||
Script: &script,
|
||||
Sequence: int64(vin.Sequence),
|
||||
OutputIndex: int(vin.Vout),
|
||||
Satoshis: vin.ValueSat.Int64(),
|
||||
Satoshis: (*big.Int)(vin.ValueSat).Int64(),
|
||||
}
|
||||
if len(vin.Addresses) > 0 {
|
||||
a := vin.Addresses[0]
|
||||
|
@ -325,7 +325,7 @@ func txToResTx(tx *api.Tx) resTx {
|
|||
vout := &tx.Vout[i]
|
||||
script := vout.ScriptPubKey.Hex
|
||||
output := txOutputs{
|
||||
Satoshis: vout.ValueSat.Int64(),
|
||||
Satoshis: (*big.Int)(vout.ValueSat).Int64(),
|
||||
Script: &script,
|
||||
}
|
||||
if len(vout.ScriptPubKey.Addresses) > 0 {
|
||||
|
@ -342,15 +342,15 @@ func txToResTx(tx *api.Tx) resTx {
|
|||
}
|
||||
return resTx{
|
||||
BlockTimestamp: tx.Blocktime,
|
||||
FeeSatoshis: tx.FeesSat.Int64(),
|
||||
FeeSatoshis: (*big.Int)(tx.FeesSat).Int64(),
|
||||
Hash: tx.Txid,
|
||||
Height: h,
|
||||
Hex: tx.Hex,
|
||||
Inputs: inputs,
|
||||
InputSatoshis: tx.ValueInSat.Int64(),
|
||||
InputSatoshis: (*big.Int)(tx.ValueInSat).Int64(),
|
||||
Locktime: int(tx.Locktime),
|
||||
Outputs: outputs,
|
||||
OutputSatoshis: tx.ValueOutSat.Int64(),
|
||||
OutputSatoshis: (*big.Int)(tx.ValueOutSat).Int64(),
|
||||
Version: int(tx.Version),
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||
ads[a] = hi
|
||||
}
|
||||
hi.InputIndexes = append(hi.InputIndexes, int(vin.N))
|
||||
totalSat.Sub(&totalSat, &vin.ValueSat)
|
||||
totalSat.Sub(&totalSat, (*big.Int)(vin.ValueSat))
|
||||
}
|
||||
}
|
||||
for i := range tx.Vout {
|
||||
|
@ -420,7 +420,7 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||
ads[a] = hi
|
||||
}
|
||||
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
|
||||
totalSat.Add(&totalSat, &vout.ValueSat)
|
||||
totalSat.Add(&totalSat, (*big.Int)(vout.ValueSat))
|
||||
}
|
||||
}
|
||||
ahi := addressHistoryItem{}
|
||||
|
|
|
@ -91,7 +91,7 @@ func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxC
|
|||
return s, nil
|
||||
}
|
||||
|
||||
// allow all origins, at least for now
|
||||
// allow all origins
|
||||
func checkOrigin(r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{define "specific"}}{{$cs := .CoinShortcut}}{{$addr := .Address}}{{$data := .}}
|
||||
<h1>{{if $addr.Erc20Contract}}Contract {{$addr.Erc20Contract.Name}} ({{$addr.Erc20Contract.Symbol}}){{else}}Address{{end}}
|
||||
<small class="text-muted">{{formatAmount $addr.Balance}} {{$cs}}</small>
|
||||
<small class="text-muted">{{formatAmount $addr.BalanceSat}} {{$cs}}</small>
|
||||
</h1>
|
||||
<div class="alert alert-data ellipsis">
|
||||
<span class="data">{{$addr.AddrStr}}</span>
|
||||
|
@ -13,7 +13,7 @@
|
|||
{{- if eq .ChainType 1 -}}
|
||||
<tr>
|
||||
<td style="width: 25%;">Balance</td>
|
||||
<td class="data">{{formatAmount $addr.Balance}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $addr.BalanceSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Non-contract Transactions</td>
|
||||
|
@ -37,7 +37,7 @@
|
|||
{{- range $et := $addr.Erc20Tokens -}}
|
||||
<tr>
|
||||
<td class="data ellipsis"><a href="/address/{{$et.Contract}}">{{$et.Name}}</a></td>
|
||||
<td class="data">{{formatAmount $et.Balance}} {{$et.Symbol}}</td>
|
||||
<td class="data">{{formatAmountWithDecimals $et.BalanceSat $et.Decimals}} {{$et.Symbol}}</td>
|
||||
<td class="data">{{$et.Txs}}</td>
|
||||
</tr>
|
||||
{{- end -}}
|
||||
|
@ -50,15 +50,15 @@
|
|||
{{- else -}}
|
||||
<tr>
|
||||
<td style="width: 25%;">Total Received</td>
|
||||
<td class="data">{{formatAmount $addr.TotalReceived}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $addr.TotalReceivedSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Sent</td>
|
||||
<td class="data">{{formatAmount $addr.TotalSent}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $addr.TotalSentSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Final Balance</td>
|
||||
<td class="data">{{formatAmount $addr.Balance}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $addr.BalanceSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. Transactions</td>
|
||||
|
@ -83,7 +83,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 25%;">Unconfirmed Balance</td>
|
||||
<td class="data">{{formatAmount $addr.UnconfirmedBalance}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $addr.UnconfirmedBalanceSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>No. Transactions</td>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Value</td>
|
||||
<td class="data">{{formatAmount $tx.ValueOut}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $tx.ValueOutSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gas Used / Limit</td>
|
||||
|
@ -49,17 +49,17 @@
|
|||
{{- else -}}
|
||||
<tr>
|
||||
<td>Total Input</td>
|
||||
<td class="data">{{formatAmount $tx.ValueIn}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $tx.ValueInSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Output</td>
|
||||
<td class="data">{{formatAmount $tx.ValueOut}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $tx.ValueOutSat}} {{$cs}}</td>
|
||||
</tr>
|
||||
{{- end -}}
|
||||
{{- if $tx.Fees -}}
|
||||
{{- if $tx.FeesSat -}}
|
||||
<tr>
|
||||
<td>Fees</td>
|
||||
<td class="data">{{formatAmount $tx.Fees}} {{$cs}}</td>
|
||||
<td class="data">{{formatAmount $tx.FeesSat}} {{$cs}}</td>
|
||||
</tr>{{end -}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
{{- else -}}
|
||||
<span class="float-left">{{- if $vin.ScriptSig.Hex -}}Unparsed address{{- else -}}No Inputs (Newly Generated Coins){{- end -}}</span>
|
||||
{{- end -}}{{- if $vin.Addresses -}}
|
||||
<span class="float-right{{if stringInSlice $addr $vin.Addresses}} text-danger{{end}}">{{formatAmount $vin.Value}} {{$cs}}</span>
|
||||
<span class="float-right{{if stringInSlice $addr $vin.Addresses}} text-danger{{end}}">{{formatAmount $vin.ValueSat}} {{$cs}}</span>
|
||||
{{- end -}}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -59,7 +59,7 @@
|
|||
<span class="float-left">Unparsed address</span>
|
||||
{{- end -}}
|
||||
<span class="float-right{{if stringInSlice $addr $vout.ScriptPubKey.Addresses}} text-success{{end}}">
|
||||
{{formatAmount $vout.Value}} {{$cs}} {{if $vout.Spent}}<a class="text-danger" href="{{if $vout.SpentTxID}}/tx/{{$vout.SpentTxID}}{{else}}/spending/{{$tx.Txid}}/{{$vout.N}}{{end}}" title="Spent">➡</a>{{else -}}
|
||||
{{formatAmount $vout.ValueSat}} {{$cs}} {{if $vout.Spent}}<a class="text-danger" href="{{if $vout.SpentTxID}}/tx/{{$vout.SpentTxID}}{{else}}/spending/{{$tx.Txid}}/{{$vout.N}}{{end}}" title="Spent">➡</a>{{else -}}
|
||||
<span class="text-success" title="Unspent"> <b>×</b></span>
|
||||
{{- end -}}
|
||||
</span>
|
||||
|
@ -77,8 +77,8 @@
|
|||
</div>
|
||||
<div class="row line-top">
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{- if $tx.Fees -}}
|
||||
<span class="txvalues txvalues-default">Fee: {{formatAmount $tx.Fees}} {{$cs}}</span>
|
||||
{{- if $tx.FeesSat -}}
|
||||
<span class="txvalues txvalues-default">Fee: {{formatAmount $tx.FeesSat}} {{$cs}}</span>
|
||||
{{- end -}}
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-8 col-md-8 text-right">
|
||||
|
@ -87,7 +87,7 @@
|
|||
{{- else -}}
|
||||
<span class="txvalues txvalues-danger ng-hide">Unconfirmed Transaction!</span>
|
||||
{{- end -}}
|
||||
<span class="txvalues txvalues-primary">{{formatAmount $tx.ValueOut}} {{$cs}}</span>
|
||||
<span class="txvalues txvalues-primary">{{formatAmount $tx.ValueOutSat}} {{$cs}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 text-right" style="padding: .4rem 0;">
|
||||
{{formatAmount $tx.ValueOut}} {{$cs}}
|
||||
{{formatAmount $tx.ValueOutSat}} {{$cs}}
|
||||
</div>
|
||||
</div>
|
||||
{{- if $tx.Erc20Transfers -}}
|
||||
|
@ -106,15 +106,15 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 text-right" style="padding: .4rem 0;">{{formatAmount $erc20.Tokens}} {{$erc20.Symbol}}</div>
|
||||
<div class="col-md-3 text-right" style="padding: .4rem 0;">{{formatAmountWithDecimals $erc20.Tokens $erc20.Decimals}} {{$erc20.Symbol}}</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
<div class="row" style="padding: 6px 15px;"></div>
|
||||
{{- end -}}
|
||||
<div class="row line-top">
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{- if $tx.Fees -}}
|
||||
<span class="txvalues txvalues-default">Fee: {{formatAmount $tx.Fees}} {{$cs}}</span>
|
||||
{{- if $tx.FeesSat -}}
|
||||
<span class="txvalues txvalues-default">Fee: {{formatAmount $tx.FeesSat}} {{$cs}}</span>
|
||||
{{- end -}}
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-8 col-md-8 text-right">
|
||||
|
@ -123,7 +123,7 @@
|
|||
{{- else -}}
|
||||
<span class="txvalues txvalues-danger ng-hide">Unconfirmed Transaction!</span>
|
||||
{{- end -}}
|
||||
<span class="txvalues txvalues-primary">{{formatAmount $tx.ValueOut}} {{$cs}}</span>
|
||||
<span class="txvalues txvalues-primary">{{formatAmount $tx.ValueOutSat}} {{$cs}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue