Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add zkevm_estimateGasPrice #3248

Merged
merged 4 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs/json-rpc-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,17 @@ If the endpoint is not in the list below, it means this specific endpoint is not
- `zkevm_batchNumber`
- `zkevm_batchNumberByBlockNumber`
- `zkevm_consolidatedBlockNumber`
- `zkevm_estimateFee`
- `zkevm_estimateGasPrice`
- `zkevm_getBatchByNumber`
- `zkevm_getExitRootsByGER`
- `zkevm_getFullBlockByHash`
- `zkevm_getFullBlockByNumber`
- `zkevm_getLatestGlobalExitRoot`
- `zkevm_getNativeBlockHashesInRange`
- `zkevm_getTransactionByL2Hash`
- `zkevm_getTransactionReceiptByL2Hash`
- `zkevm_isBlockConsolidated`
- `zkevm_isBlockVirtualized`
- `zkevm_verifiedBatchNumber`
- `zkevm_virtualBatchNumber`
- `zkevm_getTransactionByL2Hash`
- `zkevm_getTransactionReceiptByL2Hash`
- `zkevm_getExitRootsByGER`
142 changes: 81 additions & 61 deletions jsonrpc/endpoints_zkevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,87 +427,107 @@ func (z *ZKEVMEndpoints) GetExitRootsByGER(globalExitRoot common.Hash) (interfac
})
}

// EstimateGasPrice returns an estimate gas price for the transaction.
func (z *ZKEVMEndpoints) EstimateGasPrice(arg *types.TxArgs, blockArg *types.BlockNumberOrHash) (interface{}, types.Error) {
return z.txMan.NewDbTxScope(z.state, func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
gasPrice, _, err := z.internalEstimateGasPriceAndFee(ctx, arg, blockArg, dbTx)
if err != nil {
return nil, err
}
return hex.EncodeBig(gasPrice), nil
})
}

// EstimateFee returns an estimate fee for the transaction.
func (z *ZKEVMEndpoints) EstimateFee(arg *types.TxArgs, blockArg *types.BlockNumberOrHash) (interface{}, types.Error) {
return z.txMan.NewDbTxScope(z.state, func(ctx context.Context, dbTx pgx.Tx) (interface{}, types.Error) {
if arg == nil {
return RPCErrorResponse(types.InvalidParamsErrorCode, "missing value for required argument 0", nil, false)
_, fee, err := z.internalEstimateGasPriceAndFee(ctx, arg, blockArg, dbTx)
if err != nil {
return nil, err
}
return hex.EncodeBig(fee), nil
})
}

block, respErr := z.getBlockByArg(ctx, blockArg, dbTx)
if respErr != nil {
return nil, respErr
}
// internalEstimateGasPriceAndFee computes the estimated gas price and the estimated fee for the transaction
func (z *ZKEVMEndpoints) internalEstimateGasPriceAndFee(ctx context.Context, arg *types.TxArgs, blockArg *types.BlockNumberOrHash, dbTx pgx.Tx) (*big.Int, *big.Int, types.Error) {
if arg == nil {
return nil, nil, types.NewRPCError(types.InvalidParamsErrorCode, "missing value for required argument 0")
}

var blockToProcess *uint64
if blockArg != nil {
blockNumArg := blockArg.Number()
if blockNumArg != nil && (*blockArg.Number() == types.LatestBlockNumber || *blockArg.Number() == types.PendingBlockNumber) {
blockToProcess = nil
} else {
n := block.NumberU64()
blockToProcess = &n
}
}
block, respErr := z.getBlockByArg(ctx, blockArg, dbTx)
if respErr != nil {
return nil, nil, respErr
}

defaultSenderAddress := common.HexToAddress(state.DefaultSenderAddress)
sender, tx, err := arg.ToTransaction(ctx, z.state, z.cfg.MaxCumulativeGasUsed, block.Root(), defaultSenderAddress, dbTx)
if err != nil {
return RPCErrorResponse(types.DefaultErrorCode, "failed to convert arguments into an unsigned transaction", err, false)
var blockToProcess *uint64
if blockArg != nil {
blockNumArg := blockArg.Number()
if blockNumArg != nil && (*blockArg.Number() == types.LatestBlockNumber || *blockArg.Number() == types.PendingBlockNumber) {
blockToProcess = nil
} else {
n := block.NumberU64()
blockToProcess = &n
}
}

gasEstimation, returnValue, err := z.state.EstimateGas(tx, sender, blockToProcess, dbTx)
if errors.Is(err, runtime.ErrExecutionReverted) {
data := make([]byte, len(returnValue))
copy(data, returnValue)
return nil, types.NewRPCErrorWithData(types.RevertedErrorCode, err.Error(), data)
} else if err != nil {
errMsg := fmt.Sprintf("failed to estimate gas: %v", err.Error())
return nil, types.NewRPCError(types.DefaultErrorCode, errMsg)
}
defaultSenderAddress := common.HexToAddress(state.DefaultSenderAddress)
sender, tx, err := arg.ToTransaction(ctx, z.state, z.cfg.MaxCumulativeGasUsed, block.Root(), defaultSenderAddress, dbTx)
if err != nil {
return nil, nil, types.NewRPCError(types.DefaultErrorCode, "failed to convert arguments into an unsigned transaction")
}

gasEstimation, returnValue, err := z.state.EstimateGas(tx, sender, blockToProcess, dbTx)
if errors.Is(err, runtime.ErrExecutionReverted) {
data := make([]byte, len(returnValue))
copy(data, returnValue)
return nil, nil, types.NewRPCErrorWithData(types.RevertedErrorCode, err.Error(), data)
} else if err != nil {
errMsg := fmt.Sprintf("failed to estimate gas: %v", err.Error())
return nil, nil, types.NewRPCError(types.DefaultErrorCode, errMsg)
}

gasPrices, err := z.pool.GetGasPrices(ctx)
if err != nil {
return nil, nil, types.NewRPCError(types.DefaultErrorCode, "failed to get L2 gas price", err, false)
}

txGasPrice := new(big.Int).SetUint64(gasPrices.L2GasPrice) // by default we assume the tx gas price is the current L2 gas price
txEGPPct := state.MaxEffectivePercentage
egpEnabled := z.pool.EffectiveGasPriceEnabled()

gasPrices, err := z.pool.GetGasPrices(ctx)
if egpEnabled {
rawTx, err := state.EncodeTransactionWithoutEffectivePercentage(*tx)
if err != nil {
return RPCErrorResponse(types.DefaultErrorCode, "failed to get L2 gas price", err, false)
return nil, nil, types.NewRPCError(types.DefaultErrorCode, "failed to encode tx", err, false)
}

txGasPrice := new(big.Int).SetUint64(gasPrices.L2GasPrice) // by default we assume the tx gas price is the current L2 gas price
txEGPPct := state.MaxEffectivePercentage
egpEnabled := z.pool.EffectiveGasPriceEnabled()

if egpEnabled {
rawTx, err := state.EncodeTransactionWithoutEffectivePercentage(*tx)
if err != nil {
return RPCErrorResponse(types.DefaultErrorCode, "failed to encode tx", err, false)
}
txEGP, err := z.pool.CalculateEffectiveGasPrice(rawTx, txGasPrice, gasEstimation, gasPrices.L1GasPrice, gasPrices.L2GasPrice)
if err != nil {
return nil, nil, types.NewRPCError(types.DefaultErrorCode, "failed to calculate effective gas price", err, false)
}

txEGP, err := z.pool.CalculateEffectiveGasPrice(rawTx, txGasPrice, gasEstimation, gasPrices.L1GasPrice, gasPrices.L2GasPrice)
if txEGP.Cmp(txGasPrice) == -1 { // txEGP < txGasPrice
// We need to "round" the final effectiveGasPrice to a 256 fraction of the txGasPrice
txEGPPct, err = z.pool.CalculateEffectiveGasPricePercentage(txGasPrice, txEGP)
if err != nil {
return RPCErrorResponse(types.DefaultErrorCode, "failed to calculate effective gas price", err, false)
}

if txEGP.Cmp(txGasPrice) == -1 { // txEGP < txGasPrice
// We need to "round" the final effectiveGasPrice to a 256 fraction of the txGasPrice
txEGPPct, err = z.pool.CalculateEffectiveGasPricePercentage(txGasPrice, txEGP)
if err != nil {
return RPCErrorResponse(types.DefaultErrorCode, "failed to calculate effective gas price percentage", err, false)
}
// txGasPriceFraction = txGasPrice/256
txGasPriceFraction := new(big.Int).Div(txGasPrice, new(big.Int).SetUint64(256)) //nolint:gomnd
// txGasPrice = txGasPriceFraction*(txEGPPct+1)
txGasPrice = new(big.Int).Mul(txGasPriceFraction, new(big.Int).SetUint64(uint64(txEGPPct+1)))
return nil, nil, types.NewRPCError(types.DefaultErrorCode, "failed to calculate effective gas price percentage", err, false)
}

log.Infof("[EstimateFee] finalGasPrice: %d, effectiveGasPrice: %d, egpPct: %d, l2GasPrice: %d, len: %d, gas: %d, l1GasPrice: %d",
txGasPrice, txEGP, txEGPPct, gasPrices.L2GasPrice, len(rawTx), gasEstimation, gasPrices.L1GasPrice)
// txGasPriceFraction = txGasPrice/256
txGasPriceFraction := new(big.Int).Div(txGasPrice, new(big.Int).SetUint64(256)) //nolint:gomnd
// txGasPrice = txGasPriceFraction*(txEGPPct+1)
txGasPrice = new(big.Int).Mul(txGasPriceFraction, new(big.Int).SetUint64(uint64(txEGPPct+1)))
}

fee := new(big.Int).Mul(txGasPrice, new(big.Int).SetUint64(gasEstimation))
log.Infof("[internalEstimateGasPriceAndFee] finalGasPrice: %d, effectiveGasPrice: %d, egpPct: %d, l2GasPrice: %d, len: %d, gas: %d, l1GasPrice: %d",
txGasPrice, txEGP, txEGPPct, gasPrices.L2GasPrice, len(rawTx), gasEstimation, gasPrices.L1GasPrice)
}

log.Infof("[EstimateFee] egpEnabled: %t, fee: %d, gasPrice: %d, gas: %d", egpEnabled, fee, txGasPrice, gasEstimation)
fee := new(big.Int).Mul(txGasPrice, new(big.Int).SetUint64(gasEstimation))

return hex.EncodeBig(fee), nil
})
log.Infof("[internalEstimateGasPriceAndFee] egpEnabled: %t, fee: %d, gasPrice: %d, gas: %d", egpEnabled, fee, txGasPrice, gasEstimation)

return txGasPrice, fee, nil
}

func (z *ZKEVMEndpoints) getBlockByArg(ctx context.Context, blockArg *types.BlockNumberOrHash, dbTx pgx.Tx) (*state.L2Block, types.Error) {
Expand Down
39 changes: 39 additions & 0 deletions jsonrpc/endpoints_zkevm.openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,38 @@
"$ref": "#/components/schemas/Keccak"
}
}
},
{
"name": "zkevm_estimateFee",
"summary": "Estimates the transaction Fee following the effective gas price rules",
"params": [
{
"$ref": "#/components/contentDescriptors/Transaction"
}
],
"result": {
"name": "fee",
"description": "The amount of the fee",
"schema": {
"$ref": "#/components/schemas/Integer"
}
}
},
{
"name": "zkevm_estimateGasPrice",
"summary": "Estimates the transaction Gas Price following the effective gas price rules",
"params": [
{
"$ref": "#/components/contentDescriptors/Transaction"
}
],
"result": {
"name": "gasPrice",
"description": "The amount of gas price",
"schema": {
"$ref": "#/components/schemas/Integer"
}
}
}
],
"components": {
Expand Down Expand Up @@ -472,6 +504,13 @@
"$ref": "#/components/schemas/Block"
}
},
"Transaction": {
"required": true,
"name": "transaction",
"schema": {
"$ref": "#/components/schemas/Transaction"
}
},
"TransactionHash": {
"name": "transactionHash",
"required": true,
Expand Down
Loading