diff --git a/precompiles/interfaces/contracts/IWrappedA0GIBase.sol b/precompiles/interfaces/contracts/IWrappedA0GIBase.sol index ba975348..ad7e8132 100644 --- a/precompiles/interfaces/contracts/IWrappedA0GIBase.sol +++ b/precompiles/interfaces/contracts/IWrappedA0GIBase.sol @@ -20,7 +20,7 @@ interface IWrappedA0GIBase { /** * @dev get the wA0GI address. */ - function getWA0GI() external returns (address); + function getWA0GI() external view returns (address); /** * @dev set the cap for a minter. diff --git a/precompiles/wrapped-a0gi-base/IWrappedA0GIBase.json b/precompiles/wrapped-a0gi-base/IWrappedA0GIBase.json index 6f4d2f48..67a7800b 100644 --- a/precompiles/wrapped-a0gi-base/IWrappedA0GIBase.json +++ b/precompiles/wrapped-a0gi-base/IWrappedA0GIBase.json @@ -27,7 +27,7 @@ "type": "address" } ], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { diff --git a/precompiles/wrapped-a0gi-base/contract.go b/precompiles/wrapped-a0gi-base/contract.go index 99174a4f..3cadb588 100644 --- a/precompiles/wrapped-a0gi-base/contract.go +++ b/precompiles/wrapped-a0gi-base/contract.go @@ -30,7 +30,7 @@ var ( // Wrappeda0gibaseMetaData contains all meta data concerning the Wrappeda0gibase contract. var Wrappeda0gibaseMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWA0GI\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"minterSupply\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"cap\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"total\",\"type\":\"uint256\"}],\"internalType\":\"structSupply\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWA0GI\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"minterSupply\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"cap\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"total\",\"type\":\"uint256\"}],\"internalType\":\"structSupply\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // Wrappeda0gibaseABI is the input ABI used to generate the binding from. @@ -179,6 +179,37 @@ func (_Wrappeda0gibase *Wrappeda0gibaseTransactorRaw) Transact(opts *bind.Transa return _Wrappeda0gibase.Contract.contract.Transact(opts, method, params...) } +// GetWA0GI is a free data retrieval call binding the contract method 0xa9283a7a. +// +// Solidity: function getWA0GI() view returns(address) +func (_Wrappeda0gibase *Wrappeda0gibaseCaller) GetWA0GI(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Wrappeda0gibase.contract.Call(opts, &out, "getWA0GI") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetWA0GI is a free data retrieval call binding the contract method 0xa9283a7a. +// +// Solidity: function getWA0GI() view returns(address) +func (_Wrappeda0gibase *Wrappeda0gibaseSession) GetWA0GI() (common.Address, error) { + return _Wrappeda0gibase.Contract.GetWA0GI(&_Wrappeda0gibase.CallOpts) +} + +// GetWA0GI is a free data retrieval call binding the contract method 0xa9283a7a. +// +// Solidity: function getWA0GI() view returns(address) +func (_Wrappeda0gibase *Wrappeda0gibaseCallerSession) GetWA0GI() (common.Address, error) { + return _Wrappeda0gibase.Contract.GetWA0GI(&_Wrappeda0gibase.CallOpts) +} + // MinterSupply is a free data retrieval call binding the contract method 0x95609212. // // Solidity: function minterSupply(address minter) view returns((uint256,uint256)) @@ -231,27 +262,6 @@ func (_Wrappeda0gibase *Wrappeda0gibaseTransactorSession) Burn(minter common.Add return _Wrappeda0gibase.Contract.Burn(&_Wrappeda0gibase.TransactOpts, minter, amount) } -// GetWA0GI is a paid mutator transaction binding the contract method 0xa9283a7a. -// -// Solidity: function getWA0GI() returns(address) -func (_Wrappeda0gibase *Wrappeda0gibaseTransactor) GetWA0GI(opts *bind.TransactOpts) (*types.Transaction, error) { - return _Wrappeda0gibase.contract.Transact(opts, "getWA0GI") -} - -// GetWA0GI is a paid mutator transaction binding the contract method 0xa9283a7a. -// -// Solidity: function getWA0GI() returns(address) -func (_Wrappeda0gibase *Wrappeda0gibaseSession) GetWA0GI() (*types.Transaction, error) { - return _Wrappeda0gibase.Contract.GetWA0GI(&_Wrappeda0gibase.TransactOpts) -} - -// GetWA0GI is a paid mutator transaction binding the contract method 0xa9283a7a. -// -// Solidity: function getWA0GI() returns(address) -func (_Wrappeda0gibase *Wrappeda0gibaseTransactorSession) GetWA0GI() (*types.Transaction, error) { - return _Wrappeda0gibase.Contract.GetWA0GI(&_Wrappeda0gibase.TransactOpts) -} - // Mint is a paid mutator transaction binding the contract method 0x40c10f19. // // Solidity: function mint(address minter, uint256 amount) returns() diff --git a/precompiles/wrapped-a0gi-base/errors.go b/precompiles/wrapped-a0gi-base/errors.go new file mode 100644 index 00000000..4d793692 --- /dev/null +++ b/precompiles/wrapped-a0gi-base/errors.go @@ -0,0 +1,5 @@ +package wrappeda0gibase + +const ( + ErrSenderNotWA0GI = "sender is not WA0GI" +) diff --git a/precompiles/wrapped-a0gi-base/query.go b/precompiles/wrapped-a0gi-base/query.go new file mode 100644 index 00000000..9052dbf7 --- /dev/null +++ b/precompiles/wrapped-a0gi-base/query.go @@ -0,0 +1,38 @@ +package wrappeda0gibase + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +func (w *WrappedA0giBasePrecompile) GetW0GI(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) { + req, err := NewGetW0GIRequest(args) + if err != nil { + return nil, err + } + response, err := w.wrappeda0gibaseKeeper.GetWA0GI(ctx, req) + if err != nil { + return nil, err + } + return method.Outputs.Pack(common.BytesToAddress(response.Address)) +} + +func (w *WrappedA0giBasePrecompile) MinterSupply(ctx sdk.Context, _ *vm.EVM, method *abi.Method, args []interface{}) ([]byte, error) { + req, err := NewMinterSupplyRequest(args) + if err != nil { + return nil, err + } + response, err := w.wrappeda0gibaseKeeper.MinterSupply(ctx, req) + if err != nil { + return nil, err + } + supply := Supply{ + Cap: new(big.Int).SetBytes(response.Cap), + Total: new(big.Int).SetBytes(response.Supply), + } + return method.Outputs.Pack(supply) +} diff --git a/precompiles/wrapped-a0gi-base/tx.go b/precompiles/wrapped-a0gi-base/tx.go new file mode 100644 index 00000000..74032d94 --- /dev/null +++ b/precompiles/wrapped-a0gi-base/tx.go @@ -0,0 +1,62 @@ +package wrappeda0gibase + +import ( + "errors" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/evmos/ethermint/x/evm/statedb" +) + +func (w *WrappedA0giBasePrecompile) Mint( + ctx sdk.Context, + evm *vm.EVM, + stateDB *statedb.StateDB, + contract *vm.Contract, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + msg, err := NewMsgMint(args) + if err != nil { + return nil, err + } + // validation + wa0gi := common.BytesToAddress(w.wrappeda0gibaseKeeper.GetWA0GIAddress(ctx)) + if contract.CallerAddress != wa0gi { + return nil, errors.New(ErrSenderNotWA0GI) + } + // execute + _, err = w.wrappeda0gibaseKeeper.Mint(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return nil, err + } + return method.Outputs.Pack() +} + +func (w *WrappedA0giBasePrecompile) Burn( + ctx sdk.Context, + evm *vm.EVM, + stateDB *statedb.StateDB, + contract *vm.Contract, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + msg, err := NewMsgBurn(args) + if err != nil { + return nil, err + } + // validation + wa0gi := common.BytesToAddress(w.wrappeda0gibaseKeeper.GetWA0GIAddress(ctx)) + if contract.CallerAddress != wa0gi { + return nil, errors.New(ErrSenderNotWA0GI) + } + // execute + _, err = w.wrappeda0gibaseKeeper.Burn(sdk.WrapSDKContext(ctx), msg) + if err != nil { + return nil, err + } + return method.Outputs.Pack() +} diff --git a/precompiles/wrapped-a0gi-base/types.go b/precompiles/wrapped-a0gi-base/types.go index 95e427bd..23c6c2c0 100644 --- a/precompiles/wrapped-a0gi-base/types.go +++ b/precompiles/wrapped-a0gi-base/types.go @@ -1,8 +1,51 @@ package wrappeda0gibase -import "math/big" +import ( + "fmt" + "math/big" + + precompiles_common "github.com/0glabs/0g-chain/precompiles/common" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/types" + "github.com/ethereum/go-ethereum/common" +) type Supply = struct { Cap *big.Int "json:\"cap\"" Total *big.Int "json:\"total\"" } + +func NewGetW0GIRequest(args []interface{}) (*types.GetWA0GIRequest, error) { + if len(args) != 0 { + return nil, fmt.Errorf(precompiles_common.ErrInvalidNumberOfArgs, 0, len(args)) + } + return &types.GetWA0GIRequest{}, nil +} + +func NewMinterSupplyRequest(args []interface{}) (*types.MinterSupplyRequest, error) { + if len(args) != 1 { + return nil, fmt.Errorf(precompiles_common.ErrInvalidNumberOfArgs, 1, len(args)) + } + return &types.MinterSupplyRequest{ + Address: args[0].(common.Address).Bytes(), + }, nil +} + +func NewMsgMint(args []interface{}) (*types.MsgMint, error) { + if len(args) != 2 { + return nil, fmt.Errorf(precompiles_common.ErrInvalidNumberOfArgs, 1, len(args)) + } + return &types.MsgMint{ + Minter: args[0].(common.Address).Bytes(), + Amount: args[1].(*big.Int).Bytes(), + }, nil +} + +func NewMsgBurn(args []interface{}) (*types.MsgBurn, error) { + if len(args) != 2 { + return nil, fmt.Errorf(precompiles_common.ErrInvalidNumberOfArgs, 1, len(args)) + } + return &types.MsgBurn{ + Minter: args[0].(common.Address).Bytes(), + Amount: args[1].(*big.Int).Bytes(), + }, nil +} diff --git a/precompiles/wrapped-a0gi-base/wrapped_a0gi_base.go b/precompiles/wrapped-a0gi-base/wrapped_a0gi_base.go index 088ecf40..ca3176dc 100644 --- a/precompiles/wrapped-a0gi-base/wrapped_a0gi_base.go +++ b/precompiles/wrapped-a0gi-base/wrapped_a0gi_base.go @@ -5,7 +5,7 @@ import ( precompiles_common "github.com/0glabs/0g-chain/precompiles/common" wrappeda0gibasekeeper "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/keeper" - "github.com/cosmos/cosmos-sdk/store/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" @@ -13,6 +13,13 @@ import ( const ( PrecompileAddress = "0x0000000000000000000000000000000000001002" + + // txs + WrappedA0GIBaseFunctionMint = "mint" + WrappedA0GIBaseFunctionBurn = "burn" + // queries + WrappedA0GIBaseFunctionGetWA0GI = "getWA0GI" + WrappedA0GIBaseFunctionMinterSupply = "minterSupply" ) var _ vm.PrecompiledContract = &WrappedA0giBasePrecompile{} @@ -25,17 +32,23 @@ type WrappedA0giBasePrecompile struct { // Abi implements common.PrecompileCommon. func (w *WrappedA0giBasePrecompile) Abi() *abi.ABI { - panic("unimplemented") + return &w.abi } // IsTx implements common.PrecompileCommon. -func (w *WrappedA0giBasePrecompile) IsTx(string) bool { - panic("unimplemented") +func (w *WrappedA0giBasePrecompile) IsTx(method string) bool { + switch method { + case WrappedA0GIBaseFunctionMint, + WrappedA0GIBaseFunctionBurn: + return true + default: + return false + } } // KVGasConfig implements common.PrecompileCommon. -func (w *WrappedA0giBasePrecompile) KVGasConfig() types.GasConfig { - panic("unimplemented") +func (w *WrappedA0giBasePrecompile) KVGasConfig() storetypes.GasConfig { + return storetypes.KVGasConfig() } // Address implements vm.PrecompiledContract. @@ -45,7 +58,7 @@ func (w *WrappedA0giBasePrecompile) Address() common.Address { // RequiredGas implements vm.PrecompiledContract. func (w *WrappedA0giBasePrecompile) RequiredGas(input []byte) uint64 { - panic("unimplemented") + return 0 } func NewWrappedA0giBasePrecompile(wrappeda0gibaseKeeper wrappeda0gibasekeeper.Keeper) (*WrappedA0giBasePrecompile, error) { @@ -61,5 +74,33 @@ func NewWrappedA0giBasePrecompile(wrappeda0gibaseKeeper wrappeda0gibasekeeper.Ke // Run implements vm.PrecompiledContract. func (w *WrappedA0giBasePrecompile) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) { - panic("unimplemented") + ctx, stateDB, method, initialGas, args, err := precompiles_common.InitializePrecompileCall(w, evm, contract, readonly) + if err != nil { + return nil, err + } + + var bz []byte + switch method.Name { + // queries + case WrappedA0GIBaseFunctionGetWA0GI: + bz, err = w.GetW0GI(ctx, evm, method, args) + case WrappedA0GIBaseFunctionMinterSupply: + bz, err = w.MinterSupply(ctx, evm, method, args) + // txs + case WrappedA0GIBaseFunctionMint: + bz, err = w.Mint(ctx, evm, stateDB, contract, method, args) + case WrappedA0GIBaseFunctionBurn: + bz, err = w.Burn(ctx, evm, stateDB, contract, method, args) + } + + if err != nil { + return nil, err + } + + cost := ctx.GasMeter().GasConsumed() - initialGas + + if !contract.UseGas(cost) { + return nil, vm.ErrOutOfGas + } + return bz, nil }