feat_: change how we compute fees

This commit is contained in:
Anthony 2024-06-19 11:20:41 +02:00 committed by Anthony Laibe
parent 2b7d153826
commit fb261e4a0e
5 changed files with 95 additions and 134 deletions

View File

@ -24,7 +24,7 @@ import (
type CommunityTokenFees struct { type CommunityTokenFees struct {
GasUnits uint64 `json:"gasUnits"` GasUnits uint64 `json:"gasUnits"`
SuggestedFees *router.SuggestedFees `json:"suggestedFees"` SuggestedFees *router.SuggestedFeesGwei `json:"suggestedFees"`
} }
func weiToGwei(val *big.Int) *big.Float { func weiToGwei(val *big.Int) *big.Float {
@ -356,7 +356,7 @@ func (s *Service) mintTokensGasUnits(ctx context.Context, chainID uint64, contra
} }
func (s *Service) prepareCommunityTokenFees(ctx context.Context, from common.Address, to *common.Address, gasUnits uint64, chainID uint64) (*CommunityTokenFees, error) { func (s *Service) prepareCommunityTokenFees(ctx context.Context, from common.Address, to *common.Address, gasUnits uint64, chainID uint64) (*CommunityTokenFees, error) {
suggestedFees, err := s.feeManager.SuggestedFees(ctx, chainID) suggestedFees, err := s.feeManager.SuggestedFeesGwei(ctx, chainID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -373,7 +373,7 @@ func (s *Service) prepareCommunityTokenFees(ctx context.Context, from common.Add
}, nil }, nil
} }
func (s *Service) suggestedFeesToSendTxArgs(from common.Address, to *common.Address, gas uint64, suggestedFees *router.SuggestedFees) transactions.SendTxArgs { func (s *Service) suggestedFeesToSendTxArgs(from common.Address, to *common.Address, gas uint64, suggestedFees *router.SuggestedFeesGwei) transactions.SendTxArgs {
sendArgs := transactions.SendTxArgs{} sendArgs := transactions.SendTxArgs{}
sendArgs.From = types.Address(from) sendArgs.From = types.Address(from)
sendArgs.To = (*types.Address)(to) sendArgs.To = (*types.Address)(to)

View File

@ -416,9 +416,9 @@ func (api *API) FetchTokenDetails(ctx context.Context, symbols []string) (map[st
return api.s.marketManager.FetchTokenDetails(symbols) return api.s.marketManager.FetchTokenDetails(symbols)
} }
func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*router.SuggestedFees, error) { func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*router.SuggestedFeesGwei, error) {
log.Debug("call to GetSuggestedFees") log.Debug("call to GetSuggestedFees")
return api.router.GetFeesManager().SuggestedFees(ctx, chainID) return api.router.GetFeesManager().SuggestedFeesGwei(ctx, chainID)
} }
func (api *API) GetEstimatedLatestBlockNumber(ctx context.Context, chainID uint64) (uint64, error) { func (api *API) GetEstimatedLatestBlockNumber(ctx context.Context, chainID uint64) (uint64, error) {
@ -429,7 +429,12 @@ func (api *API) GetEstimatedLatestBlockNumber(ctx context.Context, chainID uint6
// @deprecated // @deprecated
func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) (router.TransactionEstimation, error) { func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) (router.TransactionEstimation, error) {
log.Debug("call to getTransactionEstimatedTime") log.Debug("call to getTransactionEstimatedTime")
return api.router.GetFeesManager().TransactionEstimatedTime(ctx, chainID, maxFeePerGas), nil return api.router.GetFeesManager().TransactionEstimatedTime(ctx, chainID, gweiToWei(maxFeePerGas)), nil
}
func gweiToWei(val *big.Float) *big.Int {
res, _ := new(big.Float).Mul(val, big.NewFloat(1000000000)).Int(nil)
return res
} }
func (api *API) GetSuggestedRoutes( func (api *API) GetSuggestedRoutes(

View File

@ -24,10 +24,25 @@ const (
GasFeeHigh GasFeeHigh
) )
// ////////////////////////////////////////////////////////////////////////////// type MaxFeesLevels struct {
// TODO: remove `SuggestedFees` struct once new router is in place Low *big.Int `json:"low"`
// ////////////////////////////////////////////////////////////////////////////// Medium *big.Int `json:"medium"`
High *big.Int `json:"high"`
}
type SuggestedFees struct { type SuggestedFees struct {
GasPrice *big.Int `json:"gasPrice"`
BaseFee *big.Int `json:"baseFee"`
MaxFeesLevels *MaxFeesLevels `json:"maxFeesLevels"`
MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"`
L1GasFee *big.Float `json:"l1GasFee,omitempty"`
EIP1559Enabled bool `json:"eip1559Enabled"`
}
// //////////////////////////////////////////////////////////////////////////////
// TODO: remove `SuggestedFeesGwei` struct once new router is in place
// //////////////////////////////////////////////////////////////////////////////
type SuggestedFeesGwei struct {
GasPrice *big.Float `json:"gasPrice"` GasPrice *big.Float `json:"gasPrice"`
BaseFee *big.Float `json:"baseFee"` BaseFee *big.Float `json:"baseFee"`
MaxPriorityFeePerGas *big.Float `json:"maxPriorityFeePerGas"` MaxPriorityFeePerGas *big.Float `json:"maxPriorityFeePerGas"`
@ -38,13 +53,23 @@ type SuggestedFees struct {
EIP1559Enabled bool `json:"eip1559Enabled"` EIP1559Enabled bool `json:"eip1559Enabled"`
} }
type PriorityFees struct { func (s *SuggestedFees) feeFor(mode GasFeeMode) *big.Int {
Low *big.Int `json:"low"` if !s.EIP1559Enabled {
Medium *big.Int `json:"medium"` return s.GasPrice
High *big.Int `json:"high"` }
if mode == GasFeeLow {
return s.MaxFeesLevels.Low
}
if mode == GasFeeHigh {
return s.MaxFeesLevels.High
}
return s.MaxFeesLevels.Medium
} }
func (s *SuggestedFees) feeFor(mode GasFeeMode) *big.Float { func (s *SuggestedFeesGwei) feeFor(mode GasFeeMode) *big.Float {
if !s.EIP1559Enabled { if !s.EIP1559Enabled {
return s.GasPrice return s.GasPrice
} }
@ -99,12 +124,6 @@ func gweiToWei(val *big.Float) *big.Int {
return res return res
} }
// //////////////////////////////////////////////////////////////////////////////
// TODO: remove `suggestedFees` function once new router is in place
//
// But we should check the client since this function is exposed to API as `GetSuggestedFees` call.
// Maybe we should keep it and remove it later when the client is ready for that change.
// //////////////////////////////////////////////////////////////////////////////
func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*SuggestedFees, error) { func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*SuggestedFees, error) {
backend, err := f.RPCClient.EthClient(chainID) backend, err := f.RPCClient.EthClient(chainID)
if err != nil { if err != nil {
@ -117,12 +136,14 @@ func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*Sugges
maxPriorityFeePerGas, err := backend.SuggestGasTipCap(ctx) maxPriorityFeePerGas, err := backend.SuggestGasTipCap(ctx)
if err != nil { if err != nil {
return &SuggestedFees{ return &SuggestedFees{
GasPrice: weiToGwei(gasPrice), GasPrice: gasPrice,
BaseFee: big.NewFloat(0), BaseFee: big.NewInt(0),
MaxPriorityFeePerGas: big.NewFloat(0), MaxPriorityFeePerGas: big.NewInt(0),
MaxFeePerGasLow: big.NewFloat(0), MaxFeesLevels: &MaxFeesLevels{
MaxFeePerGasMedium: big.NewFloat(0), Low: big.NewInt(0),
MaxFeePerGasHigh: big.NewFloat(0), Medium: big.NewInt(0),
High: big.NewInt(0),
},
EIP1559Enabled: false, EIP1559Enabled: false,
}, nil }, nil
} }
@ -132,50 +153,35 @@ func (f *FeeManager) SuggestedFees(ctx context.Context, chainID uint64) (*Sugges
return nil, err return nil, err
} }
fees, err := f.getFeeHistorySorted(chainID)
if err != nil {
return &SuggestedFees{ return &SuggestedFees{
GasPrice: weiToGwei(gasPrice), GasPrice: gasPrice,
BaseFee: weiToGwei(baseFee), BaseFee: baseFee,
MaxPriorityFeePerGas: weiToGwei(maxPriorityFeePerGas), MaxPriorityFeePerGas: maxPriorityFeePerGas,
MaxFeePerGasLow: weiToGwei(maxPriorityFeePerGas), MaxFeesLevels: &MaxFeesLevels{
MaxFeePerGasMedium: weiToGwei(maxPriorityFeePerGas), Low: new(big.Int).Add(baseFee, maxPriorityFeePerGas),
MaxFeePerGasHigh: weiToGwei(maxPriorityFeePerGas), Medium: new(big.Int).Add(new(big.Int).Mul(baseFee, big.NewInt(2)), maxPriorityFeePerGas),
EIP1559Enabled: false, High: new(big.Int).Add(new(big.Int).Mul(baseFee, big.NewInt(3)), maxPriorityFeePerGas),
}, nil },
}
perc10 := fees[int64(0.1*float64(len(fees)))-1]
perc20 := fees[int64(0.2*float64(len(fees)))-1]
var maxFeePerGasMedium *big.Int
if baseFee.Cmp(perc20) >= 0 {
maxFeePerGasMedium = baseFee
} else {
maxFeePerGasMedium = perc20
}
if maxPriorityFeePerGas.Cmp(maxFeePerGasMedium) > 0 {
maxFeePerGasMedium = maxPriorityFeePerGas
}
maxFeePerGasHigh := new(big.Int).Mul(maxPriorityFeePerGas, big.NewInt(2))
twoTimesBaseFee := new(big.Int).Mul(baseFee, big.NewInt(2))
if twoTimesBaseFee.Cmp(maxFeePerGasHigh) > 0 {
maxFeePerGasHigh = twoTimesBaseFee
}
return &SuggestedFees{
GasPrice: weiToGwei(gasPrice),
BaseFee: weiToGwei(baseFee),
MaxPriorityFeePerGas: weiToGwei(maxPriorityFeePerGas),
MaxFeePerGasLow: weiToGwei(perc10),
MaxFeePerGasMedium: weiToGwei(maxFeePerGasMedium),
MaxFeePerGasHigh: weiToGwei(maxFeePerGasHigh),
EIP1559Enabled: true, EIP1559Enabled: true,
}, nil }, nil
} }
func (f *FeeManager) SuggestedFeesGwei(ctx context.Context, chainID uint64) (*SuggestedFeesGwei, error) {
fees, err := f.SuggestedFees(ctx, chainID)
if err != nil {
return nil, err
}
return &SuggestedFeesGwei{
GasPrice: weiToGwei(fees.GasPrice),
BaseFee: weiToGwei(fees.BaseFee),
MaxPriorityFeePerGas: weiToGwei(fees.MaxPriorityFeePerGas),
MaxFeePerGasLow: weiToGwei(fees.MaxFeesLevels.Low),
MaxFeePerGasMedium: weiToGwei(fees.MaxFeesLevels.Medium),
MaxFeePerGasHigh: weiToGwei(fees.MaxFeesLevels.High),
EIP1559Enabled: fees.EIP1559Enabled,
}, nil
}
func (f *FeeManager) getBaseFee(ctx context.Context, client chain.ClientInterface) (*big.Int, error) { func (f *FeeManager) getBaseFee(ctx context.Context, client chain.ClientInterface) (*big.Int, error) {
header, err := client.HeaderByNumber(ctx, nil) header, err := client.HeaderByNumber(ctx, nil)
if err != nil { if err != nil {
@ -200,54 +206,17 @@ func (f *FeeManager) getBaseFee(ctx context.Context, client chain.ClientInterfac
return baseFee, nil return baseFee, nil
} }
func (f *FeeManager) getPriorityFees(ctx context.Context, client chain.ClientInterface, baseFee *big.Int) (PriorityFees, error) { func (f *FeeManager) TransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Int) TransactionEstimation {
var priorityFee PriorityFees
fees, err := f.getFeeHistorySorted(client.NetworkID())
if err != nil {
return priorityFee, err
}
suggestedPriorityFee, err := client.SuggestGasTipCap(ctx)
if err != nil {
return priorityFee, err
}
// Calculate Low priority fee
priorityFee.Low = fees[int64(0.1*float64(len(fees)))-1]
// Calculate Medium priority fee
priorityFee.Medium = fees[int64(0.2*float64(len(fees)))-1]
if baseFee.Cmp(priorityFee.Medium) > 0 {
priorityFee.Medium = baseFee
}
if suggestedPriorityFee.Cmp(priorityFee.Medium) > 0 {
priorityFee.Medium = suggestedPriorityFee
}
// Calculate High priority fee
priorityFee.High = new(big.Int).Mul(suggestedPriorityFee, big.NewInt(2))
twoTimesBaseFee := new(big.Int).Mul(baseFee, big.NewInt(2))
if twoTimesBaseFee.Cmp(priorityFee.High) > 0 {
priorityFee.High = twoTimesBaseFee
}
return priorityFee, nil
}
func (f *FeeManager) TransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) TransactionEstimation {
fees, err := f.getFeeHistorySorted(chainID) fees, err := f.getFeeHistorySorted(chainID)
if err != nil { if err != nil {
return Unknown return Unknown
} }
maxFeePerGasWei := gweiToWei(maxFeePerGas)
// pEvent represents the probability of the transaction being included in a block, // pEvent represents the probability of the transaction being included in a block,
// we assume this one is static over time, in reality it is not. // we assume this one is static over time, in reality it is not.
pEvent := 0.0 pEvent := 0.0
for idx, fee := range fees { for idx, fee := range fees {
if fee.Cmp(maxFeePerGasWei) == 1 || idx == len(fees)-1 { if fee.Cmp(maxFeePerGas) == 1 || idx == len(fees)-1 {
pEvent = float64(idx) / float64(len(fees)) pEvent = float64(idx) / float64(len(fees))
break break
} }

View File

@ -51,7 +51,7 @@ type Path struct {
AmountInLocked bool AmountInLocked bool
AmountOut *hexutil.Big AmountOut *hexutil.Big
GasAmount uint64 GasAmount uint64
GasFees *SuggestedFees GasFees *SuggestedFeesGwei
BonderFees *hexutil.Big BonderFees *hexutil.Big
TokenFees *big.Float TokenFees *big.Float
Cost *big.Float Cost *big.Float
@ -522,7 +522,7 @@ func (r *Router) SuggestedRoutes(
} }
group.Add(func(c context.Context) error { group.Add(func(c context.Context) error {
gasFees, err := r.feesManager.SuggestedFees(ctx, network.ChainID) gasFees, err := r.feesManager.SuggestedFeesGwei(ctx, network.ChainID)
if err != nil { if err != nil {
return err return err
} }
@ -555,7 +555,7 @@ func (r *Router) SuggestedRoutes(
} }
maxFees := gasFees.feeFor(gasFeeMode) maxFees := gasFees.feeFor(gasFeeMode)
estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFees) estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, gweiToWei(maxFees))
for _, pProcessor := range r.pathProcessors { for _, pProcessor := range r.pathProcessors {
// Skip processors that are added because of the Router V2, to not break the current functionality // Skip processors that are added because of the Router V2, to not break the current functionality
if pProcessor.Name() == pathprocessor.ProcessorENSRegisterName || if pProcessor.Name() == pathprocessor.ProcessorENSRegisterName ||

View File

@ -62,10 +62,10 @@ type PathV2 struct {
AmountInLocked bool // Is the amount locked AmountInLocked bool // Is the amount locked
AmountOut *hexutil.Big // Amount that will be received on the destination chain AmountOut *hexutil.Big // Amount that will be received on the destination chain
SuggestedPriorityFees *PriorityFees // Suggested priority fees for the transaction SuggestedLevelsForMaxFeesPerGas *MaxFeesLevels // Suggested max fees for the transaction
TxBaseFee *hexutil.Big // Base fee for the transaction TxBaseFee *hexutil.Big // Base fee for the transaction
TxPriorityFee *hexutil.Big // Priority fee for the transaction, by default we're using the Medium priority fee TxPriorityFee *hexutil.Big // Priority fee for the transaction
TxGasAmount uint64 // Gas used for the transaction TxGasAmount uint64 // Gas used for the transaction
TxBonderFees *hexutil.Big // Bonder fees for the transaction - used for Hop bridge TxBonderFees *hexutil.Big // Bonder fees for the transaction - used for Hop bridge
TxTokenFees *hexutil.Big // Token fees for the transaction - used for bridges (represent the difference between the amount in and the amount out) TxTokenFees *hexutil.Big // Token fees for the transaction - used for bridges (represent the difference between the amount in and the amount out)
@ -432,7 +432,6 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
} }
group.Add(func(c context.Context) error { group.Add(func(c context.Context) error {
client, err := r.rpcClient.EthClient(network.ChainID)
if err != nil { if err != nil {
return err return err
} }
@ -529,29 +528,18 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
l1FeeWei, _ = r.feesManager.GetL1Fee(ctx, network.ChainID, txInputData) l1FeeWei, _ = r.feesManager.GetL1Fee(ctx, network.ChainID, txInputData)
} }
baseFee, err := r.feesManager.getBaseFee(ctx, client) fees, err := r.feesManager.SuggestedFees(ctx, network.ChainID)
if err != nil { if err != nil {
continue continue
} }
priorityFees, err := r.feesManager.getPriorityFees(ctx, client, baseFee)
if err != nil {
continue
}
selctedPriorityFee := priorityFees.Medium
if input.GasFeeMode == GasFeeHigh {
selctedPriorityFee = priorityFees.High
} else if input.GasFeeMode == GasFeeLow {
selctedPriorityFee = priorityFees.Low
}
amountOut, err := pProcessor.CalculateAmountOut(processorInputParams) amountOut, err := pProcessor.CalculateAmountOut(processorInputParams)
if err != nil { if err != nil {
continue continue
} }
maxFeesPerGas := new(big.Float) maxFeesPerGas := fees.feeFor(input.GasFeeMode)
maxFeesPerGas.Add(new(big.Float).SetInt(baseFee), new(big.Float).SetInt(selctedPriorityFee))
estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFeesPerGas) estimatedTime := r.feesManager.TransactionEstimatedTime(ctx, network.ChainID, maxFeesPerGas)
if approvalRequired && estimatedTime < MoreThanFiveMinutes { if approvalRequired && estimatedTime < MoreThanFiveMinutes {
estimatedTime += 1 estimatedTime += 1
@ -567,10 +555,10 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
AmountInLocked: amountLocked, AmountInLocked: amountLocked,
AmountOut: (*hexutil.Big)(amountOut), AmountOut: (*hexutil.Big)(amountOut),
SuggestedPriorityFees: &priorityFees, SuggestedLevelsForMaxFeesPerGas: fees.MaxFeesLevels,
TxBaseFee: (*hexutil.Big)(baseFee), TxBaseFee: (*hexutil.Big)(fees.BaseFee),
TxPriorityFee: (*hexutil.Big)(selctedPriorityFee), TxPriorityFee: (*hexutil.Big)(fees.MaxPriorityFeePerGas),
TxGasAmount: gasLimit, TxGasAmount: gasLimit,
TxBonderFees: (*hexutil.Big)(bonderFees), TxBonderFees: (*hexutil.Big)(bonderFees),
TxTokenFees: (*hexutil.Big)(tokenFees), TxTokenFees: (*hexutil.Big)(tokenFees),
@ -579,14 +567,13 @@ func (r *Router) SuggestedRoutesV2(ctx context.Context, input *RouteInputParams)
ApprovalRequired: approvalRequired, ApprovalRequired: approvalRequired,
ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired), ApprovalAmountRequired: (*hexutil.Big)(approvalAmountRequired),
ApprovalContractAddress: &approvalContractAddress, ApprovalContractAddress: &approvalContractAddress,
ApprovalBaseFee: (*hexutil.Big)(baseFee), ApprovalBaseFee: (*hexutil.Big)(fees.BaseFee),
ApprovalPriorityFee: (*hexutil.Big)(selctedPriorityFee), ApprovalPriorityFee: (*hexutil.Big)(fees.MaxPriorityFeePerGas),
ApprovalGasAmount: approvalGasLimit, ApprovalGasAmount: approvalGasLimit,
ApprovalL1Fee: (*hexutil.Big)(big.NewInt(int64(l1ApprovalFee))), ApprovalL1Fee: (*hexutil.Big)(big.NewInt(int64(l1ApprovalFee))),
EstimatedTime: estimatedTime, EstimatedTime: estimatedTime,
}, })
)
mu.Unlock() mu.Unlock()
} }
} }