Skip to content

Commit 5b887fd

Browse files
authored
Merge pull request #300 from uprendis/feature/add-fee-history-api
Add fee history API
2 parents 4484e47 + 35dbb9b commit 5b887fd

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

ethapi/api.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"strings"
2525
"time"
2626

27+
"github.com/Fantom-foundation/lachesis-base/inter/idx"
2728
"github.com/davecgh/go-spew/spew"
2829
"github.com/ethereum/go-ethereum/accounts"
2930
"github.com/ethereum/go-ethereum/accounts/abi"
@@ -46,8 +47,10 @@ import (
4647
"github.com/tyler-smith/go-bip39"
4748

4849
"github.com/Fantom-foundation/go-opera/evmcore"
50+
"github.com/Fantom-foundation/go-opera/gossip/gasprice"
4951
"github.com/Fantom-foundation/go-opera/opera"
5052
"github.com/Fantom-foundation/go-opera/utils/gsignercache"
53+
"github.com/Fantom-foundation/go-opera/utils/piecefunc"
5154
)
5255

5356
var (
@@ -84,6 +87,103 @@ func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.
8487
return (*hexutil.Big)(tipcap), err
8588
}
8689

90+
type feeHistoryResult struct {
91+
OldestBlock *hexutil.Big `json:"oldestBlock"`
92+
Reward [][]*hexutil.Big `json:"reward,omitempty"`
93+
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
94+
GasUsedRatio []float64 `json:"gasUsedRatio"`
95+
}
96+
97+
func scaleGasTip(tip, baseFee *big.Int, ratio uint64) *big.Int {
98+
// max((SuggestedGasTip+minGasPrice)*0.6-minGasPrice, 0)
99+
min := baseFee
100+
est := new(big.Int).Set(tip)
101+
est.Add(est, min)
102+
est.Mul(est, new(big.Int).SetUint64(ratio))
103+
est.Div(est, gasprice.DecimalUnitBn)
104+
est.Sub(est, min)
105+
if est.Sign() < 0 {
106+
return new(big.Int)
107+
}
108+
109+
return est
110+
}
111+
112+
var tipScaleRatio = piecefunc.NewFunc([]piecefunc.Dot{
113+
{
114+
X: 0,
115+
Y: 0.7 * gasprice.DecimalUnit,
116+
},
117+
{
118+
X: 0.2 * gasprice.DecimalUnit,
119+
Y: 1.0 * gasprice.DecimalUnit,
120+
},
121+
{
122+
X: 0.8 * gasprice.DecimalUnit,
123+
Y: 1.2 * gasprice.DecimalUnit,
124+
},
125+
{
126+
X: 1.0 * gasprice.DecimalUnit,
127+
Y: 2.0 * gasprice.DecimalUnit,
128+
},
129+
})
130+
131+
var errInvalidPercentile = errors.New("invalid reward percentile")
132+
133+
func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
134+
res := &feeHistoryResult{}
135+
res.Reward = make([][]*hexutil.Big, 0, blockCount)
136+
res.BaseFee = make([]*hexutil.Big, 0, blockCount)
137+
res.GasUsedRatio = make([]float64, 0, blockCount)
138+
res.OldestBlock = (*hexutil.Big)(new(big.Int))
139+
140+
// validate input parameters
141+
if blockCount == 0 {
142+
return res, nil
143+
}
144+
if blockCount > 1024 {
145+
blockCount = 1024
146+
}
147+
for i, p := range rewardPercentiles {
148+
if p < 0 || p > 100 {
149+
return nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
150+
}
151+
if i > 0 && p < rewardPercentiles[i-1] {
152+
return nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
153+
}
154+
}
155+
last, err := s.b.ResolveRpcBlockNumberOrHash(ctx, rpc.BlockNumberOrHash{BlockNumber: &lastBlock})
156+
if err != nil {
157+
return nil, err
158+
}
159+
oldest := last
160+
if oldest > idx.Block(blockCount) {
161+
oldest -= idx.Block(blockCount - 1)
162+
} else {
163+
oldest = 0
164+
}
165+
166+
baseFee := s.b.MinGasPrice()
167+
goldTip, err := s.b.SuggestGasTipCap(ctx)
168+
if err != nil {
169+
return nil, err
170+
}
171+
172+
tips := make([]*hexutil.Big, 0, len(rewardPercentiles))
173+
for _, p := range rewardPercentiles {
174+
ratio := tipScaleRatio(uint64(gasprice.DecimalUnit * p / 100.0))
175+
scaledTip := scaleGasTip(goldTip, baseFee, ratio)
176+
tips = append(tips, (*hexutil.Big)(scaledTip))
177+
}
178+
res.OldestBlock.ToInt().SetUint64(uint64(oldest))
179+
for i := uint64(0); i < uint64(last-oldest+1); i++ {
180+
res.Reward = append(res.Reward, tips)
181+
res.BaseFee = append(res.BaseFee, (*hexutil.Big)(baseFee))
182+
res.GasUsedRatio = append(res.GasUsedRatio, 0.99)
183+
}
184+
return res, nil
185+
}
186+
87187
// Syncing returns true if node is syncing
88188
func (s *PublicEthereumAPI) Syncing() (interface{}, error) {
89189
progress := s.b.Progress()

ethapi/backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type Backend interface {
6868
HeaderByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmHeader, error)
6969
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmBlock, error)
7070
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *evmcore.EvmHeader, error)
71-
//GetHeader(ctx context.Context, hash common.Hash) *evmcore.EvmHeader
71+
ResolveRpcBlockNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (idx.Block, error)
7272
BlockByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmBlock, error)
7373
GetReceiptsByNumber(ctx context.Context, number rpc.BlockNumber) (types.Receipts, error)
7474
GetTd(hash common.Hash) *big.Int

gossip/ethapi_backend.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ func (b *EthAPIBackend) CurrentBlock() *evmcore.EvmBlock {
5151
return b.state.CurrentBlock()
5252
}
5353

54+
func (b *EthAPIBackend) ResolveRpcBlockNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (idx.Block, error) {
55+
latest := b.svc.store.GetLatestBlockIndex()
56+
if number, ok := blockNrOrHash.Number(); ok && (number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber) {
57+
return latest, nil
58+
} else if number, ok := blockNrOrHash.Number(); ok {
59+
if idx.Block(number) > latest {
60+
return 0, errors.New("block not found")
61+
}
62+
return idx.Block(number), nil
63+
} else if h, ok := blockNrOrHash.Hash(); ok {
64+
index := b.svc.store.GetBlockIndex(hash.Event(h))
65+
if index == nil {
66+
return 0, errors.New("block not found")
67+
}
68+
return *index, nil
69+
}
70+
return 0, errors.New("unknown header selector")
71+
}
72+
5473
// HeaderByNumber returns evm block header by its number, or nil if not exists.
5574
func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmHeader, error) {
5675
blk, err := b.BlockByNumber(ctx, number)

0 commit comments

Comments
 (0)