Compare commits
4 Commits
deepcrayon
...
sendToSelf
Author | SHA1 | Date |
---|---|---|
Martin Boehm | 45fa80d060 | |
Martin Boehm | 7782107ba0 | |
Martin Boehm | 8126c6e476 | |
Martin Boehm | c4e75d4f2d |
12
api/types.go
12
api/types.go
|
@ -226,6 +226,8 @@ const (
|
||||||
AddressFilterVoutInputs = -2
|
AddressFilterVoutInputs = -2
|
||||||
// AddressFilterVoutOutputs specifies that only txs where the address is as output are returned
|
// AddressFilterVoutOutputs specifies that only txs where the address is as output are returned
|
||||||
AddressFilterVoutOutputs = -3
|
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 - return only tokens with nonzero balance
|
||||||
TokensToReturnNonzeroBalance TokensToReturn = 0
|
TokensToReturnNonzeroBalance TokensToReturn = 0
|
||||||
|
@ -305,6 +307,7 @@ type BalanceHistory struct {
|
||||||
Txs uint32 `json:"txs"`
|
Txs uint32 `json:"txs"`
|
||||||
ReceivedSat *Amount `json:"received"`
|
ReceivedSat *Amount `json:"received"`
|
||||||
SentSat *Amount `json:"sent"`
|
SentSat *Amount `json:"sent"`
|
||||||
|
SentToSelfSat *Amount `json:"sentToSelf"`
|
||||||
FiatRates map[string]float64 `json:"rates,omitempty"`
|
FiatRates map[string]float64 `json:"rates,omitempty"`
|
||||||
Txid string `json:"txid,omitempty"`
|
Txid string `json:"txid,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -328,8 +331,9 @@ func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories
|
||||||
bhs := make(BalanceHistories, 0)
|
bhs := make(BalanceHistories, 0)
|
||||||
if len(a) > 0 {
|
if len(a) > 0 {
|
||||||
bha := BalanceHistory{
|
bha := BalanceHistory{
|
||||||
SentSat: &Amount{},
|
|
||||||
ReceivedSat: &Amount{},
|
ReceivedSat: &Amount{},
|
||||||
|
SentSat: &Amount{},
|
||||||
|
SentToSelfSat: &Amount{},
|
||||||
}
|
}
|
||||||
sort.Sort(a)
|
sort.Sort(a)
|
||||||
for i := range a {
|
for i := range a {
|
||||||
|
@ -343,16 +347,18 @@ func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories
|
||||||
}
|
}
|
||||||
bha = BalanceHistory{
|
bha = BalanceHistory{
|
||||||
Time: time,
|
Time: time,
|
||||||
SentSat: &Amount{},
|
|
||||||
ReceivedSat: &Amount{},
|
ReceivedSat: &Amount{},
|
||||||
|
SentSat: &Amount{},
|
||||||
|
SentToSelfSat: &Amount{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bha.Txid != bh.Txid {
|
if bha.Txid != bh.Txid {
|
||||||
bha.Txs += bh.Txs
|
bha.Txs += bh.Txs
|
||||||
bha.Txid = bh.Txid
|
bha.Txid = bh.Txid
|
||||||
}
|
}
|
||||||
(*big.Int)(bha.SentSat).Add((*big.Int)(bha.SentSat), (*big.Int)(bh.SentSat))
|
|
||||||
(*big.Int)(bha.ReceivedSat).Add((*big.Int)(bha.ReceivedSat), (*big.Int)(bh.ReceivedSat))
|
(*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 {
|
if bha.Txs > 0 {
|
||||||
bha.Txid = ""
|
bha.Txid = ""
|
||||||
|
|
|
@ -69,6 +69,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(1)),
|
ReceivedSat: (*Amount)(big.NewInt(1)),
|
||||||
SentSat: (*Amount)(big.NewInt(2)),
|
SentSat: (*Amount)(big.NewInt(2)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(1)),
|
||||||
Time: 1521514812,
|
Time: 1521514812,
|
||||||
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -79,6 +80,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(1)),
|
ReceivedSat: (*Amount)(big.NewInt(1)),
|
||||||
SentSat: (*Amount)(big.NewInt(2)),
|
SentSat: (*Amount)(big.NewInt(2)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(1)),
|
||||||
Time: 1521514800,
|
Time: 1521514800,
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
},
|
},
|
||||||
|
@ -90,6 +92,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(1)),
|
ReceivedSat: (*Amount)(big.NewInt(1)),
|
||||||
SentSat: (*Amount)(big.NewInt(2)),
|
SentSat: (*Amount)(big.NewInt(2)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(0)),
|
||||||
Time: 1521504812,
|
Time: 1521504812,
|
||||||
Txid: "0011223344556677889900112233445566778899001122334455667788990011",
|
Txid: "0011223344556677889900112233445566778899001122334455667788990011",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -97,6 +100,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(3)),
|
ReceivedSat: (*Amount)(big.NewInt(3)),
|
||||||
SentSat: (*Amount)(big.NewInt(4)),
|
SentSat: (*Amount)(big.NewInt(4)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(2)),
|
||||||
Time: 1521504812,
|
Time: 1521504812,
|
||||||
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -104,6 +108,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(5)),
|
ReceivedSat: (*Amount)(big.NewInt(5)),
|
||||||
SentSat: (*Amount)(big.NewInt(6)),
|
SentSat: (*Amount)(big.NewInt(6)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(3)),
|
||||||
Time: 1521514812,
|
Time: 1521514812,
|
||||||
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -111,6 +116,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(7)),
|
ReceivedSat: (*Amount)(big.NewInt(7)),
|
||||||
SentSat: (*Amount)(big.NewInt(8)),
|
SentSat: (*Amount)(big.NewInt(8)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(3)),
|
||||||
Time: 1521504812,
|
Time: 1521504812,
|
||||||
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -118,6 +124,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(9)),
|
ReceivedSat: (*Amount)(big.NewInt(9)),
|
||||||
SentSat: (*Amount)(big.NewInt(10)),
|
SentSat: (*Amount)(big.NewInt(10)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(5)),
|
||||||
Time: 1521534812,
|
Time: 1521534812,
|
||||||
Txid: "0011223344556677889900112233445566778899001122334455667788990011",
|
Txid: "0011223344556677889900112233445566778899001122334455667788990011",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -125,6 +132,7 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(11)),
|
ReceivedSat: (*Amount)(big.NewInt(11)),
|
||||||
SentSat: (*Amount)(big.NewInt(12)),
|
SentSat: (*Amount)(big.NewInt(12)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(6)),
|
||||||
Time: 1521534812,
|
Time: 1521534812,
|
||||||
Txid: "1122334455667788990011223344556677889900112233445566778899001100",
|
Txid: "1122334455667788990011223344556677889900112233445566778899001100",
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
|
@ -135,18 +143,21 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(11)),
|
ReceivedSat: (*Amount)(big.NewInt(11)),
|
||||||
SentSat: (*Amount)(big.NewInt(14)),
|
SentSat: (*Amount)(big.NewInt(14)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(5)),
|
||||||
Time: 1521504000,
|
Time: 1521504000,
|
||||||
Txs: 2,
|
Txs: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(5)),
|
ReceivedSat: (*Amount)(big.NewInt(5)),
|
||||||
SentSat: (*Amount)(big.NewInt(6)),
|
SentSat: (*Amount)(big.NewInt(6)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(3)),
|
||||||
Time: 1521514800,
|
Time: 1521514800,
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ReceivedSat: (*Amount)(big.NewInt(20)),
|
ReceivedSat: (*Amount)(big.NewInt(20)),
|
||||||
SentSat: (*Amount)(big.NewInt(22)),
|
SentSat: (*Amount)(big.NewInt(22)),
|
||||||
|
SentToSelfSat: (*Amount)(big.NewInt(11)),
|
||||||
Time: 1521532800,
|
Time: 1521532800,
|
||||||
Txs: 2,
|
Txs: 2,
|
||||||
},
|
},
|
||||||
|
|
134
api/worker.go
134
api/worker.go
|
@ -401,6 +401,13 @@ func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
|
||||||
}
|
}
|
||||||
return &val
|
return &val
|
||||||
}
|
}
|
||||||
|
func (t *Tx) getAddrEthereumTypeInputValue(addrDesc bchain.AddressDescriptor) *big.Int {
|
||||||
|
var val big.Int
|
||||||
|
if len(t.Vin) > 0 && len(t.Vout) > 0 && bytes.Equal(t.Vin[0].AddrDesc, addrDesc) {
|
||||||
|
val.Add(&val, (*big.Int)(t.Vout[0].ValueSat))
|
||||||
|
}
|
||||||
|
return &val
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
|
func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
|
||||||
var val big.Int
|
var val big.Int
|
||||||
|
@ -497,6 +504,44 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
|
||||||
}, from, to, page
|
}, from, to, page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDescriptor, details AccountDetails, txs int) (*Token, error) {
|
||||||
|
var b *big.Int
|
||||||
|
validContract := true
|
||||||
|
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", contract)
|
||||||
|
}
|
||||||
|
if ci == nil {
|
||||||
|
ci = &bchain.Erc20Contract{}
|
||||||
|
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract)
|
||||||
|
if len(addresses) > 0 {
|
||||||
|
ci.Contract = addresses[0]
|
||||||
|
ci.Name = addresses[0]
|
||||||
|
}
|
||||||
|
validContract = false
|
||||||
|
}
|
||||||
|
// do not read contract balances etc in case of Basic option
|
||||||
|
if details >= AccountDetailsTokenBalances && validContract {
|
||||||
|
b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, contract)
|
||||||
|
if err != nil {
|
||||||
|
// return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
|
||||||
|
glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, contract, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b = nil
|
||||||
|
}
|
||||||
|
return &Token{
|
||||||
|
Type: ERC20TokenType,
|
||||||
|
BalanceSat: (*Amount)(b),
|
||||||
|
Contract: ci.Contract,
|
||||||
|
Name: ci.Name,
|
||||||
|
Symbol: ci.Symbol,
|
||||||
|
Transfers: txs,
|
||||||
|
Decimals: ci.Decimals,
|
||||||
|
ContractIndex: strconv.Itoa(index),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) {
|
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) {
|
||||||
var (
|
var (
|
||||||
ba *db.AddrBalance
|
ba *db.AddrBalance
|
||||||
|
@ -544,44 +589,27 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||||
// filter only transactions of this contract
|
// filter only transactions of this contract
|
||||||
filter.Vout = i + 1
|
filter.Vout = i + 1
|
||||||
}
|
}
|
||||||
validContract := true
|
t, err := w.getEthereumToken(i+1, addrDesc, c.Contract, details, int(c.Txs))
|
||||||
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
|
return nil, nil, nil, 0, 0, 0, err
|
||||||
}
|
|
||||||
if ci == nil {
|
|
||||||
ci = &bchain.Erc20Contract{}
|
|
||||||
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract)
|
|
||||||
if len(addresses) > 0 {
|
|
||||||
ci.Contract = addresses[0]
|
|
||||||
ci.Name = addresses[0]
|
|
||||||
}
|
|
||||||
validContract = false
|
|
||||||
}
|
|
||||||
// do not read contract balances etc in case of Basic option
|
|
||||||
if details >= AccountDetailsTokenBalances && validContract {
|
|
||||||
b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract)
|
|
||||||
if err != nil {
|
|
||||||
// return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
|
|
||||||
glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b = nil
|
|
||||||
}
|
|
||||||
tokens[j] = Token{
|
|
||||||
Type: ERC20TokenType,
|
|
||||||
BalanceSat: (*Amount)(b),
|
|
||||||
Contract: ci.Contract,
|
|
||||||
Name: ci.Name,
|
|
||||||
Symbol: ci.Symbol,
|
|
||||||
Transfers: int(c.Txs),
|
|
||||||
Decimals: ci.Decimals,
|
|
||||||
ContractIndex: strconv.Itoa(i + 1),
|
|
||||||
}
|
}
|
||||||
|
tokens[j] = *t
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
|
// special handling if filter has contract
|
||||||
|
// if the address has no transactions with given contract, check the balance, the address may have some balance even without transactions
|
||||||
|
if len(filterDesc) > 0 && j == 0 && details >= AccountDetailsTokens {
|
||||||
|
t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, 0, 0, 0, err
|
||||||
|
}
|
||||||
|
tokens = []Token{*t}
|
||||||
|
// switch off query for transactions, there are no transactions
|
||||||
|
filter.Vout = AddressFilterVoutQueryNotNecessary
|
||||||
|
} else {
|
||||||
tokens = tokens[:j]
|
tokens = tokens[:j]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, 0, 0, 0, err
|
return nil, nil, nil, 0, 0, 0, err
|
||||||
|
@ -594,6 +622,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||||
totalResults = int(ca.NonContractTxs)
|
totalResults = int(ca.NonContractTxs)
|
||||||
} else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) {
|
} else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) {
|
||||||
totalResults = int(ca.Contracts[filter.Vout-1].Txs)
|
totalResults = int(ca.Contracts[filter.Vout-1].Txs)
|
||||||
|
} else if filter.Vout == AddressFilterVoutQueryNotNecessary {
|
||||||
|
totalResults = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nonContractTxs = int(ca.NonContractTxs)
|
nonContractTxs = int(ca.NonContractTxs)
|
||||||
|
@ -737,7 +767,12 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
||||||
if tx.Confirmations == 0 {
|
if tx.Confirmations == 0 {
|
||||||
unconfirmedTxs++
|
unconfirmedTxs++
|
||||||
uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc))
|
uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc))
|
||||||
|
// for ethereum take the value from vout, vin is always empty
|
||||||
|
if w.chainType == bchain.ChainEthereumType {
|
||||||
|
uBalSat.Sub(&uBalSat, tx.getAddrEthereumTypeInputValue(addrDesc))
|
||||||
|
} else {
|
||||||
uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc))
|
uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc))
|
||||||
|
}
|
||||||
if page == 0 {
|
if page == 0 {
|
||||||
if option == AccountDetailsTxidHistory {
|
if option == AccountDetailsTxidHistory {
|
||||||
txids = append(txids, tx.Txid)
|
txids = append(txids, tx.Txid)
|
||||||
|
@ -750,7 +785,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// get tx history if requested by option or check mempool if there are some transactions for a new address
|
// get tx history if requested by option or check mempool if there are some transactions for a new address
|
||||||
if option >= AccountDetailsTxidHistory {
|
if option >= AccountDetailsTxidHistory && filter.Vout != AddressFilterVoutQueryNotNecessary {
|
||||||
txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage)
|
txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
|
return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
|
||||||
|
@ -821,7 +856,7 @@ func (w *Worker) balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp int64) (
|
||||||
return fromUnix, fromHeight, toUnix, toHeight
|
return fromUnix, fromHeight, toUnix, toHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32) (*BalanceHistory, error) {
|
func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32, selfAddrDesc map[string]struct{}) (*BalanceHistory, error) {
|
||||||
var time uint32
|
var time uint32
|
||||||
var err error
|
var err error
|
||||||
var ta *db.TxAddresses
|
var ta *db.TxAddresses
|
||||||
|
@ -856,15 +891,28 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
|
||||||
bh := BalanceHistory{
|
bh := BalanceHistory{
|
||||||
Time: time,
|
Time: time,
|
||||||
Txs: 1,
|
Txs: 1,
|
||||||
SentSat: &Amount{},
|
|
||||||
ReceivedSat: &Amount{},
|
ReceivedSat: &Amount{},
|
||||||
|
SentSat: &Amount{},
|
||||||
|
SentToSelfSat: &Amount{},
|
||||||
Txid: txid,
|
Txid: txid,
|
||||||
}
|
}
|
||||||
|
countSentToSelf := false
|
||||||
if w.chainType == bchain.ChainBitcoinType {
|
if w.chainType == bchain.ChainBitcoinType {
|
||||||
|
// detect if this input is the first of selfAddrDesc
|
||||||
|
// to not to count sentToSelf multiple times if counting multiple xpub addresses
|
||||||
|
ownInputIndex := -1
|
||||||
for i := range ta.Inputs {
|
for i := range ta.Inputs {
|
||||||
tai := &ta.Inputs[i]
|
tai := &ta.Inputs[i]
|
||||||
|
if _, found := selfAddrDesc[string(tai.AddrDesc)]; found {
|
||||||
|
if ownInputIndex < 0 {
|
||||||
|
ownInputIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
if bytes.Equal(addrDesc, tai.AddrDesc) {
|
if bytes.Equal(addrDesc, tai.AddrDesc) {
|
||||||
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat)
|
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat)
|
||||||
|
if ownInputIndex == i {
|
||||||
|
countSentToSelf = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range ta.Outputs {
|
for i := range ta.Outputs {
|
||||||
|
@ -872,6 +920,11 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
|
||||||
if bytes.Equal(addrDesc, tao.AddrDesc) {
|
if bytes.Equal(addrDesc, tao.AddrDesc) {
|
||||||
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
|
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
|
||||||
}
|
}
|
||||||
|
if countSentToSelf {
|
||||||
|
if _, found := selfAddrDesc[string(tao.AddrDesc)]; found {
|
||||||
|
(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &tao.ValueSat)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if w.chainType == bchain.ChainEthereumType {
|
} else if w.chainType == bchain.ChainEthereumType {
|
||||||
var value big.Int
|
var value big.Int
|
||||||
|
@ -889,6 +942,9 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
|
||||||
if bytes.Equal(addrDesc, txAddrDesc) {
|
if bytes.Equal(addrDesc, txAddrDesc) {
|
||||||
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &value)
|
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &value)
|
||||||
}
|
}
|
||||||
|
if _, found := selfAddrDesc[string(txAddrDesc)]; found {
|
||||||
|
countSentToSelf = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -903,6 +959,11 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
|
||||||
// add sent amount only for OK transactions, however fees always
|
// add sent amount only for OK transactions, however fees always
|
||||||
if ethTxData.Status == 1 {
|
if ethTxData.Status == 1 {
|
||||||
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &value)
|
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &value)
|
||||||
|
if countSentToSelf {
|
||||||
|
if _, found := selfAddrDesc[string(txAddrDesc)]; found {
|
||||||
|
(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &value)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var feesSat big.Int
|
var feesSat big.Int
|
||||||
// mempool txs do not have fees yet
|
// mempool txs do not have fees yet
|
||||||
|
@ -963,8 +1024,9 @@ func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
selfAddrDesc := map[string]struct{}{string(addrDesc): {}}
|
||||||
for txi := len(txs) - 1; txi >= 0; txi-- {
|
for txi := len(txs) - 1; txi >= 0; txi-- {
|
||||||
bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix)
|
bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix, selfAddrDesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -606,12 +606,18 @@ func (w *Worker) GetXpubBalanceHistory(xpub string, fromTimestamp, toTimestamp i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
selfAddrDesc := make(map[string]struct{})
|
||||||
|
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
|
||||||
|
for i := range da {
|
||||||
|
selfAddrDesc[string(da[i].addrDesc)] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
|
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
|
||||||
for i := range da {
|
for i := range da {
|
||||||
ad := &da[i]
|
ad := &da[i]
|
||||||
txids := ad.txids
|
txids := ad.txids
|
||||||
for txi := len(txids) - 1; txi >= 0; txi-- {
|
for txi := len(txids) - 1; txi >= 0; txi-- {
|
||||||
bh, err := w.balanceHistoryForTxid(ad.addrDesc, txids[txi].txid, fromUnix, toUnix)
|
bh, err := w.balanceHistoryForTxid(ad.addrDesc, txids[txi].txid, fromUnix, toUnix, selfAddrDesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
ethereum "github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
@ -721,6 +722,18 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui
|
||||||
if ok && len(s) > 0 {
|
if ok && len(s) > 0 {
|
||||||
msg.Data = ethcommon.FromHex(s)
|
msg.Data = ethcommon.FromHex(s)
|
||||||
}
|
}
|
||||||
|
s, ok = getStringFromMap("value", params)
|
||||||
|
if ok && len(s) > 0 {
|
||||||
|
msg.Value, _ = hexutil.DecodeBig(s)
|
||||||
|
}
|
||||||
|
s, ok = getStringFromMap("gas", params)
|
||||||
|
if ok && len(s) > 0 {
|
||||||
|
msg.Gas, _ = hexutil.DecodeUint64(s)
|
||||||
|
}
|
||||||
|
s, ok = getStringFromMap("gasPrice", params)
|
||||||
|
if ok && len(s) > 0 {
|
||||||
|
msg.GasPrice, _ = hexutil.DecodeBig(s)
|
||||||
|
}
|
||||||
return b.client.EstimateGas(ctx, msg)
|
return b.client.EstimateGas(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -674,6 +674,7 @@ Example response (fiatcurrency not specified):
|
||||||
"txs": 5,
|
"txs": 5,
|
||||||
"received": "5000000",
|
"received": "5000000",
|
||||||
"sent": "0",
|
"sent": "0",
|
||||||
|
"sentToSelf":"100000",
|
||||||
"rates": {
|
"rates": {
|
||||||
"usd": 7855.9,
|
"usd": 7855.9,
|
||||||
"eur": 6838.13,
|
"eur": 6838.13,
|
||||||
|
@ -685,6 +686,7 @@ Example response (fiatcurrency not specified):
|
||||||
"txs": 1,
|
"txs": 1,
|
||||||
"received": "0",
|
"received": "0",
|
||||||
"sent": "5000000",
|
"sent": "5000000",
|
||||||
|
"sentToSelf":"0",
|
||||||
"rates": {
|
"rates": {
|
||||||
"usd": 8283.11,
|
"usd": 8283.11,
|
||||||
"eur": 7464.45,
|
"eur": 7464.45,
|
||||||
|
@ -703,6 +705,7 @@ Example response (fiatcurrency=usd):
|
||||||
"txs": 5,
|
"txs": 5,
|
||||||
"received": "5000000",
|
"received": "5000000",
|
||||||
"sent": "0",
|
"sent": "0",
|
||||||
|
"sentToSelf":"0",
|
||||||
"rates": {
|
"rates": {
|
||||||
"usd": 7855.9
|
"usd": 7855.9
|
||||||
}
|
}
|
||||||
|
@ -712,6 +715,7 @@ Example response (fiatcurrency=usd):
|
||||||
"txs": 1,
|
"txs": 1,
|
||||||
"received": "0",
|
"received": "0",
|
||||||
"sent": "5000000",
|
"sent": "5000000",
|
||||||
|
"sentToSelf":"0",
|
||||||
"rates": {
|
"rates": {
|
||||||
"usd": 8283.11
|
"usd": 8283.11
|
||||||
}
|
}
|
||||||
|
@ -728,6 +732,7 @@ Example response (fiatcurrency=usd&groupBy=172800):
|
||||||
"txs": 6,
|
"txs": 6,
|
||||||
"received": "5000000",
|
"received": "5000000",
|
||||||
"sent": "5000000",
|
"sent": "5000000",
|
||||||
|
"sentToSelf":"0",
|
||||||
"rates": {
|
"rates": {
|
||||||
"usd": 7734.45
|
"usd": 7734.45
|
||||||
}
|
}
|
||||||
|
@ -735,6 +740,8 @@ Example response (fiatcurrency=usd&groupBy=172800):
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The value of `sentToSelf` is the amount sent from the same address to the same address or within addresses of xpub.
|
||||||
|
|
||||||
### Websocket API
|
### Websocket API
|
||||||
|
|
||||||
Websocket interface is provided at `/websocket/`. The interface can be explored using Blockbook Websocket Test Page found at `/test-websocket.html`.
|
Websocket interface is provided at `/websocket/`. The interface can be explored using Blockbook Websocket Test Page found at `/test-websocket.html`.
|
||||||
|
|
|
@ -776,7 +776,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"24690","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","rates":{"eur":1303,"usd":2003}}]`,
|
`[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","sentToSelf":"0","rates":{"eur":1303,"usd":2003}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -785,7 +785,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","rates":{"eur":1303,"usd":2003}}]`,
|
`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","sentToSelf":"9000","rates":{"eur":1303,"usd":2003}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -794,7 +794,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","rates":{"eur":1301}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","rates":{"eur":1303}}]`,
|
`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","sentToSelf":"0","rates":{"eur":1301}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","sentToSelf":"9000","rates":{"eur":1303}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -803,7 +803,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"24690","sent":"0","rates":{"eur":1301,"usd":2001}}]`,
|
`[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -812,7 +812,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","rates":{"eur":1303,"usd":2003}}]`,
|
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1303,"usd":2003}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -821,7 +821,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}}]`,
|
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -830,7 +830,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"usd":2001}}]`,
|
`[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"usd":2001}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -839,7 +839,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
body: []string{
|
||||||
`[{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","rates":{"eur":1303,"usd":2003}}]`,
|
`[{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1303,"usd":2003}}]`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1370,7 +1370,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
"descriptor": "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz",
|
"descriptor": "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"id":"32","data":[{"time":1521514800,"txs":1,"received":"24690","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","rates":{"eur":1303,"usd":2003}}]}`,
|
want: `{"id":"32","data":[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","sentToSelf":"0","rates":{"eur":1303,"usd":2003}}]}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "websocket getBalanceHistory xpub",
|
name: "websocket getBalanceHistory xpub",
|
||||||
|
@ -1380,7 +1380,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
"descriptor": dbtestdata.Xpub,
|
"descriptor": dbtestdata.Xpub,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","rates":{"eur":1303,"usd":2003}}]}`,
|
want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1303,"usd":2003}}]}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd]",
|
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd]",
|
||||||
|
@ -1393,7 +1393,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
"currencies": []string{"usd"},
|
"currencies": []string{"usd"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"id":"34","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"usd":2001}}]}`,
|
want: `{"id":"34","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"usd":2001}}]}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd, eur, incorrect]",
|
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd, eur, incorrect]",
|
||||||
|
@ -1406,7 +1406,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
"currencies": []string{"usd", "eur", "incorrect"},
|
"currencies": []string{"usd", "eur", "incorrect"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"id":"35","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"incorrect":-1,"usd":2001}}]}`,
|
want: `{"id":"35","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"incorrect":-1,"usd":2001}}]}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[]",
|
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[]",
|
||||||
|
@ -1419,7 +1419,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||||
"currencies": []string{},
|
"currencies": []string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"id":"36","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}}]}`,
|
want: `{"id":"36","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}}]}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,7 @@
|
||||||
var specific = document.getElementById('estimateFeeSpecific').value.trim();
|
var specific = document.getElementById('estimateFeeSpecific').value.trim();
|
||||||
if (specific) {
|
if (specific) {
|
||||||
// example for bitcoin type: {"conservative": false,"txsize":1234}
|
// example for bitcoin type: {"conservative": false,"txsize":1234}
|
||||||
// example for ethereum type: {"from":"0x65513ecd11fd3a5b1fefdcc6a500b025008405a2","to":"0x65513ecd11fd3a5b1fefdcc6a500b025008405a2","data":"0xabcd"}
|
// example for ethereum type: {"from":"0x65513ecd11fd3a5b1fefdcc6a500b025008405a2","to":"0x65513ecd11fd3a5b1fefdcc6a500b025008405a2","data":"0xabcd","gasPrice":"0x30d40","value":"0x1234"}
|
||||||
specific = JSON.parse(specific)
|
specific = JSON.parse(specific)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in New Issue