diff --git a/graft/subnet-evm/precompile/contracts/gaspricemanager/BUILD.bazel b/graft/subnet-evm/precompile/contracts/gaspricemanager/BUILD.bazel index 6b476640927b..22c4539ef96b 100644 --- a/graft/subnet-evm/precompile/contracts/gaspricemanager/BUILD.bazel +++ b/graft/subnet-evm/precompile/contracts/gaspricemanager/BUILD.bazel @@ -30,19 +30,28 @@ graft_go_test( srcs = [ "config_test.go", "contract_test.go", + "simulated_test.go", ], embed = [":gaspricemanager"], deps = [ + "//graft/subnet-evm/accounts/abi/bind", "//graft/subnet-evm/commontype", + "//graft/subnet-evm/core", "//graft/subnet-evm/core/extstate", + "//graft/subnet-evm/ethclient/simulated", + "//graft/subnet-evm/params", + "//graft/subnet-evm/plugin/evm/customtypes", "//graft/subnet-evm/precompile/allowlist", "//graft/subnet-evm/precompile/allowlist/allowlisttest", "//graft/subnet-evm/precompile/contract", + "//graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings", + "//graft/subnet-evm/precompile/contracts/utilstest", "//graft/subnet-evm/precompile/precompileconfig", "//graft/subnet-evm/precompile/precompiletest", "//utils", "@com_github_ava_labs_libevm//common", "@com_github_ava_labs_libevm//core/vm", + "@com_github_ava_labs_libevm//crypto", "@com_github_stretchr_testify//require", "@org_uber_go_mock//gomock", ], diff --git a/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/BUILD.bazel b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/BUILD.bazel index a0bab17215e5..cc3b3d1dca86 100644 --- a/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/BUILD.bazel +++ b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/BUILD.bazel @@ -9,6 +9,7 @@ go_library( name = "bindings", srcs = [ "compile.go", + "gen_gaspricemanagertest_binding.go", "gen_igaspricemanager_binding.go", ], importpath = "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings", diff --git a/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/GasPriceManagerTest.sol b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/GasPriceManagerTest.sol new file mode 100644 index 000000000000..10f6ba7463c5 --- /dev/null +++ b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/GasPriceManagerTest.sol @@ -0,0 +1,24 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "precompile/contracts/gaspricemanager/IGasPriceManager.sol"; + +contract GasPriceManagerTest { + IGasPriceManager private gasPriceManager; + + constructor(address gasPriceManagerPrecompile) { + gasPriceManager = IGasPriceManager(gasPriceManagerPrecompile); + } + + function setGasPriceConfig(IGasPriceManager.GasPriceConfig calldata config) external { + gasPriceManager.setGasPriceConfig(config); + } + + function getGasPriceConfig() external view returns (IGasPriceManager.GasPriceConfig memory) { + return gasPriceManager.getGasPriceConfig(); + } + + function getGasPriceConfigLastChangedAt() external view returns (uint256) { + return gasPriceManager.getGasPriceConfigLastChangedAt(); + } +} diff --git a/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/compile.go b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/compile.go index 376841490097..e89295119838 100644 --- a/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/compile.go +++ b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/compile.go @@ -5,8 +5,13 @@ package bindings // Step 1: Compile interface to generate ABI at top level //go:generate solc -o ../.. --overwrite --abi --base-path ../../../../.. --pretty-json --evm-version cancun ../../IGasPriceManager.sol -// Step 2: Generate Go bindings from the compiled artifacts +// Step 2: Compile test contract to generate ABI and bin files +//go:generate solc -o artifacts --overwrite --abi --bin --base-path ../../../../.. --metadata-hash none --evm-version cancun GasPriceManagerTest.sol +// Step 3: Generate Go bindings from the compiled artifacts //go:generate go run github.com/ava-labs/libevm/cmd/abigen --pkg bindings --type IGasPriceManager --abi ../../IGasPriceManager.abi --out gen_igaspricemanager_binding.go -// Step 3: Replace import paths in generated binding to use subnet-evm instead of libevm +//go:generate go run github.com/ava-labs/libevm/cmd/abigen --pkg bindings --type GasPriceManagerTest --abi artifacts/GasPriceManagerTest.abi --bin artifacts/GasPriceManagerTest.bin --out gen_gaspricemanagertest_binding.go +// Step 4: Remove duplicate IGasPriceManagerGasPriceConfig struct from test binding (already defined in interface binding) +//go:generate sh -c "sed -i.bak '/IGasPriceManagerGasPriceConfig is an auto/,/^}/d' gen_gaspricemanagertest_binding.go && rm -f gen_gaspricemanagertest_binding.go.bak" +// Step 5: Replace import paths in generated binding to use subnet-evm instead of libevm // This is necessary because the libevm bindings package is not compatible with the subnet-evm simulated backend, which is used for testing. -//go:generate sh -c "sed -i.bak -e 's|github.com/ava-labs/libevm/accounts/abi/bind|github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi/bind|g' gen_igaspricemanager_binding.go && rm -f gen_igaspricemanager_binding.go.bak" +//go:generate sh -c "sed -i.bak -e 's|github.com/ava-labs/libevm/accounts/abi/bind|github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi/bind|g' gen_igaspricemanager_binding.go gen_gaspricemanagertest_binding.go && rm -f gen_igaspricemanager_binding.go.bak gen_gaspricemanagertest_binding.go.bak" diff --git a/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/gen_gaspricemanagertest_binding.go b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/gen_gaspricemanagertest_binding.go new file mode 100644 index 000000000000..d22e33cda419 --- /dev/null +++ b/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings/gen_gaspricemanagertest_binding.go @@ -0,0 +1,287 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ava-labs/libevm" + "github.com/ava-labs/libevm/accounts/abi" + "github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi/bind" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + + +// GasPriceManagerTestMetaData contains all meta data concerning the GasPriceManagerTest contract. +var GasPriceManagerTestMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"gasPriceManagerPrecompile\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"getGasPriceConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"validatorTargetGas\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"targetGas\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"staticPricing\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minGasPrice\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timeToDouble\",\"type\":\"uint64\"}],\"internalType\":\"structIGasPriceManager.GasPriceConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGasPriceConfigLastChangedAt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"validatorTargetGas\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"targetGas\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"staticPricing\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minGasPrice\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timeToDouble\",\"type\":\"uint64\"}],\"internalType\":\"structIGasPriceManager.GasPriceConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"setGasPriceConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561000f575f5ffd5b506040516107e03803806107e0833981810160405281019061003191906100d4565b805f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506100ff565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100a38261007a565b9050919050565b6100b381610099565b81146100bd575f5ffd5b50565b5f815190506100ce816100aa565b92915050565b5f602082840312156100e9576100e8610076565b5b5f6100f6848285016100c0565b91505092915050565b6106d48061010c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c8063445821e31461004357806365c97a2814610061578063eb8df3961461007d575b5f5ffd5b61004b61009b565b604051610058919061033f565b60405180910390f35b61007b60048036038101906100769190610387565b610135565b005b6100856101be565b60405161009291906103ca565b60405180910390f35b6100a3610251565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663445821e36040518163ffffffff1660e01b815260040160a060405180830381865afa15801561010c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610130919061054c565b905090565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166365c97a28826040518263ffffffff1660e01b815260040161018e9190610659565b5f604051808303815f87803b1580156101a5575f5ffd5b505af11580156101b7573d5f5f3e3d5ffd5b5050505050565b5f5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eb8df3966040518163ffffffff1660e01b8152600401602060405180830381865afa158015610228573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061024c919061069c565b905090565b6040518060a001604052805f151581526020015f67ffffffffffffffff1681526020015f151581526020015f67ffffffffffffffff1681526020015f67ffffffffffffffff1681525090565b5f8115159050919050565b6102b18161029d565b82525050565b5f67ffffffffffffffff82169050919050565b6102d3816102b7565b82525050565b60a082015f8201516102ed5f8501826102a8565b50602082015161030060208501826102ca565b50604082015161031360408501826102a8565b50606082015161032660608501826102ca565b50608082015161033960808501826102ca565b50505050565b5f60a0820190506103525f8301846102d9565b92915050565b5f604051905090565b5f5ffd5b5f5ffd5b5f60a0828403121561037e5761037d610365565b5b81905092915050565b5f60a0828403121561039c5761039b610361565b5b5f6103a984828501610369565b91505092915050565b5f819050919050565b6103c4816103b2565b82525050565b5f6020820190506103dd5f8301846103bb565b92915050565b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61042d826103e7565b810181811067ffffffffffffffff8211171561044c5761044b6103f7565b5b80604052505050565b5f61045e610358565b905061046a8282610424565b919050565b6104788161029d565b8114610482575f5ffd5b50565b5f815190506104938161046f565b92915050565b6104a2816102b7565b81146104ac575f5ffd5b50565b5f815190506104bd81610499565b92915050565b5f60a082840312156104d8576104d76103e3565b5b6104e260a0610455565b90505f6104f184828501610485565b5f830152506020610504848285016104af565b602083015250604061051884828501610485565b604083015250606061052c848285016104af565b6060830152506080610540848285016104af565b60808301525092915050565b5f60a0828403121561056157610560610361565b5b5f61056e848285016104c3565b91505092915050565b5f813590506105858161046f565b92915050565b5f6105996020840184610577565b905092915050565b5f813590506105af81610499565b92915050565b5f6105c360208401846105a1565b905092915050565b60a082016105db5f83018361058b565b6105e75f8501826102a8565b506105f560208301836105b5565b61060260208501826102ca565b50610610604083018361058b565b61061d60408501826102a8565b5061062b60608301836105b5565b61063860608501826102ca565b5061064660808301836105b5565b61065360808501826102ca565b50505050565b5f60a08201905061066c5f8301846105cb565b92915050565b61067b816103b2565b8114610685575f5ffd5b50565b5f8151905061069681610672565b92915050565b5f602082840312156106b1576106b0610361565b5b5f6106be84828501610688565b9150509291505056fea164736f6c634300081c000a", +} + +// GasPriceManagerTestABI is the input ABI used to generate the binding from. +// Deprecated: Use GasPriceManagerTestMetaData.ABI instead. +var GasPriceManagerTestABI = GasPriceManagerTestMetaData.ABI + +// GasPriceManagerTestBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use GasPriceManagerTestMetaData.Bin instead. +var GasPriceManagerTestBin = GasPriceManagerTestMetaData.Bin + +// DeployGasPriceManagerTest deploys a new Ethereum contract, binding an instance of GasPriceManagerTest to it. +func DeployGasPriceManagerTest(auth *bind.TransactOpts, backend bind.ContractBackend, gasPriceManagerPrecompile common.Address) (common.Address, *types.Transaction, *GasPriceManagerTest, error) { + parsed, err := GasPriceManagerTestMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(GasPriceManagerTestBin), backend, gasPriceManagerPrecompile) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &GasPriceManagerTest{GasPriceManagerTestCaller: GasPriceManagerTestCaller{contract: contract}, GasPriceManagerTestTransactor: GasPriceManagerTestTransactor{contract: contract}, GasPriceManagerTestFilterer: GasPriceManagerTestFilterer{contract: contract}}, nil +} + +// GasPriceManagerTest is an auto generated Go binding around an Ethereum contract. +type GasPriceManagerTest struct { + GasPriceManagerTestCaller // Read-only binding to the contract + GasPriceManagerTestTransactor // Write-only binding to the contract + GasPriceManagerTestFilterer // Log filterer for contract events +} + +// GasPriceManagerTestCaller is an auto generated read-only Go binding around an Ethereum contract. +type GasPriceManagerTestCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GasPriceManagerTestTransactor is an auto generated write-only Go binding around an Ethereum contract. +type GasPriceManagerTestTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GasPriceManagerTestFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type GasPriceManagerTestFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GasPriceManagerTestSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type GasPriceManagerTestSession struct { + Contract *GasPriceManagerTest // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GasPriceManagerTestCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type GasPriceManagerTestCallerSession struct { + Contract *GasPriceManagerTestCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// GasPriceManagerTestTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type GasPriceManagerTestTransactorSession struct { + Contract *GasPriceManagerTestTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GasPriceManagerTestRaw is an auto generated low-level Go binding around an Ethereum contract. +type GasPriceManagerTestRaw struct { + Contract *GasPriceManagerTest // Generic contract binding to access the raw methods on +} + +// GasPriceManagerTestCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type GasPriceManagerTestCallerRaw struct { + Contract *GasPriceManagerTestCaller // Generic read-only contract binding to access the raw methods on +} + +// GasPriceManagerTestTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type GasPriceManagerTestTransactorRaw struct { + Contract *GasPriceManagerTestTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewGasPriceManagerTest creates a new instance of GasPriceManagerTest, bound to a specific deployed contract. +func NewGasPriceManagerTest(address common.Address, backend bind.ContractBackend) (*GasPriceManagerTest, error) { + contract, err := bindGasPriceManagerTest(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &GasPriceManagerTest{GasPriceManagerTestCaller: GasPriceManagerTestCaller{contract: contract}, GasPriceManagerTestTransactor: GasPriceManagerTestTransactor{contract: contract}, GasPriceManagerTestFilterer: GasPriceManagerTestFilterer{contract: contract}}, nil +} + +// NewGasPriceManagerTestCaller creates a new read-only instance of GasPriceManagerTest, bound to a specific deployed contract. +func NewGasPriceManagerTestCaller(address common.Address, caller bind.ContractCaller) (*GasPriceManagerTestCaller, error) { + contract, err := bindGasPriceManagerTest(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &GasPriceManagerTestCaller{contract: contract}, nil +} + +// NewGasPriceManagerTestTransactor creates a new write-only instance of GasPriceManagerTest, bound to a specific deployed contract. +func NewGasPriceManagerTestTransactor(address common.Address, transactor bind.ContractTransactor) (*GasPriceManagerTestTransactor, error) { + contract, err := bindGasPriceManagerTest(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &GasPriceManagerTestTransactor{contract: contract}, nil +} + +// NewGasPriceManagerTestFilterer creates a new log filterer instance of GasPriceManagerTest, bound to a specific deployed contract. +func NewGasPriceManagerTestFilterer(address common.Address, filterer bind.ContractFilterer) (*GasPriceManagerTestFilterer, error) { + contract, err := bindGasPriceManagerTest(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &GasPriceManagerTestFilterer{contract: contract}, nil +} + +// bindGasPriceManagerTest binds a generic wrapper to an already deployed contract. +func bindGasPriceManagerTest(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := GasPriceManagerTestMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_GasPriceManagerTest *GasPriceManagerTestRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _GasPriceManagerTest.Contract.GasPriceManagerTestCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_GasPriceManagerTest *GasPriceManagerTestRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _GasPriceManagerTest.Contract.GasPriceManagerTestTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_GasPriceManagerTest *GasPriceManagerTestRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _GasPriceManagerTest.Contract.GasPriceManagerTestTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_GasPriceManagerTest *GasPriceManagerTestCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _GasPriceManagerTest.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_GasPriceManagerTest *GasPriceManagerTestTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _GasPriceManagerTest.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_GasPriceManagerTest *GasPriceManagerTestTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _GasPriceManagerTest.Contract.contract.Transact(opts, method, params...) +} + +// GetGasPriceConfig is a free data retrieval call binding the contract method 0x445821e3. +// +// Solidity: function getGasPriceConfig() view returns((bool,uint64,bool,uint64,uint64)) +func (_GasPriceManagerTest *GasPriceManagerTestCaller) GetGasPriceConfig(opts *bind.CallOpts) (IGasPriceManagerGasPriceConfig, error) { + var out []interface{} + err := _GasPriceManagerTest.contract.Call(opts, &out, "getGasPriceConfig") + + if err != nil { + return *new(IGasPriceManagerGasPriceConfig), err + } + + out0 := *abi.ConvertType(out[0], new(IGasPriceManagerGasPriceConfig)).(*IGasPriceManagerGasPriceConfig) + + return out0, err + +} + +// GetGasPriceConfig is a free data retrieval call binding the contract method 0x445821e3. +// +// Solidity: function getGasPriceConfig() view returns((bool,uint64,bool,uint64,uint64)) +func (_GasPriceManagerTest *GasPriceManagerTestSession) GetGasPriceConfig() (IGasPriceManagerGasPriceConfig, error) { + return _GasPriceManagerTest.Contract.GetGasPriceConfig(&_GasPriceManagerTest.CallOpts) +} + +// GetGasPriceConfig is a free data retrieval call binding the contract method 0x445821e3. +// +// Solidity: function getGasPriceConfig() view returns((bool,uint64,bool,uint64,uint64)) +func (_GasPriceManagerTest *GasPriceManagerTestCallerSession) GetGasPriceConfig() (IGasPriceManagerGasPriceConfig, error) { + return _GasPriceManagerTest.Contract.GetGasPriceConfig(&_GasPriceManagerTest.CallOpts) +} + +// GetGasPriceConfigLastChangedAt is a free data retrieval call binding the contract method 0xeb8df396. +// +// Solidity: function getGasPriceConfigLastChangedAt() view returns(uint256) +func (_GasPriceManagerTest *GasPriceManagerTestCaller) GetGasPriceConfigLastChangedAt(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _GasPriceManagerTest.contract.Call(opts, &out, "getGasPriceConfigLastChangedAt") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetGasPriceConfigLastChangedAt is a free data retrieval call binding the contract method 0xeb8df396. +// +// Solidity: function getGasPriceConfigLastChangedAt() view returns(uint256) +func (_GasPriceManagerTest *GasPriceManagerTestSession) GetGasPriceConfigLastChangedAt() (*big.Int, error) { + return _GasPriceManagerTest.Contract.GetGasPriceConfigLastChangedAt(&_GasPriceManagerTest.CallOpts) +} + +// GetGasPriceConfigLastChangedAt is a free data retrieval call binding the contract method 0xeb8df396. +// +// Solidity: function getGasPriceConfigLastChangedAt() view returns(uint256) +func (_GasPriceManagerTest *GasPriceManagerTestCallerSession) GetGasPriceConfigLastChangedAt() (*big.Int, error) { + return _GasPriceManagerTest.Contract.GetGasPriceConfigLastChangedAt(&_GasPriceManagerTest.CallOpts) +} + +// SetGasPriceConfig is a paid mutator transaction binding the contract method 0x65c97a28. +// +// Solidity: function setGasPriceConfig((bool,uint64,bool,uint64,uint64) config) returns() +func (_GasPriceManagerTest *GasPriceManagerTestTransactor) SetGasPriceConfig(opts *bind.TransactOpts, config IGasPriceManagerGasPriceConfig) (*types.Transaction, error) { + return _GasPriceManagerTest.contract.Transact(opts, "setGasPriceConfig", config) +} + +// SetGasPriceConfig is a paid mutator transaction binding the contract method 0x65c97a28. +// +// Solidity: function setGasPriceConfig((bool,uint64,bool,uint64,uint64) config) returns() +func (_GasPriceManagerTest *GasPriceManagerTestSession) SetGasPriceConfig(config IGasPriceManagerGasPriceConfig) (*types.Transaction, error) { + return _GasPriceManagerTest.Contract.SetGasPriceConfig(&_GasPriceManagerTest.TransactOpts, config) +} + +// SetGasPriceConfig is a paid mutator transaction binding the contract method 0x65c97a28. +// +// Solidity: function setGasPriceConfig((bool,uint64,bool,uint64,uint64) config) returns() +func (_GasPriceManagerTest *GasPriceManagerTestTransactorSession) SetGasPriceConfig(config IGasPriceManagerGasPriceConfig) (*types.Transaction, error) { + return _GasPriceManagerTest.Contract.SetGasPriceConfig(&_GasPriceManagerTest.TransactOpts, config) +} diff --git a/graft/subnet-evm/precompile/contracts/gaspricemanager/simulated_test.go b/graft/subnet-evm/precompile/contracts/gaspricemanager/simulated_test.go new file mode 100644 index 000000000000..8f49c8d2797e --- /dev/null +++ b/graft/subnet-evm/precompile/contracts/gaspricemanager/simulated_test.go @@ -0,0 +1,308 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// simulated_test.go validates the gas price manager precompile by routing +// calls through a deployed Solidity wrapper contract (GasPriceManagerTest.sol), +// which acts as an integration test with the EVM interpreter: +// +// Go abi.Pack() -> wrapper (solc-compiled) -> ABI re-encoding -> precompile -> Go abi.Unpack() +// +// catching any disagreement between Go's ABI package and solc's ABI +// implementation. Note that some tests overlap with contract_test.go (e.g. gas price config +// validation) but the overlap is intentional: contract_test.go tests precompile +// logic directly, while these tests verify the ABI round-trip through the EVM +// interpreter. +// +// Also, errors from the precompile lose their identity when serialized as EVM revert +// data, so error assertions use ErrorContains instead of ErrorIs. + +package gaspricemanager_test + +import ( + "os" + "testing" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/vm" + "github.com/ava-labs/libevm/crypto" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi/bind" + "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" + "github.com/ava-labs/avalanchego/graft/subnet-evm/core" + "github.com/ava-labs/avalanchego/graft/subnet-evm/params" + "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" + "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/allowlist" + "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/allowlist/allowlisttest" + "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/gaspricemanager" + "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/gaspricemanager/gaspricemanagertest/bindings" + "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/utilstest" + "github.com/ava-labs/avalanchego/utils" + + sim "github.com/ava-labs/avalanchego/graft/subnet-evm/ethclient/simulated" +) + +var ( + adminKey, _ = crypto.GenerateKey() + adminAddress = crypto.PubkeyToAddress(adminKey.PublicKey) +) + +var ( + genesisGasPriceConfig = commontype.GasPriceConfig{ + TargetGas: 2_000_000, + MinGasPrice: 25, + TimeToDouble: 120, + } + + updatedGasPriceConfig = commontype.GasPriceConfig{ + TargetGas: 5_000_000, + MinGasPrice: 42, + TimeToDouble: 300, + } + + boolGasPriceConfig = commontype.GasPriceConfig{ + ValidatorTargetGas: true, + StaticPricing: true, + MinGasPrice: 7, + } + + testPrecompileConfig = gaspricemanager.NewConfig( + utils.PointerTo[uint64](0), + []common.Address{adminAddress}, + nil, nil, + &genesisGasPriceConfig, + ) +) + +func TestMain(m *testing.M) { + // Ensure libevm extras are registered for tests. + core.RegisterExtras() + customtypes.Register() + params.RegisterExtras() + os.Exit(m.Run()) +} + +func toBindingsGasPriceConfig(c commontype.GasPriceConfig) bindings.IGasPriceManagerGasPriceConfig { + return bindings.IGasPriceManagerGasPriceConfig{ + ValidatorTargetGas: c.ValidatorTargetGas, + TargetGas: c.TargetGas, + StaticPricing: c.StaticPricing, + MinGasPrice: c.MinGasPrice, + TimeToDouble: c.TimeToDouble, + } +} + +func toCommonGasPriceConfig(c bindings.IGasPriceManagerGasPriceConfig) commontype.GasPriceConfig { + return commontype.GasPriceConfig{ + ValidatorTargetGas: c.ValidatorTargetGas, + TargetGas: c.TargetGas, + StaticPricing: c.StaticPricing, + MinGasPrice: c.MinGasPrice, + TimeToDouble: c.TimeToDouble, + } +} + +func deployTestContract(t *testing.T, b *sim.Backend, auth *bind.TransactOpts) (common.Address, *bindings.GasPriceManagerTest) { + t.Helper() + addr, tx, contract, err := bindings.DeployGasPriceManagerTest(auth, b.Client(), gaspricemanager.ContractAddress) + require.NoError(t, err, "DeployGasPriceManagerTest()") + utilstest.WaitReceiptSuccessful(t, b, tx) + return addr, contract +} + +func setGasPriceConfig(t *testing.T, b *sim.Backend, contract *bindings.GasPriceManagerTest, auth *bind.TransactOpts, config commontype.GasPriceConfig) uint64 { + t.Helper() + tx, err := contract.SetGasPriceConfig(auth, toBindingsGasPriceConfig(config)) + require.NoError(t, err, "SetGasPriceConfig()") + receipt := utilstest.WaitReceiptSuccessful(t, b, tx) + return receipt.BlockNumber.Uint64() +} + +func TestGasPriceManager(t *testing.T) { + chainID := params.TestChainConfig.ChainID + admin := utilstest.NewAuth(t, adminKey, chainID) + + type testCase struct { + name string + test func(t *testing.T, backend *sim.Backend, gasPriceManager *bindings.IGasPriceManager) + } + + testCases := []testCase{ + { + name: "should verify admin has admin role", + test: func(t *testing.T, _ *sim.Backend, gasPriceManager *bindings.IGasPriceManager) { + allowlisttest.VerifyRole(t, gasPriceManager, adminAddress, allowlist.AdminRole) + }, + }, + { + name: "should verify new contract has no role", + test: func(t *testing.T, backend *sim.Backend, gasPriceManager *bindings.IGasPriceManager) { + testContractAddr, _ := deployTestContract(t, backend, admin) + allowlisttest.VerifyRole(t, gasPriceManager, testContractAddr, allowlist.NoRole) + }, + }, + { + name: "contract should not be able to change gas price config without enabled", + test: func(t *testing.T, backend *sim.Backend, _ *bindings.IGasPriceManager) { + _, testContract := deployTestContract(t, backend, admin) + + _, err := testContract.SetGasPriceConfig(admin, toBindingsGasPriceConfig(updatedGasPriceConfig)) + require.ErrorContains(t, err, vm.ErrExecutionReverted.Error(), "SetGasPriceConfig() without enabled role") //nolint:forbidigo // upstream error wrapped as string + }, + }, + { + name: "contract should be added to enabled list", + test: func(t *testing.T, backend *sim.Backend, gasPriceManager *bindings.IGasPriceManager) { + testContractAddr, _ := deployTestContract(t, backend, admin) + allowlisttest.SetAsEnabled(t, backend, gasPriceManager, admin, testContractAddr) + allowlisttest.VerifyRole(t, gasPriceManager, testContractAddr, allowlist.EnabledRole) + }, + }, + { + name: "enabled contract should set and read gas price config", + test: func(t *testing.T, backend *sim.Backend, gasPriceManager *bindings.IGasPriceManager) { + testContractAddr, testContract := deployTestContract(t, backend, admin) + allowlisttest.SetAsEnabled(t, backend, gasPriceManager, admin, testContractAddr) + + got, err := testContract.GetGasPriceConfig(nil) + require.NoError(t, err, "GetGasPriceConfig() before set") + require.Equal(t, genesisGasPriceConfig, toCommonGasPriceConfig(got), "genesis gas price config") + + blockNum := setGasPriceConfig(t, backend, testContract, admin, updatedGasPriceConfig) + + got, err = testContract.GetGasPriceConfig(nil) + require.NoError(t, err, "GetGasPriceConfig() after set") + require.Equal(t, updatedGasPriceConfig, toCommonGasPriceConfig(got), "updated gas price config") + + lastChangedAt, err := testContract.GetGasPriceConfigLastChangedAt(nil) + require.NoError(t, err, "GetGasPriceConfigLastChangedAt()") + require.Equal(t, blockNum, lastChangedAt.Uint64(), "last changed at block number") + }, + }, + { + name: "bool fields round-trip through contract", + test: func(t *testing.T, backend *sim.Backend, gasPriceManager *bindings.IGasPriceManager) { + testContractAddr, testContract := deployTestContract(t, backend, admin) + allowlisttest.SetAsEnabled(t, backend, gasPriceManager, admin, testContractAddr) + + setGasPriceConfig(t, backend, testContract, admin, boolGasPriceConfig) + + got, err := testContract.GetGasPriceConfig(nil) + require.NoError(t, err, "GetGasPriceConfig()") + require.Equal(t, boolGasPriceConfig, toCommonGasPriceConfig(got), "boolean gas price config round-trip") + }, + }, + { + name: "contract role can be revoked", + test: func(t *testing.T, backend *sim.Backend, gasPriceManager *bindings.IGasPriceManager) { + testContractAddr, testContract := deployTestContract(t, backend, admin) + allowlisttest.SetAsEnabled(t, backend, gasPriceManager, admin, testContractAddr) + + setGasPriceConfig(t, backend, testContract, admin, updatedGasPriceConfig) + + got, err := testContract.GetGasPriceConfig(nil) + require.NoError(t, err, "GetGasPriceConfig() after set") + require.Equal(t, updatedGasPriceConfig, toCommonGasPriceConfig(got), "config set by enabled contract") + + allowlisttest.SetAsNone(t, backend, gasPriceManager, admin, testContractAddr) + allowlisttest.VerifyRole(t, gasPriceManager, testContractAddr, allowlist.NoRole) + + _, err = testContract.SetGasPriceConfig(admin, toBindingsGasPriceConfig(boolGasPriceConfig)) + require.ErrorContains(t, err, vm.ErrExecutionReverted.Error(), "SetGasPriceConfig() after role revoked") //nolint:forbidigo // upstream error wrapped as string + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + backend := utilstest.NewBackendWithPrecompile(t, testPrecompileConfig, []common.Address{adminAddress}) + defer backend.Close() + + gasPriceManager, err := bindings.NewIGasPriceManager(gaspricemanager.ContractAddress, backend.Client()) + require.NoError(t, err, "NewIGasPriceManager()") + + tc.test(t, backend, gasPriceManager) + }) + } +} + +func TestGasPriceManager_InvalidGasPriceConfig(t *testing.T) { + // Validation errors from the precompile revert through the wrapper + // contract, so only the generic "execution reverted" is visible. The + // specific error messages are tested in contract_test.go. + tests := []struct { + name string + config bindings.IGasPriceManagerGasPriceConfig + }{ + { + name: "zero MinGasPrice", + config: bindings.IGasPriceManagerGasPriceConfig{ + TargetGas: 1_000_000, + TimeToDouble: 60, + }, + }, + { + name: "ValidatorTargetGas with non-zero TargetGas", + config: bindings.IGasPriceManagerGasPriceConfig{ + ValidatorTargetGas: true, + TargetGas: 1_000_000, + MinGasPrice: 1, + }, + }, + { + name: "StaticPricing with non-zero TimeToDouble", + config: bindings.IGasPriceManagerGasPriceConfig{ + StaticPricing: true, + TargetGas: 1_000_000, + MinGasPrice: 1, + TimeToDouble: 60, + }, + }, + } + + admin := utilstest.NewAuth(t, adminKey, params.TestChainConfig.ChainID) + + backend := utilstest.NewBackendWithPrecompile(t, testPrecompileConfig, []common.Address{adminAddress}) + defer backend.Close() + + gasPriceManager, err := bindings.NewIGasPriceManager(gaspricemanager.ContractAddress, backend.Client()) + require.NoError(t, err, "NewIGasPriceManager()") + + testContractAddr, testContract := deployTestContract(t, backend, admin) + allowlisttest.SetAsEnabled(t, backend, gasPriceManager, admin, testContractAddr) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := testContract.SetGasPriceConfig(admin, tt.config) + require.ErrorContains(t, err, vm.ErrExecutionReverted.Error(), "SetGasPriceConfig(%+v)", tt.config) //nolint:forbidigo // upstream error wrapped as string + }) + } +} + +func TestGasPriceManager_Events(t *testing.T) { + admin := utilstest.NewAuth(t, adminKey, params.TestChainConfig.ChainID) + + backend := utilstest.NewBackendWithPrecompile(t, testPrecompileConfig, []common.Address{adminAddress}) + defer backend.Close() + + gasPriceManager, err := bindings.NewIGasPriceManager(gaspricemanager.ContractAddress, backend.Client()) + require.NoError(t, err, "NewIGasPriceManager()") + + testContractAddr, testContract := deployTestContract(t, backend, admin) + allowlisttest.SetAsEnabled(t, backend, gasPriceManager, admin, testContractAddr) + + setGasPriceConfig(t, backend, testContract, admin, updatedGasPriceConfig) + + // The event sender is the wrapper contract address, not adminAddress. + iter, err := gasPriceManager.FilterGasPriceConfigUpdated(nil, []common.Address{testContractAddr}) + require.NoError(t, err, "FilterGasPriceConfigUpdated()") + defer iter.Close() + + require.True(t, iter.Next(), "expected GasPriceConfigUpdated event") + event := iter.Event + require.Equal(t, testContractAddr, event.Sender, "event sender") + require.Equal(t, genesisGasPriceConfig, toCommonGasPriceConfig(event.OldGasPriceConfig), "old gas price config in event") + require.Equal(t, updatedGasPriceConfig, toCommonGasPriceConfig(event.NewGasPriceConfig), "new gas price config in event") + require.False(t, iter.Next(), "expected no more events") + require.NoError(t, iter.Error(), "iterator error") +}