Add support of staking pools
This commit is contained in:
parent
f03c625def
commit
ac46385f49
16
api/types.go
16
api/types.go
@ -316,6 +316,20 @@ type AddressFilter struct {
|
||||
OnlyConfirmed bool
|
||||
}
|
||||
|
||||
// StakingPool holds data about address participation in a staking pool contract
|
||||
type StakingPool struct {
|
||||
Contract string `json:"contract"`
|
||||
Name string `json:"name"`
|
||||
PendingBalance *Amount `json:"pendingBalance"`
|
||||
PendingDepositedBalance *Amount `json:"pendingDepositedBalance"`
|
||||
DepositedBalance *Amount `json:"depositedBalance"`
|
||||
WithdrawTotalAmount *Amount `json:"withdrawTotalAmount"`
|
||||
ClaimableAmount *Amount `json:"claimableAmount"`
|
||||
PendingRestakedReward *Amount `json:"pendingRestakedReward"`
|
||||
RestakedReward *Amount `json:"restakedReward"`
|
||||
AutocompoundBalance *Amount `json:"autocompoundBalance"`
|
||||
}
|
||||
|
||||
// Address holds information about address and its transactions
|
||||
type Address struct {
|
||||
Paging
|
||||
@ -342,6 +356,7 @@ type Address struct {
|
||||
ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"`
|
||||
Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated
|
||||
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
|
||||
StakingPools []StakingPool `json:"stakingPools,omitempty"`
|
||||
// helpers for explorer
|
||||
Filter string `json:"-"`
|
||||
XPubAddresses map[string]struct{} `json:"-"`
|
||||
@ -504,6 +519,7 @@ type BlockbookInfo struct {
|
||||
CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"`
|
||||
HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"`
|
||||
HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"`
|
||||
SupportedStakingPools []string `json:"supportedStakingPools,omitempty"`
|
||||
DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"`
|
||||
DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"`
|
||||
About string `json:"about"`
|
||||
|
||||
@ -1058,6 +1058,7 @@ type ethereumTypeAddressData struct {
|
||||
totalResults int
|
||||
tokensBaseValue float64
|
||||
tokensSecondaryValue float64
|
||||
stakingPools []StakingPool
|
||||
}
|
||||
|
||||
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter, secondaryCoin string) (*db.AddrBalance, *ethereumTypeAddressData, error) {
|
||||
@ -1157,9 +1158,42 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||
filter.Vout = AddressFilterVoutQueryNotNecessary
|
||||
d.totalResults = -1
|
||||
}
|
||||
// if staking pool enabled, fetch the staking pool details
|
||||
if details >= AccountDetailsTokenBalances {
|
||||
d.stakingPools, err = w.getStakingPoolsData(addrDesc)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return ba, &d, nil
|
||||
}
|
||||
|
||||
func (w *Worker) getStakingPoolsData(addrDesc bchain.AddressDescriptor) ([]StakingPool, error) {
|
||||
var pools []StakingPool
|
||||
if len(w.chain.EthereumTypeGetSupportedStakingPools()) > 0 {
|
||||
sp, err := w.chain.EthereumTypeGetStakingPoolsData(addrDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range sp {
|
||||
p := &sp[i]
|
||||
pools = append(pools, StakingPool{
|
||||
Contract: p.Contract,
|
||||
Name: p.Name,
|
||||
PendingBalance: (*Amount)(&p.PendingBalance),
|
||||
PendingDepositedBalance: (*Amount)(&p.PendingDepositedBalance),
|
||||
DepositedBalance: (*Amount)(&p.DepositedBalance),
|
||||
WithdrawTotalAmount: (*Amount)(&p.WithdrawTotalAmount),
|
||||
ClaimableAmount: (*Amount)(&p.ClaimableAmount),
|
||||
PendingRestakedReward: (*Amount)(&p.PendingRestakedReward),
|
||||
RestakedReward: (*Amount)(&p.RestakedReward),
|
||||
AutocompoundBalance: (*Amount)(&p.AutocompoundBalance),
|
||||
})
|
||||
}
|
||||
}
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*Tx, error) {
|
||||
var tx *Tx
|
||||
var err error
|
||||
@ -1401,6 +1435,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
||||
ContractInfo: ed.contractInfo,
|
||||
Nonce: ed.nonce,
|
||||
AddressAliases: w.getAddressAliases(addresses),
|
||||
StakingPools: ed.stakingPools,
|
||||
}
|
||||
// keep address backward compatible, set deprecated Erc20Contract value if ERC20 token
|
||||
if ed.contractInfo != nil && ed.contractInfo.Type == bchain.ERC20TokenType {
|
||||
@ -2379,6 +2414,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
|
||||
CurrentFiatRatesTime: nonZeroTime(currentFiatRatesTime),
|
||||
HistoricalFiatRatesTime: nonZeroTime(w.is.HistoricalFiatRatesTime),
|
||||
HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime),
|
||||
SupportedStakingPools: w.chain.EthereumTypeGetSupportedStakingPools(),
|
||||
DbSize: w.db.DatabaseSizeOnDisk(),
|
||||
DbSizeFromColumns: internalDBSize,
|
||||
DbColumns: columnStats,
|
||||
|
||||
@ -41,30 +41,38 @@ func (b *BaseChain) GetMempoolEntry(txid string) (*MempoolEntry, error) {
|
||||
|
||||
// EthereumTypeGetBalance is not supported
|
||||
func (b *BaseChain) EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) {
|
||||
return nil, errors.New("Not supported")
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
|
||||
// EthereumTypeGetNonce is not supported
|
||||
func (b *BaseChain) EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error) {
|
||||
return 0, errors.New("Not supported")
|
||||
return 0, errors.New("not supported")
|
||||
}
|
||||
|
||||
// EthereumTypeEstimateGas is not supported
|
||||
func (b *BaseChain) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) {
|
||||
return 0, errors.New("Not supported")
|
||||
return 0, errors.New("not supported")
|
||||
}
|
||||
|
||||
// GetContractInfo is not supported
|
||||
func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) {
|
||||
return nil, errors.New("Not supported")
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
|
||||
// EthereumTypeGetErc20ContractBalance is not supported
|
||||
func (b *BaseChain) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) {
|
||||
return nil, errors.New("Not supported")
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
|
||||
// GetContractInfo returns URI of non fungible or multi token defined by token id
|
||||
func (p *BaseChain) GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error) {
|
||||
return "", errors.New("Not supported")
|
||||
return "", errors.New("not supported")
|
||||
}
|
||||
|
||||
func (b *BaseChain) EthereumTypeGetSupportedStakingPools() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BaseChain) EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) ([]StakingPoolData, error) {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
|
||||
@ -332,6 +332,15 @@ func (c *blockChainWithMetrics) GetTokenURI(contractDesc bchain.AddressDescripto
|
||||
return c.b.GetTokenURI(contractDesc, tokenID)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) EthereumTypeGetSupportedStakingPools() []string {
|
||||
return c.b.EthereumTypeGetSupportedStakingPools()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) EthereumTypeGetStakingPoolsData(addrDesc bchain.AddressDescriptor) (v []bchain.StakingPoolData, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeStakingPoolsData", s, err) }(time.Now())
|
||||
return c.b.EthereumTypeGetStakingPoolsData(addrDesc)
|
||||
}
|
||||
|
||||
type mempoolWithMetrics struct {
|
||||
mempool bchain.Mempool
|
||||
m *common.Metrics
|
||||
|
||||
@ -337,9 +337,9 @@ func (b *EthereumRPC) GetContractInfo(contractDesc bchain.AddressDescriptor) (*b
|
||||
|
||||
// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address
|
||||
func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) {
|
||||
addr := hexutil.Encode(addrDesc)
|
||||
addr := hexutil.Encode(addrDesc)[2:]
|
||||
contract := hexutil.Encode(contractDesc)
|
||||
req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:]
|
||||
req := contractBalanceOfSignature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr):] + addr
|
||||
data, err := b.ethCall(req, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -56,23 +56,26 @@ type Configuration struct {
|
||||
// EthereumRPC is an interface to JSON-RPC eth service.
|
||||
type EthereumRPC struct {
|
||||
*bchain.BaseChain
|
||||
Client bchain.EVMClient
|
||||
RPC bchain.EVMRPCClient
|
||||
MainNetChainID Network
|
||||
Timeout time.Duration
|
||||
Parser *EthereumParser
|
||||
PushHandler func(bchain.NotificationType)
|
||||
OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error)
|
||||
Mempool *bchain.MempoolEthereumType
|
||||
mempoolInitialized bool
|
||||
bestHeaderLock sync.Mutex
|
||||
bestHeader bchain.EVMHeader
|
||||
bestHeaderTime time.Time
|
||||
NewBlock bchain.EVMNewBlockSubscriber
|
||||
newBlockSubscription bchain.EVMClientSubscription
|
||||
NewTx bchain.EVMNewTxSubscriber
|
||||
newTxSubscription bchain.EVMClientSubscription
|
||||
ChainConfig *Configuration
|
||||
Client bchain.EVMClient
|
||||
RPC bchain.EVMRPCClient
|
||||
MainNetChainID Network
|
||||
Timeout time.Duration
|
||||
Parser *EthereumParser
|
||||
PushHandler func(bchain.NotificationType)
|
||||
OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error)
|
||||
Mempool *bchain.MempoolEthereumType
|
||||
mempoolInitialized bool
|
||||
bestHeaderLock sync.Mutex
|
||||
bestHeader bchain.EVMHeader
|
||||
bestHeaderTime time.Time
|
||||
NewBlock bchain.EVMNewBlockSubscriber
|
||||
newBlockSubscription bchain.EVMClientSubscription
|
||||
NewTx bchain.EVMNewTxSubscriber
|
||||
newTxSubscription bchain.EVMClientSubscription
|
||||
ChainConfig *Configuration
|
||||
supportedStakingPools []string
|
||||
stakingPoolNames []string
|
||||
stakingPoolContracts []string
|
||||
}
|
||||
|
||||
// ProcessInternalTransactions specifies if internal transactions are processed
|
||||
@ -155,6 +158,12 @@ func (b *EthereumRPC) Initialize() error {
|
||||
default:
|
||||
return errors.Errorf("Unknown network id %v", id)
|
||||
}
|
||||
|
||||
err = b.initStakingPools(b.ChainConfig.CoinShortcut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.Info("rpc: block chain ", b.Network)
|
||||
|
||||
return nil
|
||||
|
||||
150
bchain/coins/eth/stakingpool.go
Normal file
150
bchain/coins/eth/stakingpool.go
Normal file
@ -0,0 +1,150 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/glog"
|
||||
"github.com/juju/errors"
|
||||
"github.com/trezor/blockbook/bchain"
|
||||
)
|
||||
|
||||
func (b *EthereumRPC) initStakingPools(coinShortcut string) error {
|
||||
// for now only single staking pool
|
||||
envVar := strings.ToUpper(coinShortcut) + "_STAKING_POOL_CONTRACT"
|
||||
envValue := os.Getenv(envVar)
|
||||
if envValue != "" {
|
||||
parts := strings.Split(envValue, "/")
|
||||
if len(parts) != 2 {
|
||||
glog.Errorf("Wrong format of environment variable %s=%s, expecting value '<pool name>/<pool contract>', staking pools not enabled", envVar, envValue)
|
||||
return nil
|
||||
}
|
||||
b.supportedStakingPools = []string{envValue}
|
||||
b.stakingPoolNames = []string{parts[0]}
|
||||
b.stakingPoolContracts = []string{parts[1]}
|
||||
glog.Info("Support of staking pools enabled with these pools: ", b.supportedStakingPools)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) EthereumTypeGetSupportedStakingPools() []string {
|
||||
return b.supportedStakingPools
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) EthereumTypeGetStakingPoolsData(addrDesc bchain.AddressDescriptor) ([]bchain.StakingPoolData, error) {
|
||||
// for now only single staking pool - Everstake
|
||||
addr := hexutil.Encode(addrDesc)[2:]
|
||||
if len(b.supportedStakingPools) == 1 {
|
||||
data, err := b.everstakePoolData(addr, b.stakingPoolContracts[0], b.stakingPoolNames[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data != nil {
|
||||
return []bchain.StakingPoolData{*data}, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
const everstakePendingBalanceOfMethodSignature = "0x59b8c763" // pendingBalanceOf(address)
|
||||
const everstakePendingDepositedBalanceOfMethodSignature = "0x80f14ecc" // pendingDepositedBalanceOf(address)
|
||||
const everstakeDepositedBalanceOfMethodSignature = "0x68b48254" // depositedBalanceOf(address)
|
||||
const everstakeWithdrawRequestMethodSignature = "0x14cbc46a" // withdrawRequest(address)
|
||||
const everstakePendingRestakedRewardOfMethodSignature = "0x376d1884" // pendingRestakedRewardOf(address)
|
||||
const everstakeRestakedRewardOfMethodSignature = "0x0c98929a" // restakedRewardOf(address)
|
||||
const everstakeAutocompoundBalanceOfMethodSignature = "0x2fec7966" // autocompoundBalanceOf(address)
|
||||
|
||||
func isZeroBigInt(b *big.Int) bool {
|
||||
return len(b.Bits()) == 0
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) everstakeBalanceTypeContractCall(signature, addr, contract string) (string, error) {
|
||||
req := signature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr):] + addr
|
||||
return b.ethCall(req, contract)
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) everstakeContractCallSimpleNumeric(signature, addr, contract string) (*big.Int, error) {
|
||||
data, err := b.everstakeBalanceTypeContractCall(signature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := parseSimpleNumericProperty(data)
|
||||
if r == nil {
|
||||
return nil, errors.New("Invalid balance")
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) everstakePoolData(addr, contract, name string) (*bchain.StakingPoolData, error) {
|
||||
poolData := bchain.StakingPoolData{
|
||||
Contract: contract,
|
||||
Name: name,
|
||||
}
|
||||
allZeros := true
|
||||
|
||||
value, err := b.everstakeContractCallSimpleNumeric(everstakePendingBalanceOfMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolData.PendingBalance = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
value, err = b.everstakeContractCallSimpleNumeric(everstakePendingDepositedBalanceOfMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolData.PendingDepositedBalance = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
value, err = b.everstakeContractCallSimpleNumeric(everstakeDepositedBalanceOfMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolData.DepositedBalance = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
data, err := b.everstakeBalanceTypeContractCall(everstakeWithdrawRequestMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value = parseSimpleNumericProperty(data)
|
||||
if value == nil {
|
||||
return nil, errors.New("Invalid balance")
|
||||
}
|
||||
poolData.WithdrawTotalAmount = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
value = parseSimpleNumericProperty(data[64+2:])
|
||||
if value == nil {
|
||||
return nil, errors.New("Invalid balance")
|
||||
}
|
||||
poolData.ClaimableAmount = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
value, err = b.everstakeContractCallSimpleNumeric(everstakePendingRestakedRewardOfMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolData.PendingRestakedReward = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
value, err = b.everstakeContractCallSimpleNumeric(everstakeRestakedRewardOfMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolData.RestakedReward = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
value, err = b.everstakeContractCallSimpleNumeric(everstakeAutocompoundBalanceOfMethodSignature, addr, contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolData.AutocompoundBalance = *value
|
||||
allZeros = allZeros && isZeroBigInt(value)
|
||||
|
||||
if allZeros {
|
||||
return nil, nil
|
||||
}
|
||||
return &poolData, nil
|
||||
}
|
||||
@ -333,6 +333,8 @@ type BlockChain interface {
|
||||
EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error)
|
||||
EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error)
|
||||
EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error)
|
||||
EthereumTypeGetSupportedStakingPools() []string
|
||||
EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) ([]StakingPoolData, error)
|
||||
GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error)
|
||||
}
|
||||
|
||||
|
||||
@ -146,3 +146,17 @@ type EthereumBlockSpecificData struct {
|
||||
AddressAliasRecords []AddressAliasRecord
|
||||
Contracts []ContractInfo
|
||||
}
|
||||
|
||||
// StakingPool holds data about address participation in a staking pool contract
|
||||
type StakingPoolData struct {
|
||||
Contract string `json:"contract"`
|
||||
Name string `json:"name"`
|
||||
PendingBalance big.Int `json:"pendingBalance"` // pendingBalanceOf method
|
||||
PendingDepositedBalance big.Int `json:"pendingDepositedBalance"` // pendingDepositedBalanceOf method
|
||||
DepositedBalance big.Int `json:"depositedBalance"` // depositedBalanceOf method
|
||||
WithdrawTotalAmount big.Int `json:"withdrawTotalAmount"` // withdrawRequest method, return value [0]
|
||||
ClaimableAmount big.Int `json:"claimableAmount"` // withdrawRequest method, return value [1]
|
||||
PendingRestakedReward big.Int `json:"pendingRestakedReward"` // pendingRestakedRewardOf method
|
||||
RestakedReward big.Int `json:"restakedReward"` // restakedRewardOf method
|
||||
AutocompoundBalance big.Int `json:"autocompoundBalance"` // autocompoundBalanceOf method
|
||||
}
|
||||
|
||||
@ -109,6 +109,17 @@ export interface FeeStats {
|
||||
averageFeePerKb: number;
|
||||
decilesFeePerKb: number[];
|
||||
}
|
||||
export interface StakingPool {
|
||||
contract: string;
|
||||
pendingBalance: string;
|
||||
pendingDepositedBalance: string;
|
||||
depositedBalance: string;
|
||||
withdrawTotalAmount: string;
|
||||
claimableAmount: string;
|
||||
pendingRestakedReward: string;
|
||||
restakedReward: string;
|
||||
autocompoundBalance: string;
|
||||
}
|
||||
export interface ContractInfo {
|
||||
type: string;
|
||||
contract: string;
|
||||
@ -161,6 +172,7 @@ export interface Address {
|
||||
contractInfo?: ContractInfo;
|
||||
erc20Contract?: ContractInfo;
|
||||
addressAliases?: { [key: string]: AddressAlias };
|
||||
stakingPools?: StakingPool[];
|
||||
}
|
||||
export interface Utxo {
|
||||
txid: string;
|
||||
@ -264,6 +276,7 @@ export interface BlockbookInfo {
|
||||
currentFiatRatesTime?: string;
|
||||
historicalFiatRatesTime?: string;
|
||||
historicalTokenFiatRatesTime?: string;
|
||||
stakingPoolContracts?: string[];
|
||||
dbSizeFromColumns?: number;
|
||||
dbColumns?: InternalStateColumn[];
|
||||
about: string;
|
||||
|
||||
@ -221,6 +221,64 @@
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if $addr.StakingPools }}
|
||||
<div class="accordion mt-2 mb-2" id="stakingPools">
|
||||
<div class="accordion-item">
|
||||
<div class="accordion-header" id="stakingPoolsHeading">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stakingPoolsBody" aria-expanded="false" aria-controls="stakingPoolsBody">
|
||||
<div class="row g-0 w-100">
|
||||
<h5 class="col-12 mb-md-0">Staking Pools <span class="badge bg-secondary">{{len $addr.StakingPools}}</span></span></h5>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div id="stakingPoolsBody" class="accordion-collapse collapse" aria-labelledby="stakingPoolsHeading" data-bs-parent="#stakingPools">
|
||||
<div class="accordion-body">
|
||||
{{range $sp := $addr.StakingPools}}
|
||||
<table class="table data-table info-table mt-0 mb-2 ml-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2" style="white-space: nowrap;"><span class="h5" style="color: var(--bs-body-color);">{{$sp.Name}}</span> {{$sp.Contract}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 25%;">Pending Balance</td>
|
||||
<td>{{amountSpan $sp.PendingBalance $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pending Deposited Balance</td>
|
||||
<td>{{amountSpan $sp.PendingDepositedBalance $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 25%;">Deposited Balance</td>
|
||||
<td>{{amountSpan $sp.DepositedBalance $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Withdrawal Total Amount</td>
|
||||
<td>{{amountSpan $sp.WithdrawTotalAmount $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 25%;">Claimable Amount</td>
|
||||
<td>{{amountSpan $sp.ClaimableAmount $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pending Restaked Reward</td>
|
||||
<td>{{amountSpan $sp.PendingRestakedReward $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Restaked Reward</td>
|
||||
<td>{{amountSpan $sp.RestakedReward $data "copyable"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Autocompound Balance</td>
|
||||
<td>{{amountSpan $sp.AutocompoundBalance $data "copyable"}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if or $addr.Transactions $addr.Filter}}
|
||||
<div class="row pt-3 pb-1">
|
||||
|
||||
@ -64,6 +64,12 @@
|
||||
<td>Size On Disk</td>
|
||||
<td>{{formatInt64 $bb.DbSize}}</td>
|
||||
</tr>
|
||||
{{if $bb.SupportedStakingPools}}
|
||||
<tr>
|
||||
<td>Supported Staking Pools</td>
|
||||
<td>{{$bb.SupportedStakingPools}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user