keeper

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2024 License: LGPL-3.0 Imports: 39 Imported by: 0

Documentation

Overview

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Copyright 2022 Match Foundation This file is part of the Match Network packages.

Match is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The Match packages are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with the Match packages. If not, see https://github.com/matchain/match/blob/main/LICENSE

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckSenderBalance

func CheckSenderBalance(
	balance sdkmath.Int,
	txData types.TxData,
) error

CheckSenderBalance validates that the tx cost value is positive and that the sender has enough funds to pay for the fees and value of the transaction.

func GasToRefund

func GasToRefund(availableRefund, gasConsumed, refundQuotient uint64) uint64

GasToRefund calculates the amount of gas the state machine should refund to the sender. It is capped by the refund quotient value. Note: do not pass 0 to refundQuotient

func GetProposerAddress

func GetProposerAddress(ctx sdk.Context, proposerAddress sdk.ConsAddress) sdk.ConsAddress

GetProposerAddress returns current block proposer's address when provided proposer address is empty.

func VerifyFee

func VerifyFee(
	txData types.TxData,
	denom string,
	baseFee *big.Int,
	homestead, istanbul, isCheckTx bool,
) (sdk.Coins, error)

VerifyFee is used to return the fee for the given transaction data in sdk.Coins. It checks that the gas limit is not reached, the gas limit is higher than the intrinsic gas and that the base fee is higher than the gas fee cap.

Types

type Keeper

type Keeper struct {
	// contains filtered or unexported fields
}

Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface.

func NewKeeper

func NewKeeper(
	cdc codec.BinaryCodec,
	storeKey, transientKey storetypes.StoreKey,
	authority sdk.AccAddress,
	ak types.AccountKeeper,
	bankKeeper types.BankKeeper,
	sk types.StakingKeeper,
	fmk types.FeeMarketKeeper,
	rk types.RewardKeeper,
	tracer string,
	ss paramstypes.Subspace,
) *Keeper

NewKeeper generates new evm module keeper

func (Keeper) Account

Account implements the Query/Account gRPC method

func (Keeper) AddTransientGasUsed

func (k Keeper) AddTransientGasUsed(ctx sdk.Context, gasUsed uint64) (uint64, error)

AddTransientGasUsed accumulate gas used by each eth msgs included in current cosmos tx.

func (*Keeper) ApplyMessage

func (k *Keeper) ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*types.MsgEthereumTxResponse, error)

ApplyMessage calls ApplyMessageWithConfig with an empty TxConfig.

func (*Keeper) ApplyMessageWithConfig

func (k *Keeper) ApplyMessageWithConfig(ctx sdk.Context,
	msg core.Message,
	tracer vm.EVMLogger,
	commit bool,
	cfg *statedb.EVMConfig,
	txConfig statedb.TxConfig,
) (*types.MsgEthereumTxResponse, error)

ApplyMessageWithConfig computes the new state by applying the given message against the existing state. If the message fails, the VM execution error with the reason will be returned to the client and the transaction won't be committed to the store.

Reverted state

The snapshot and rollback are supported by the `statedb.StateDB`.

Different Callers

It's called in three scenarios: 1. `ApplyTransaction`, in the transaction processing flow. 2. `EthCall/EthEstimateGas` grpc query handler. 3. Called by other native modules directly.

Prechecks and Preprocessing

All relevant state transition prechecks for the MsgEthereumTx are performed on the AnteHandler, prior to running the transaction against the state. The prechecks run are the following:

1. the nonce of the message caller is correct 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) 3. the amount of gas required is available in the block 4. the purchased gas is enough to cover intrinsic usage 5. there is no overflow when calculating intrinsic gas 6. caller has enough balance to cover asset transfer for **topmost** call

The preprocessing steps performed by the AnteHandler are:

1. set up the initial access list (iff fork > Berlin)

Tracer parameter

It should be a `vm.Tracer` object or nil, if pass `nil`, it'll create a default one based on keeper options.

Commit parameter

If commit is true, the `StateDB` will be committed, otherwise discarded.

func (*Keeper) ApplyTransaction

func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*types.MsgEthereumTxResponse, error)

ApplyTransaction runs and attempts to perform a state transition with the given transaction (i.e Message), that will only be persisted (committed) to the underlying KVStore if the transaction does not fail.

Gas tracking

Ethereum consumes gas according to the EVM opcodes instead of general reads and writes to store. Because of this, the state transition needs to ignore the SDK gas consumption mechanism defined by the GasKVStore and instead consume the amount of gas used by the VM execution. The amount of gas used is tracked by the EVM and returned in the execution result.

Prior to the execution, the starting tx gas meter is saved and replaced with an infinite gas meter in a new context in order to ignore the SDK gas consumption config values (read, write, has, delete). After the execution, the gas used from the message execution will be added to the starting gas consumed, taking into consideration the amount of gas returned. Finally, the context is updated with the EVM gas consumed value prior to returning.

For relevant discussion see: https://github.com/cosmos/cosmos-sdk/discussions/9072

func (Keeper) Balance

Balance implements the Query/Balance gRPC method

func (Keeper) BaseFee

BaseFee implements the Query/BaseFee gRPC method

func (*Keeper) BeginBlock

func (k *Keeper) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock)

BeginBlock sets the sdk Context and EIP155 chain id to the Keeper.

func (Keeper) ChainID

func (k Keeper) ChainID() *big.Int

ChainID returns the EIP155 chain ID for the EVM context

func (*Keeper) CleanHooks

func (k *Keeper) CleanHooks() *Keeper

CleanHooks resets the hooks for the EVM module NOTE: Should only be used for testing purposes

func (Keeper) Code

Code implements the Query/Code gRPC method

func (*Keeper) DeductTxCostsFromUserBalance

func (k *Keeper) DeductTxCostsFromUserBalance(
	ctx sdk.Context,
	fees sdk.Coins,
	from common.Address,
) error

DeductTxCostsFromUserBalance deducts the fees from the user balance. Returns an error if the specified sender address does not exist or the account balance is not sufficient.

func (*Keeper) DeleteAccount

func (k *Keeper) DeleteAccount(ctx sdk.Context, addr common.Address) error

DeleteAccount handles contract's suicide call: - clear balance - remove code - remove states - remove auth account

func (*Keeper) EVMConfig

func (k *Keeper) EVMConfig(ctx sdk.Context, proposerAddress sdk.ConsAddress, chainID *big.Int) (*statedb.EVMConfig, error)

EVMConfig creates the EVMConfig based on current state

func (Keeper) EmitBlockBloomEvent

func (k Keeper) EmitBlockBloomEvent(ctx sdk.Context, bloom ethtypes.Bloom)

EmitBlockBloomEvent emit block bloom events

func (*Keeper) EndBlock

func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate

EndBlock also retrieves the bloom filter value from the transient store and commits it to the KVStore. The EVM end block logic doesn't update the validator set, thus it returns an empty slice.

func (Keeper) EstimateGas

EstimateGas implements eth_estimateGas rpc api.

func (Keeper) EthCall

EthCall implements eth_call rpc api.

func (*Keeper) EthereumTx

func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error)

EthereumTx implements the gRPC MsgServer interface. It receives a transaction which is then executed (i.e applied) against the go-ethereum EVM. The provided SDK Context is set to the Keeper so that it can implements and call the StateDB methods without receiving it as a function parameter.

func (*Keeper) ForEachStorage

func (k *Keeper) ForEachStorage(ctx sdk.Context, addr common.Address, cb func(key, value common.Hash) bool)

ForEachStorage iterate contract storage, callback return false to break early

func (*Keeper) GetAccount

func (k *Keeper) GetAccount(ctx sdk.Context, addr common.Address) *statedb.Account

GetAccount returns nil if account is not exist, returns error if it's not `EthAccountI`

func (*Keeper) GetAccountOrEmpty

func (k *Keeper) GetAccountOrEmpty(ctx sdk.Context, addr common.Address) statedb.Account

GetAccountOrEmpty returns empty account if not exist, returns error if it's not `EthAccount`

func (Keeper) GetAccountStorage

func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) types.Storage

GetAccountStorage return state storage associated with an account

func (*Keeper) GetAccountWithoutBalance

func (k *Keeper) GetAccountWithoutBalance(ctx sdk.Context, addr common.Address) *statedb.Account

GetAccountWithoutBalance load nonce and codehash without balance, more efficient in cases where balance is not needed.

func (Keeper) GetAuthority

func (k Keeper) GetAuthority() sdk.AccAddress

GetAuthority returns the x/evm module authority address

func (*Keeper) GetBalance

func (k *Keeper) GetBalance(ctx sdk.Context, addr common.Address) *big.Int

GetBalance load account's balance of gas token

func (Keeper) GetBaseFee

func (k Keeper) GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int

GetBaseFee returns current base fee, return values: - `nil`: london hardfork not enabled. - `0`: london hardfork enabled but feemarket is not enabled. - `n`: both london hardfork and feemarket are enabled.

func (Keeper) GetBlockBloomTransient

func (k Keeper) GetBlockBloomTransient(ctx sdk.Context) *big.Int

GetBlockBloomTransient returns bloom bytes for the current block height

func (*Keeper) GetCode

func (k *Keeper) GetCode(ctx sdk.Context, codeHash common.Hash) []byte

GetCode loads contract code from database, implements `statedb.Keeper` interface.

func (Keeper) GetCoinbaseAddress

func (k Keeper) GetCoinbaseAddress(ctx sdk.Context, proposerAddress sdk.ConsAddress) (common.Address, error)

GetCoinbaseAddress returns the block proposer's validator operator address.

func (*Keeper) GetEthIntrinsicGas

func (k *Keeper) GetEthIntrinsicGas(ctx sdk.Context, msg core.Message, cfg *params.ChainConfig, isContractCreation bool) (uint64, error)

GetEthIntrinsicGas returns the intrinsic gas cost for the transaction

func (Keeper) GetHashFn

func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc

GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases:

  1. The requested height matches the current height from context (and thus same epoch number)
  2. The requested height is from an previous height from the same chain epoch
  3. The requested height is from a height greater than the latest one

func (Keeper) GetLegacyParams

func (k Keeper) GetLegacyParams(ctx sdk.Context) types.Params

GetLegacyParams returns param set for version before migrate

func (Keeper) GetLogSizeTransient

func (k Keeper) GetLogSizeTransient(ctx sdk.Context) uint64

GetLogSizeTransient returns EVM log index on the current block.

func (Keeper) GetMinGasMultiplier

func (k Keeper) GetMinGasMultiplier(ctx sdk.Context) sdk.Dec

GetMinGasMultiplier returns the MinGasMultiplier param from the fee market module

func (*Keeper) GetNonce

func (k *Keeper) GetNonce(ctx sdk.Context, addr common.Address) uint64

GetNonce returns the sequence number of an account, returns 0 if not exists.

func (Keeper) GetParams

func (k Keeper) GetParams(ctx sdk.Context) (params types.Params)

GetParams returns the total set of evm parameters.

func (*Keeper) GetState

func (k *Keeper) GetState(ctx sdk.Context, addr common.Address, key common.Hash) common.Hash

GetState loads contract state from database, implements `statedb.Keeper` interface.

func (Keeper) GetTransientGasUsed

func (k Keeper) GetTransientGasUsed(ctx sdk.Context) uint64

GetTransientGasUsed returns the gas used by current cosmos tx.

func (Keeper) GetTxIndexTransient

func (k Keeper) GetTxIndexTransient(ctx sdk.Context) uint64

GetTxIndexTransient returns EVM transaction index on the current block.

func (Keeper) Logger

func (k Keeper) Logger(ctx sdk.Context) log.Logger

Logger returns a module-specific logger.

func (*Keeper) NewEVM

func (k *Keeper) NewEVM(
	ctx sdk.Context,
	msg core.Message,
	cfg *statedb.EVMConfig,
	tracer vm.EVMLogger,
	stateDB vm.StateDB,
) *vm.EVM

func (Keeper) Params

Params implements the Query/Params gRPC method

func (*Keeper) PostTxProcessing

func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error

PostTxProcessing delegate the call to the hooks. If no hook has been registered, this function returns with a `nil` error

func (*Keeper) RefundGas

func (k *Keeper) RefundGas(ctx sdk.Context, msg core.Message, leftoverGas uint64, denom string) error

RefundGas transfers the leftover gas to the sender of the message, caped to half of the total gas consumed in the transaction. Additionally, the function sets the total gas consumed to the value returned by the EVM execution, thus ignoring the previous intrinsic gas consumed during in the AnteHandler.

func (*Keeper) ResetGasMeterAndConsumeGas

func (k *Keeper) ResetGasMeterAndConsumeGas(ctx sdk.Context, gasUsed uint64)

ResetGasMeterAndConsumeGas reset first the gas meter consumed value to zero and set it back to the new value 'gasUsed'

func (Keeper) ResetTransientGasUsed

func (k Keeper) ResetTransientGasUsed(ctx sdk.Context)

ResetTransientGasUsed reset gas used to prepare for execution of current cosmos tx, called in ante handler.

func (*Keeper) SetAccount

func (k *Keeper) SetAccount(ctx sdk.Context, addr common.Address, account statedb.Account) error

SetAccount updates nonce/balance/codeHash together.

func (*Keeper) SetBalance

func (k *Keeper) SetBalance(ctx sdk.Context, addr common.Address, amount *big.Int) error

SetBalance update account's balance, compare with current balance first, then decide to mint or burn.

func (Keeper) SetBlockBloomTransient

func (k Keeper) SetBlockBloomTransient(ctx sdk.Context, bloom *big.Int)

SetBlockBloomTransient sets the given bloom bytes to the transient store. This value is reset on every block.

func (*Keeper) SetCode

func (k *Keeper) SetCode(ctx sdk.Context, codeHash, code []byte)

SetCode set contract code, delete if code is empty.

func (*Keeper) SetHooks

func (k *Keeper) SetHooks(eh types.EvmHooks) *Keeper

SetHooks sets the hooks for the EVM module It should be called only once during initialization, it panic if called more than once.

func (Keeper) SetLogSizeTransient

func (k Keeper) SetLogSizeTransient(ctx sdk.Context, logSize uint64)

SetLogSizeTransient fetches the current EVM log index from the transient store, increases its value by one and then sets the new index back to the transient store.

func (Keeper) SetParams

func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error

SetParams sets the EVM params each in their individual key for better get performance

func (*Keeper) SetState

func (k *Keeper) SetState(ctx sdk.Context, addr common.Address, key common.Hash, value []byte)

SetState update contract storage, delete if value is empty.

func (Keeper) SetTransientGasUsed

func (k Keeper) SetTransientGasUsed(ctx sdk.Context, gasUsed uint64)

SetTransientGasUsed sets the gas used by current cosmos tx.

func (Keeper) SetTxIndexTransient

func (k Keeper) SetTxIndexTransient(ctx sdk.Context, index uint64)

SetTxIndexTransient set the index of processing transaction

func (Keeper) Storage

Storage implements the Query/Storage gRPC method

func (Keeper) TraceBlock

TraceBlock configures a new tracer according to the provided configuration, and executes the given message in the provided environment for all the transactions in the queried block. The return value will be tracer dependent.

func (Keeper) TraceTx

TraceTx configures a new tracer according to the provided configuration, and executes the given message in the provided environment. The return value will be tracer dependent.

func (Keeper) Tracer

func (k Keeper) Tracer(ctx sdk.Context, msg core.Message, ethCfg *params.ChainConfig) vm.EVMLogger

Tracer return a default vm.Tracer based on current keeper state

func (*Keeper) TxConfig

func (k *Keeper) TxConfig(ctx sdk.Context, txHash common.Hash) statedb.TxConfig

TxConfig loads `TxConfig` from current transient storage

func (*Keeper) UpdateParams

func (k *Keeper) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error)

UpdateParams implements the gRPC MsgServer interface. When an UpdateParams proposal passes, it updates the module parameters. The update can only be performed if the requested authority is the Cosmos SDK governance module account.

func (Keeper) VMConfig

func (k Keeper) VMConfig(ctx sdk.Context, _ core.Message, cfg *statedb.EVMConfig, tracer vm.EVMLogger) vm.Config

VMConfig creates an EVM configuration from the debug setting and the extra EIPs enabled on the module parameters. The config generated uses the default JumpTable from the EVM.

func (Keeper) ValidatorAccount

ValidatorAccount implements the Query/Balance gRPC method

func (*Keeper) WithChainID

func (k *Keeper) WithChainID(ctx sdk.Context)

WithChainID sets the chain id to the local variable in the keeper

type MultiEvmHooks

type MultiEvmHooks []types.EvmHooks

MultiEvmHooks combine multiple evm hooks, all hook functions are run in array sequence

func NewMultiEvmHooks

func NewMultiEvmHooks(hooks ...types.EvmHooks) MultiEvmHooks

NewMultiEvmHooks combine multiple evm hooks

func (MultiEvmHooks) PostTxProcessing

func (mh MultiEvmHooks) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error

PostTxProcessing delegate the call to underlying hooks

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL