diff --git a/contracts/Makefile b/contracts/Makefile index 0e970bb05..0700e4181 100644 --- a/contracts/Makefile +++ b/contracts/Makefile @@ -36,7 +36,8 @@ all: install-deps build bindings allocs ## Build contracts, generate bindings an CORE_CONTRACTS := OmniPortal FeeOracleV1 Create3 TransparentUpgradeableProxy \ Staking Slashing OmniBridgeL1 OmniBridgeNative Omni WOmni \ PortalRegistry AllocPredeploys PingPong ProxyAdmin Admin \ - OmniGasPump OmniGasStation FeeOracleV2 + OmniGasPump OmniGasStation FeeOracleV2 IERC20 \ + node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20 SOLVE_CONTRACTS := SolveInbox SolveOutbox MockToken MockVault diff --git a/contracts/bindings/ierc20.go b/contracts/bindings/ierc20.go new file mode 100644 index 000000000..987286781 --- /dev/null +++ b/contracts/bindings/ierc20.go @@ -0,0 +1,645 @@ +// 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/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/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 +) + +// IERC20MetaData contains all meta data concerning the IERC20 contract. +var IERC20MetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"allowance\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"spender\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"approve\",\"inputs\":[{\"name\":\"spender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"balanceOf\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"totalSupply\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transfer\",\"inputs\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferFrom\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"Approval\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"spender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Transfer\",\"inputs\":[{\"name\":\"from\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"to\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false}]", +} + +// IERC20ABI is the input ABI used to generate the binding from. +// Deprecated: Use IERC20MetaData.ABI instead. +var IERC20ABI = IERC20MetaData.ABI + +// IERC20 is an auto generated Go binding around an Ethereum contract. +type IERC20 struct { + IERC20Caller // Read-only binding to the contract + IERC20Transactor // Write-only binding to the contract + IERC20Filterer // Log filterer for contract events +} + +// IERC20Caller is an auto generated read-only Go binding around an Ethereum contract. +type IERC20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type IERC20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type IERC20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IERC20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type IERC20Session struct { + Contract *IERC20 // 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 +} + +// IERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type IERC20CallerSession struct { + Contract *IERC20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// IERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type IERC20TransactorSession struct { + Contract *IERC20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IERC20Raw is an auto generated low-level Go binding around an Ethereum contract. +type IERC20Raw struct { + Contract *IERC20 // Generic contract binding to access the raw methods on +} + +// IERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type IERC20CallerRaw struct { + Contract *IERC20Caller // Generic read-only contract binding to access the raw methods on +} + +// IERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type IERC20TransactorRaw struct { + Contract *IERC20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewIERC20 creates a new instance of IERC20, bound to a specific deployed contract. +func NewIERC20(address common.Address, backend bind.ContractBackend) (*IERC20, error) { + contract, err := bindIERC20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IERC20{IERC20Caller: IERC20Caller{contract: contract}, IERC20Transactor: IERC20Transactor{contract: contract}, IERC20Filterer: IERC20Filterer{contract: contract}}, nil +} + +// NewIERC20Caller creates a new read-only instance of IERC20, bound to a specific deployed contract. +func NewIERC20Caller(address common.Address, caller bind.ContractCaller) (*IERC20Caller, error) { + contract, err := bindIERC20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IERC20Caller{contract: contract}, nil +} + +// NewIERC20Transactor creates a new write-only instance of IERC20, bound to a specific deployed contract. +func NewIERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*IERC20Transactor, error) { + contract, err := bindIERC20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IERC20Transactor{contract: contract}, nil +} + +// NewIERC20Filterer creates a new log filterer instance of IERC20, bound to a specific deployed contract. +func NewIERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*IERC20Filterer, error) { + contract, err := bindIERC20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IERC20Filterer{contract: contract}, nil +} + +// bindIERC20 binds a generic wrapper to an already deployed contract. +func bindIERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IERC20MetaData.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 (_IERC20 *IERC20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IERC20.Contract.IERC20Caller.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 (_IERC20 *IERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IERC20.Contract.IERC20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IERC20 *IERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IERC20.Contract.IERC20Transactor.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 (_IERC20 *IERC20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IERC20.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 (_IERC20 *IERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IERC20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IERC20 *IERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IERC20.Contract.contract.Transact(opts, method, params...) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_IERC20 *IERC20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { + var out []interface{} + err := _IERC20.contract.Call(opts, &out, "allowance", owner, spender) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_IERC20 *IERC20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _IERC20.Contract.Allowance(&_IERC20.CallOpts, owner, spender) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_IERC20 *IERC20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _IERC20.Contract.Allowance(&_IERC20.CallOpts, owner, spender) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_IERC20 *IERC20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { + var out []interface{} + err := _IERC20.contract.Call(opts, &out, "balanceOf", account) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_IERC20 *IERC20Session) BalanceOf(account common.Address) (*big.Int, error) { + return _IERC20.Contract.BalanceOf(&_IERC20.CallOpts, account) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_IERC20 *IERC20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { + return _IERC20.Contract.BalanceOf(&_IERC20.CallOpts, account) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_IERC20 *IERC20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IERC20.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_IERC20 *IERC20Session) TotalSupply() (*big.Int, error) { + return _IERC20.Contract.TotalSupply(&_IERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_IERC20 *IERC20CallerSession) TotalSupply() (*big.Int, error) { + return _IERC20.Contract.TotalSupply(&_IERC20.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_IERC20 *IERC20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.contract.Transact(opts, "approve", spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_IERC20 *IERC20Session) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.Contract.Approve(&_IERC20.TransactOpts, spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_IERC20 *IERC20TransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.Contract.Approve(&_IERC20.TransactOpts, spender, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_IERC20 *IERC20Transactor) Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.contract.Transact(opts, "transfer", to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_IERC20 *IERC20Session) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.Contract.Transfer(&_IERC20.TransactOpts, to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_IERC20 *IERC20TransactorSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.Contract.Transfer(&_IERC20.TransactOpts, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_IERC20 *IERC20Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.contract.Transact(opts, "transferFrom", from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_IERC20 *IERC20Session) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.Contract.TransferFrom(&_IERC20.TransactOpts, from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_IERC20 *IERC20TransactorSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _IERC20.Contract.TransferFrom(&_IERC20.TransactOpts, from, to, value) +} + +// IERC20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the IERC20 contract. +type IERC20ApprovalIterator struct { + Event *IERC20Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *IERC20ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(IERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(IERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *IERC20ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IERC20ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IERC20Approval represents a Approval event raised by the IERC20 contract. +type IERC20Approval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_IERC20 *IERC20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*IERC20ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _IERC20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &IERC20ApprovalIterator{contract: _IERC20.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_IERC20 *IERC20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *IERC20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _IERC20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(IERC20Approval) + if err := _IERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_IERC20 *IERC20Filterer) ParseApproval(log types.Log) (*IERC20Approval, error) { + event := new(IERC20Approval) + if err := _IERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// IERC20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the IERC20 contract. +type IERC20TransferIterator struct { + Event *IERC20Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *IERC20TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(IERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(IERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *IERC20TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IERC20TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IERC20Transfer represents a Transfer event raised by the IERC20 contract. +type IERC20Transfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_IERC20 *IERC20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*IERC20TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IERC20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &IERC20TransferIterator{contract: _IERC20.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_IERC20 *IERC20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *IERC20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _IERC20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(IERC20Transfer) + if err := _IERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_IERC20 *IERC20Filterer) ParseTransfer(log types.Log) (*IERC20Transfer, error) { + event := new(IERC20Transfer) + if err := _IERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/e2e/app/fund.go b/e2e/app/fund.go index cb2863c04..0bfb4ca88 100644 --- a/e2e/app/fund.go +++ b/e2e/app/fund.go @@ -114,10 +114,8 @@ func FundAccounts(ctx context.Context, def Definition, hotOnly bool, dryRun bool "address", contract.Address, ) - isOmniEVM := chain.ChainID == def.Testnet.Network.Static().OmniExecutionChainID - - if contract.OnlyOmniEVM && !isOmniEVM { - log.Info(ctrCtx, "Skipping non-OmniEVM chain", "chain", chain.ChainID) + if !contract.IsDeployedOn(chain.ChainID, def.Testnet.Network) { + log.Info(ctrCtx, "Skipping chain without deplyment", "chain", chain.ChainID, "contract", contract.Name) continue } diff --git a/lib/contracts/contracts.go b/lib/contracts/contracts.go index 5d26c8d9f..c884a8174 100644 --- a/lib/contracts/contracts.go +++ b/lib/contracts/contracts.go @@ -17,10 +17,15 @@ import ( type Contract struct { Name string Address common.Address - OnlyOmniEVM bool - NotOmniEVM bool FundThresholds *FundThresholds WithdrawThresholds *WithdrawThresholds + IsDeployedOn func(chainID uint64, network netconf.ID) bool + Tokens func(chainID uint64, network netconf.ID) []Token +} + +type Token struct { + Symbol string + Address common.Address } // ToMonitor returns all contracts for the given network relevant to the monitor. @@ -30,39 +35,71 @@ func ToMonitor(ctx context.Context, network netconf.ID) ([]Contract, error) { return nil, err } - return []Contract{ - // Funded contracts - { - Name: "gas-station", - Address: addrs.GasStation, - OnlyOmniEVM: true, - NotOmniEVM: false, - FundThresholds: &FundThresholds{minEther: 10, targetEther: 100}, // GasStation funds user GasPump requests, and needs a large OMNI balance. - }, - // Withdrawal contracts - { - Name: "gas-pump", - Address: addrs.GasPump, - OnlyOmniEVM: false, - NotOmniEVM: true, - WithdrawThresholds: &WithdrawThresholds{maxEther: 10}, - }, - // Monitoring contracts - { - Name: "staking", - Address: common.HexToAddress(predeploys.Staking), - OnlyOmniEVM: true, - NotOmniEVM: false, - }, - { - Name: "nativeBridge", - Address: common.HexToAddress(predeploys.OmniBridgeNative), - OnlyOmniEVM: true, - NotOmniEVM: false, + // GasStation funds user GasPump requests, and needs a large OMNI balance. It is only deployed on OmniEVM. + gasStation := Contract{ + Name: "gas-station", + Address: addrs.GasStation, + FundThresholds: &FundThresholds{minEther: 10, targetEther: 100}, + IsDeployedOn: isOmni, + } + + // GasPump is collects ETH from users. It is only deployed on ETH chains. + gasPump := Contract{ + Name: "gas-pump", + Address: addrs.GasPump, + WithdrawThresholds: &WithdrawThresholds{maxEther: 10}, + IsDeployedOn: isNotOmni, + } + + // Staking contract collects validator deposits. It is only deployed on OmniEVM. + staking := Contract{ + Name: "staking", + Address: common.HexToAddress(predeploys.Staking), + IsDeployedOn: isOmni, + } + + // NativeBridge collects & spends native OMNI. It is only deployed on OmniEVM. + nativeBridge := Contract{ + Name: "native-bridge", + Address: common.HexToAddress(predeploys.OmniBridgeNative), + IsDeployedOn: isOmni, + } + + // L1Bridge collects & spends OMNI ERC20 on Ethereum. It is only deployed on Ethereum. + l1Bridge := Contract{ + Name: "l1-bridge", + Address: addrs.L1Bridge, + IsDeployedOn: isEthereum, + Tokens: func(chainID uint64, network netconf.ID) []Token { + if !netconf.IsEthereumChain(network, chainID) { + return nil + } + + return []Token{{Symbol: "OMNI", Address: addrs.Token}} }, + } + + return []Contract{ + gasStation, + gasPump, + staking, + nativeBridge, + l1Bridge, }, nil } +func isOmni(chainID uint64, network netconf.ID) bool { + return chainID == network.Static().OmniExecutionChainID +} + +func isNotOmni(chainID uint64, network netconf.ID) bool { + return chainID != network.Static().OmniExecutionChainID && chainID != network.Static().OmniConsensusChainIDUint64() +} + +func isEthereum(chainID uint64, network netconf.ID) bool { + return netconf.IsEthereumChain(network, chainID) +} + // ToFund returns all fundable contracts for the given network. func ToFund(ctx context.Context, network netconf.ID) ([]Contract, error) { contracts, err := ToMonitor(ctx, network) diff --git a/monitor/contract/metrics.go b/monitor/contract/metrics.go index 921f84186..039843c02 100644 --- a/monitor/contract/metrics.go +++ b/monitor/contract/metrics.go @@ -13,6 +13,13 @@ var ( Help: "The balance of the contract on a specific chain in ether. Alert if low or high.", }, []string{"chain", "name"}) + contractTokenBalance = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "monitor", + Subsystem: "contract", + Name: "balance_token", + Help: "The balance of the contract of specific ERC20 tokens on a specific chain.", + }, []string{"chain", "name", "token_symbol", "token_address"}) + contractBalanceLow = promauto.NewGaugeVec(prometheus.GaugeOpts{ Namespace: "monitor", Subsystem: "contract", diff --git a/monitor/contract/monitor.go b/monitor/contract/monitor.go index f3656b259..d6fdf350e 100644 --- a/monitor/contract/monitor.go +++ b/monitor/contract/monitor.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/omni-network/omni/contracts/bindings" "github.com/omni-network/omni/lib/contracts" "github.com/omni-network/omni/lib/errors" "github.com/omni-network/omni/lib/ethclient" @@ -11,6 +12,7 @@ import ( "github.com/omni-network/omni/lib/netconf" "github.com/omni-network/omni/lib/xchain" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/params" ) @@ -41,14 +43,12 @@ func StartMonitoring(ctx context.Context, network netconf.Network, endpoints xch } for _, chain := range network.EVMChains() { - isOmniEVM := chain.ID == network.ID.Static().OmniExecutionChainID - for _, contract := range allContracts { - if (contract.OnlyOmniEVM && !isOmniEVM) || (contract.NotOmniEVM && isOmniEVM) { + if !contract.IsDeployedOn(chain.ID, network.ID) { continue } - go monitorContractForever(ctx, contract, chain.Name, rpcClients[chain.ID]) + go monitorContractForever(ctx, contract, chain, network.ID, rpcClients[chain.ID]) } } @@ -59,11 +59,12 @@ func StartMonitoring(ctx context.Context, network netconf.Network, endpoints xch func monitorContractForever( ctx context.Context, contract contracts.Contract, - chainName string, + chain netconf.Chain, + network netconf.ID, client ethclient.Client, ) { ctx = log.WithCtx(ctx, - "chain", chainName, + "chain", chain.Name, "name", contract.Name, "address", contract.Address, ) @@ -78,7 +79,7 @@ func monitorContractForever( case <-ctx.Done(): return case <-ticker.C: - err := monitorContractOnce(ctx, contract, chainName, client) + err := monitorContractOnce(ctx, contract, chain, network, client) if ctx.Err() != nil { return } else if err != nil { @@ -93,7 +94,8 @@ func monitorContractForever( func monitorContractOnce( ctx context.Context, contract contracts.Contract, - chainName string, + chain netconf.Chain, + network netconf.ID, client ethclient.Client, ) error { balance, err := client.BalanceAt(ctx, contract.Address, nil) @@ -106,7 +108,7 @@ func monitorContractOnce( balanceEth := bf / params.Ether // Always set the balance metric - contractBalance.WithLabelValues(chainName, contract.Name).Set(balanceEth) + contractBalance.WithLabelValues(chain.Name, contract.Name).Set(balanceEth) // Handle funding threshold checks, if any if contract.FundThresholds != nil { @@ -115,7 +117,7 @@ func monitorContractOnce( isLow = 1 } - contractBalanceLow.WithLabelValues(chainName, contract.Name).Set(isLow) + contractBalanceLow.WithLabelValues(chain.Name, contract.Name).Set(isLow) } // Handle withdrawal threshold checks, if any @@ -125,7 +127,26 @@ func monitorContractOnce( isHigh = 1 } - contractBalanceHigh.WithLabelValues(chainName, contract.Name).Set(isHigh) + contractBalanceHigh.WithLabelValues(chain.Name, contract.Name).Set(isHigh) + } + + // Monitor token balances, if any + if contract.Tokens != nil { + for _, t := range contract.Tokens(chain.ID, network) { + token, err := bindings.NewIERC20(t.Address, client) + if err != nil { + return err + } + + balance, err := token.BalanceOf(&bind.CallOpts{Context: ctx}, contract.Address) + if err != nil { + return err + } + + bf, _ := balance.Float64() + balanceEth := bf / params.Ether + contractTokenBalance.WithLabelValues(chain.Name, contract.Name, t.Symbol, t.Address.Hex()).Set(balanceEth) + } } return nil