Skip to content

Commit

Permalink
Merge pull request #300 from uprendis/feature/add-fee-history-api
Browse files Browse the repository at this point in the history
Add fee history API
  • Loading branch information
uprendis authored May 5, 2022
2 parents 4484e47 + 35dbb9b commit 5b887fd
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 1 deletion.
100 changes: 100 additions & 0 deletions ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"
"time"

"github.com/Fantom-foundation/lachesis-base/inter/idx"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi"
Expand All @@ -46,8 +47,10 @@ import (
"github.com/tyler-smith/go-bip39"

"github.com/Fantom-foundation/go-opera/evmcore"
"github.com/Fantom-foundation/go-opera/gossip/gasprice"
"github.com/Fantom-foundation/go-opera/opera"
"github.com/Fantom-foundation/go-opera/utils/gsignercache"
"github.com/Fantom-foundation/go-opera/utils/piecefunc"
)

var (
Expand Down Expand Up @@ -84,6 +87,103 @@ func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.
return (*hexutil.Big)(tipcap), err
}

type feeHistoryResult struct {
OldestBlock *hexutil.Big `json:"oldestBlock"`
Reward [][]*hexutil.Big `json:"reward,omitempty"`
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
GasUsedRatio []float64 `json:"gasUsedRatio"`
}

func scaleGasTip(tip, baseFee *big.Int, ratio uint64) *big.Int {
// max((SuggestedGasTip+minGasPrice)*0.6-minGasPrice, 0)
min := baseFee
est := new(big.Int).Set(tip)
est.Add(est, min)
est.Mul(est, new(big.Int).SetUint64(ratio))
est.Div(est, gasprice.DecimalUnitBn)
est.Sub(est, min)
if est.Sign() < 0 {
return new(big.Int)
}

return est
}

var tipScaleRatio = piecefunc.NewFunc([]piecefunc.Dot{
{
X: 0,
Y: 0.7 * gasprice.DecimalUnit,
},
{
X: 0.2 * gasprice.DecimalUnit,
Y: 1.0 * gasprice.DecimalUnit,
},
{
X: 0.8 * gasprice.DecimalUnit,
Y: 1.2 * gasprice.DecimalUnit,
},
{
X: 1.0 * gasprice.DecimalUnit,
Y: 2.0 * gasprice.DecimalUnit,
},
})

var errInvalidPercentile = errors.New("invalid reward percentile")

func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
res := &feeHistoryResult{}
res.Reward = make([][]*hexutil.Big, 0, blockCount)
res.BaseFee = make([]*hexutil.Big, 0, blockCount)
res.GasUsedRatio = make([]float64, 0, blockCount)
res.OldestBlock = (*hexutil.Big)(new(big.Int))

// validate input parameters
if blockCount == 0 {
return res, nil
}
if blockCount > 1024 {
blockCount = 1024
}
for i, p := range rewardPercentiles {
if p < 0 || p > 100 {
return nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
}
if i > 0 && p < rewardPercentiles[i-1] {
return nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
}
}
last, err := s.b.ResolveRpcBlockNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockNumber: &lastBlock})
if err != nil {
return nil, err
}
oldest := last
if oldest > idx.Block(blockCount) {
oldest -= idx.Block(blockCount - 1)
} else {
oldest = 0
}

baseFee := s.b.MinGasPrice()
goldTip, err := s.b.SuggestGasTipCap(ctx)
if err != nil {
return nil, err
}

tips := make([]*hexutil.Big, 0, len(rewardPercentiles))
for _, p := range rewardPercentiles {
ratio := tipScaleRatio(uint64(gasprice.DecimalUnit * p / 100.0))
scaledTip := scaleGasTip(goldTip, baseFee, ratio)
tips = append(tips, (*hexutil.Big)(scaledTip))
}
res.OldestBlock.ToInt().SetUint64(uint64(oldest))
for i := uint64(0); i < uint64(last-oldest+1); i++ {
res.Reward = append(res.Reward, tips)
res.BaseFee = append(res.BaseFee, (*hexutil.Big)(baseFee))
res.GasUsedRatio = append(res.GasUsedRatio, 0.99)
}
return res, nil
}

// Syncing returns true if node is syncing
func (s *PublicEthereumAPI) Syncing() (interface{}, error) {
progress := s.b.Progress()
Expand Down
2 changes: 1 addition & 1 deletion ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ type Backend interface {
HeaderByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmHeader, error)
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmBlock, error)
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *evmcore.EvmHeader, error)
//GetHeader(ctx context.Context, hash common.Hash) *evmcore.EvmHeader
ResolveRpcBlockNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (idx.Block, error)
BlockByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmBlock, error)
GetReceiptsByNumber(ctx context.Context, number rpc.BlockNumber) (types.Receipts, error)
GetTd(hash common.Hash) *big.Int
Expand Down
19 changes: 19 additions & 0 deletions gossip/ethapi_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ func (b *EthAPIBackend) CurrentBlock() *evmcore.EvmBlock {
return b.state.CurrentBlock()
}

func (b *EthAPIBackend) ResolveRpcBlockNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (idx.Block, error) {
latest := b.svc.store.GetLatestBlockIndex()
if number, ok := blockNrOrHash.Number(); ok && (number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber) {
return latest, nil
} else if number, ok := blockNrOrHash.Number(); ok {
if idx.Block(number) > latest {
return 0, errors.New("block not found")
}
return idx.Block(number), nil
} else if h, ok := blockNrOrHash.Hash(); ok {
index := b.svc.store.GetBlockIndex(hash.Event(h))
if index == nil {
return 0, errors.New("block not found")
}
return *index, nil
}
return 0, errors.New("unknown header selector")
}

// HeaderByNumber returns evm block header by its number, or nil if not exists.
func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmHeader, error) {
blk, err := b.BlockByNumber(ctx, number)
Expand Down

0 comments on commit 5b887fd

Please sign in to comment.