diff --git a/bridge/pkg/common/types.go b/bridge/pkg/common/types.go new file mode 100644 index 000000000..07442dfb7 --- /dev/null +++ b/bridge/pkg/common/types.go @@ -0,0 +1,25 @@ +package common + +import ( + "github.com/certusone/wormhole/bridge/pkg/vaa" + "math/big" +) + +type ( + BridgeWatcher interface { + WatchLockups(events chan *ChainLock) error + } + + ChainLock struct { + SourceAddress vaa.Address + TargetAddress vaa.Address + + SourceChain vaa.ChainID + TargetChain vaa.ChainID + + TokenChain vaa.ChainID + TokenAddress vaa.Address + + Amount *big.Int + } +) diff --git a/bridge/pkg/ethereum/abi/abi.go b/bridge/pkg/ethereum/abi/abi.go new file mode 100644 index 000000000..a90513984 --- /dev/null +++ b/bridge/pkg/ethereum/abi/abi.go @@ -0,0 +1,824 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package abi + +import ( + "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 ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// WormholeGuardianSet is an auto generated low-level Go binding around an user-defined struct. +type WormholeGuardianSet struct { + X *big.Int + Parity uint8 + ExpirationTime uint32 +} + +// WormholeBridgeABI is the input ABI used to generate the binding from. +const WormholeBridgeABI = "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"parity\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"initial_guardian_set\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"wrapped_asset_master\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"parity\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"indexed\":true,\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"oldGuardian\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"parity\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"expiration_time\",\"type\":\"uint32\"}],\"indexed\":true,\"internalType\":\"structWormhole.GuardianSet\",\"name\":\"newGuardian\",\"type\":\"tuple\"}],\"name\":\"LogGuardianSetChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"token_chain\",\"type\":\"uint8\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"token\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LogTokensLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LogTokensUnlocked\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"guardian_set_index\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"}],\"name\":\"lockAssets\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"target_chain\",\"type\":\"uint8\"}],\"name\":\"lockETH\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"vaa\",\"type\":\"bytes\"}],\"name\":\"submitVAA\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vaa_expiry\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wrappedAssetMaster\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]" + +// WormholeBridge is an auto generated Go binding around an Ethereum contract. +type WormholeBridge struct { + WormholeBridgeCaller // Read-only binding to the contract + WormholeBridgeTransactor // Write-only binding to the contract + WormholeBridgeFilterer // Log filterer for contract events +} + +// WormholeBridgeCaller is an auto generated read-only Go binding around an Ethereum contract. +type WormholeBridgeCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// WormholeBridgeTransactor is an auto generated write-only Go binding around an Ethereum contract. +type WormholeBridgeTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// WormholeBridgeFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type WormholeBridgeFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// WormholeBridgeSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type WormholeBridgeSession struct { + Contract *WormholeBridge // 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 +} + +// WormholeBridgeCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type WormholeBridgeCallerSession struct { + Contract *WormholeBridgeCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// WormholeBridgeTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type WormholeBridgeTransactorSession struct { + Contract *WormholeBridgeTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// WormholeBridgeRaw is an auto generated low-level Go binding around an Ethereum contract. +type WormholeBridgeRaw struct { + Contract *WormholeBridge // Generic contract binding to access the raw methods on +} + +// WormholeBridgeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type WormholeBridgeCallerRaw struct { + Contract *WormholeBridgeCaller // Generic read-only contract binding to access the raw methods on +} + +// WormholeBridgeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type WormholeBridgeTransactorRaw struct { + Contract *WormholeBridgeTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewWormholeBridge creates a new instance of WormholeBridge, bound to a specific deployed contract. +func NewWormholeBridge(address common.Address, backend bind.ContractBackend) (*WormholeBridge, error) { + contract, err := bindWormholeBridge(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &WormholeBridge{WormholeBridgeCaller: WormholeBridgeCaller{contract: contract}, WormholeBridgeTransactor: WormholeBridgeTransactor{contract: contract}, WormholeBridgeFilterer: WormholeBridgeFilterer{contract: contract}}, nil +} + +// NewWormholeBridgeCaller creates a new read-only instance of WormholeBridge, bound to a specific deployed contract. +func NewWormholeBridgeCaller(address common.Address, caller bind.ContractCaller) (*WormholeBridgeCaller, error) { + contract, err := bindWormholeBridge(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &WormholeBridgeCaller{contract: contract}, nil +} + +// NewWormholeBridgeTransactor creates a new write-only instance of WormholeBridge, bound to a specific deployed contract. +func NewWormholeBridgeTransactor(address common.Address, transactor bind.ContractTransactor) (*WormholeBridgeTransactor, error) { + contract, err := bindWormholeBridge(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &WormholeBridgeTransactor{contract: contract}, nil +} + +// NewWormholeBridgeFilterer creates a new log filterer instance of WormholeBridge, bound to a specific deployed contract. +func NewWormholeBridgeFilterer(address common.Address, filterer bind.ContractFilterer) (*WormholeBridgeFilterer, error) { + contract, err := bindWormholeBridge(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &WormholeBridgeFilterer{contract: contract}, nil +} + +// bindWormholeBridge binds a generic wrapper to an already deployed contract. +func bindWormholeBridge(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(WormholeBridgeABI)) + 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 (_WormholeBridge *WormholeBridgeRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _WormholeBridge.Contract.WormholeBridgeCaller.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 (_WormholeBridge *WormholeBridgeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _WormholeBridge.Contract.WormholeBridgeTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_WormholeBridge *WormholeBridgeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _WormholeBridge.Contract.WormholeBridgeTransactor.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 (_WormholeBridge *WormholeBridgeCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _WormholeBridge.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 (_WormholeBridge *WormholeBridgeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _WormholeBridge.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_WormholeBridge *WormholeBridgeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _WormholeBridge.Contract.contract.Transact(opts, method, params...) +} + +// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3. +// +// Solidity: function guardian_set_index() view returns(uint32) +func (_WormholeBridge *WormholeBridgeCaller) GuardianSetIndex(opts *bind.CallOpts) (uint32, error) { + var ( + ret0 = new(uint32) + ) + out := ret0 + err := _WormholeBridge.contract.Call(opts, out, "guardian_set_index") + return *ret0, err +} + +// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3. +// +// Solidity: function guardian_set_index() view returns(uint32) +func (_WormholeBridge *WormholeBridgeSession) GuardianSetIndex() (uint32, error) { + return _WormholeBridge.Contract.GuardianSetIndex(&_WormholeBridge.CallOpts) +} + +// GuardianSetIndex is a free data retrieval call binding the contract method 0x822d82b3. +// +// Solidity: function guardian_set_index() view returns(uint32) +func (_WormholeBridge *WormholeBridgeCallerSession) GuardianSetIndex() (uint32, error) { + return _WormholeBridge.Contract.GuardianSetIndex(&_WormholeBridge.CallOpts) +} + +// VaaExpiry is a free data retrieval call binding the contract method 0x7f04d9e6. +// +// Solidity: function vaa_expiry() view returns(uint32) +func (_WormholeBridge *WormholeBridgeCaller) VaaExpiry(opts *bind.CallOpts) (uint32, error) { + var ( + ret0 = new(uint32) + ) + out := ret0 + err := _WormholeBridge.contract.Call(opts, out, "vaa_expiry") + return *ret0, err +} + +// VaaExpiry is a free data retrieval call binding the contract method 0x7f04d9e6. +// +// Solidity: function vaa_expiry() view returns(uint32) +func (_WormholeBridge *WormholeBridgeSession) VaaExpiry() (uint32, error) { + return _WormholeBridge.Contract.VaaExpiry(&_WormholeBridge.CallOpts) +} + +// VaaExpiry is a free data retrieval call binding the contract method 0x7f04d9e6. +// +// Solidity: function vaa_expiry() view returns(uint32) +func (_WormholeBridge *WormholeBridgeCallerSession) VaaExpiry() (uint32, error) { + return _WormholeBridge.Contract.VaaExpiry(&_WormholeBridge.CallOpts) +} + +// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c. +// +// Solidity: function wrappedAssetMaster() view returns(address) +func (_WormholeBridge *WormholeBridgeCaller) WrappedAssetMaster(opts *bind.CallOpts) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _WormholeBridge.contract.Call(opts, out, "wrappedAssetMaster") + return *ret0, err +} + +// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c. +// +// Solidity: function wrappedAssetMaster() view returns(address) +func (_WormholeBridge *WormholeBridgeSession) WrappedAssetMaster() (common.Address, error) { + return _WormholeBridge.Contract.WrappedAssetMaster(&_WormholeBridge.CallOpts) +} + +// WrappedAssetMaster is a free data retrieval call binding the contract method 0x99da1d3c. +// +// Solidity: function wrappedAssetMaster() view returns(address) +func (_WormholeBridge *WormholeBridgeCallerSession) WrappedAssetMaster() (common.Address, error) { + return _WormholeBridge.Contract.WrappedAssetMaster(&_WormholeBridge.CallOpts) +} + +// LockAssets is a paid mutator transaction binding the contract method 0xe66fd373. +// +// Solidity: function lockAssets(address asset, uint256 amount, bytes32 recipient, uint8 target_chain) returns() +func (_WormholeBridge *WormholeBridgeTransactor) LockAssets(opts *bind.TransactOpts, asset common.Address, amount *big.Int, recipient [32]byte, target_chain uint8) (*types.Transaction, error) { + return _WormholeBridge.contract.Transact(opts, "lockAssets", asset, amount, recipient, target_chain) +} + +// LockAssets is a paid mutator transaction binding the contract method 0xe66fd373. +// +// Solidity: function lockAssets(address asset, uint256 amount, bytes32 recipient, uint8 target_chain) returns() +func (_WormholeBridge *WormholeBridgeSession) LockAssets(asset common.Address, amount *big.Int, recipient [32]byte, target_chain uint8) (*types.Transaction, error) { + return _WormholeBridge.Contract.LockAssets(&_WormholeBridge.TransactOpts, asset, amount, recipient, target_chain) +} + +// LockAssets is a paid mutator transaction binding the contract method 0xe66fd373. +// +// Solidity: function lockAssets(address asset, uint256 amount, bytes32 recipient, uint8 target_chain) returns() +func (_WormholeBridge *WormholeBridgeTransactorSession) LockAssets(asset common.Address, amount *big.Int, recipient [32]byte, target_chain uint8) (*types.Transaction, error) { + return _WormholeBridge.Contract.LockAssets(&_WormholeBridge.TransactOpts, asset, amount, recipient, target_chain) +} + +// LockETH is a paid mutator transaction binding the contract method 0x780e2183. +// +// Solidity: function lockETH(bytes32 recipient, uint8 target_chain) payable returns() +func (_WormholeBridge *WormholeBridgeTransactor) LockETH(opts *bind.TransactOpts, recipient [32]byte, target_chain uint8) (*types.Transaction, error) { + return _WormholeBridge.contract.Transact(opts, "lockETH", recipient, target_chain) +} + +// LockETH is a paid mutator transaction binding the contract method 0x780e2183. +// +// Solidity: function lockETH(bytes32 recipient, uint8 target_chain) payable returns() +func (_WormholeBridge *WormholeBridgeSession) LockETH(recipient [32]byte, target_chain uint8) (*types.Transaction, error) { + return _WormholeBridge.Contract.LockETH(&_WormholeBridge.TransactOpts, recipient, target_chain) +} + +// LockETH is a paid mutator transaction binding the contract method 0x780e2183. +// +// Solidity: function lockETH(bytes32 recipient, uint8 target_chain) payable returns() +func (_WormholeBridge *WormholeBridgeTransactorSession) LockETH(recipient [32]byte, target_chain uint8) (*types.Transaction, error) { + return _WormholeBridge.Contract.LockETH(&_WormholeBridge.TransactOpts, recipient, target_chain) +} + +// SubmitVAA is a paid mutator transaction binding the contract method 0x3bc0aee6. +// +// Solidity: function submitVAA(bytes vaa) returns() +func (_WormholeBridge *WormholeBridgeTransactor) SubmitVAA(opts *bind.TransactOpts, vaa []byte) (*types.Transaction, error) { + return _WormholeBridge.contract.Transact(opts, "submitVAA", vaa) +} + +// SubmitVAA is a paid mutator transaction binding the contract method 0x3bc0aee6. +// +// Solidity: function submitVAA(bytes vaa) returns() +func (_WormholeBridge *WormholeBridgeSession) SubmitVAA(vaa []byte) (*types.Transaction, error) { + return _WormholeBridge.Contract.SubmitVAA(&_WormholeBridge.TransactOpts, vaa) +} + +// SubmitVAA is a paid mutator transaction binding the contract method 0x3bc0aee6. +// +// Solidity: function submitVAA(bytes vaa) returns() +func (_WormholeBridge *WormholeBridgeTransactorSession) SubmitVAA(vaa []byte) (*types.Transaction, error) { + return _WormholeBridge.Contract.SubmitVAA(&_WormholeBridge.TransactOpts, vaa) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_WormholeBridge *WormholeBridgeTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _WormholeBridge.contract.RawTransact(opts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_WormholeBridge *WormholeBridgeSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _WormholeBridge.Contract.Fallback(&_WormholeBridge.TransactOpts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_WormholeBridge *WormholeBridgeTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _WormholeBridge.Contract.Fallback(&_WormholeBridge.TransactOpts, calldata) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_WormholeBridge *WormholeBridgeTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _WormholeBridge.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_WormholeBridge *WormholeBridgeSession) Receive() (*types.Transaction, error) { + return _WormholeBridge.Contract.Receive(&_WormholeBridge.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_WormholeBridge *WormholeBridgeTransactorSession) Receive() (*types.Transaction, error) { + return _WormholeBridge.Contract.Receive(&_WormholeBridge.TransactOpts) +} + +// WormholeBridgeLogGuardianSetChangedIterator is returned from FilterLogGuardianSetChanged and is used to iterate over the raw logs and unpacked data for LogGuardianSetChanged events raised by the WormholeBridge contract. +type WormholeBridgeLogGuardianSetChangedIterator struct { + Event *WormholeBridgeLogGuardianSetChanged // 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 *WormholeBridgeLogGuardianSetChangedIterator) 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(WormholeBridgeLogGuardianSetChanged) + 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(WormholeBridgeLogGuardianSetChanged) + 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 *WormholeBridgeLogGuardianSetChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *WormholeBridgeLogGuardianSetChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// WormholeBridgeLogGuardianSetChanged represents a LogGuardianSetChanged event raised by the WormholeBridge contract. +type WormholeBridgeLogGuardianSetChanged struct { + OldGuardian WormholeGuardianSet + NewGuardian WormholeGuardianSet + Raw types.Log // Blockchain specific contextual infos +} + +// FilterLogGuardianSetChanged is a free log retrieval operation binding the contract event 0x776a7721d091beb15fb219d7be3c92b83fa7c10428af15a7312461bc3bc52e0b. +// +// Solidity: event LogGuardianSetChanged((uint256,uint8,uint32) indexed oldGuardian, (uint256,uint8,uint32) indexed newGuardian) +func (_WormholeBridge *WormholeBridgeFilterer) FilterLogGuardianSetChanged(opts *bind.FilterOpts, oldGuardian []WormholeGuardianSet, newGuardian []WormholeGuardianSet) (*WormholeBridgeLogGuardianSetChangedIterator, error) { + + var oldGuardianRule []interface{} + for _, oldGuardianItem := range oldGuardian { + oldGuardianRule = append(oldGuardianRule, oldGuardianItem) + } + var newGuardianRule []interface{} + for _, newGuardianItem := range newGuardian { + newGuardianRule = append(newGuardianRule, newGuardianItem) + } + + logs, sub, err := _WormholeBridge.contract.FilterLogs(opts, "LogGuardianSetChanged", oldGuardianRule, newGuardianRule) + if err != nil { + return nil, err + } + return &WormholeBridgeLogGuardianSetChangedIterator{contract: _WormholeBridge.contract, event: "LogGuardianSetChanged", logs: logs, sub: sub}, nil +} + +// WatchLogGuardianSetChanged is a free log subscription operation binding the contract event 0x776a7721d091beb15fb219d7be3c92b83fa7c10428af15a7312461bc3bc52e0b. +// +// Solidity: event LogGuardianSetChanged((uint256,uint8,uint32) indexed oldGuardian, (uint256,uint8,uint32) indexed newGuardian) +func (_WormholeBridge *WormholeBridgeFilterer) WatchLogGuardianSetChanged(opts *bind.WatchOpts, sink chan<- *WormholeBridgeLogGuardianSetChanged, oldGuardian []WormholeGuardianSet, newGuardian []WormholeGuardianSet) (event.Subscription, error) { + + var oldGuardianRule []interface{} + for _, oldGuardianItem := range oldGuardian { + oldGuardianRule = append(oldGuardianRule, oldGuardianItem) + } + var newGuardianRule []interface{} + for _, newGuardianItem := range newGuardian { + newGuardianRule = append(newGuardianRule, newGuardianItem) + } + + logs, sub, err := _WormholeBridge.contract.WatchLogs(opts, "LogGuardianSetChanged", oldGuardianRule, newGuardianRule) + 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(WormholeBridgeLogGuardianSetChanged) + if err := _WormholeBridge.contract.UnpackLog(event, "LogGuardianSetChanged", 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 +} + +// ParseLogGuardianSetChanged is a log parse operation binding the contract event 0x776a7721d091beb15fb219d7be3c92b83fa7c10428af15a7312461bc3bc52e0b. +// +// Solidity: event LogGuardianSetChanged((uint256,uint8,uint32) indexed oldGuardian, (uint256,uint8,uint32) indexed newGuardian) +func (_WormholeBridge *WormholeBridgeFilterer) ParseLogGuardianSetChanged(log types.Log) (*WormholeBridgeLogGuardianSetChanged, error) { + event := new(WormholeBridgeLogGuardianSetChanged) + if err := _WormholeBridge.contract.UnpackLog(event, "LogGuardianSetChanged", log); err != nil { + return nil, err + } + return event, nil +} + +// WormholeBridgeLogTokensLockedIterator is returned from FilterLogTokensLocked and is used to iterate over the raw logs and unpacked data for LogTokensLocked events raised by the WormholeBridge contract. +type WormholeBridgeLogTokensLockedIterator struct { + Event *WormholeBridgeLogTokensLocked // 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 *WormholeBridgeLogTokensLockedIterator) 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(WormholeBridgeLogTokensLocked) + 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(WormholeBridgeLogTokensLocked) + 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 *WormholeBridgeLogTokensLockedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *WormholeBridgeLogTokensLockedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// WormholeBridgeLogTokensLocked represents a LogTokensLocked event raised by the WormholeBridge contract. +type WormholeBridgeLogTokensLocked struct { + TargetChain uint8 + TokenChain uint8 + Token [32]byte + Sender [32]byte + Recipient [32]byte + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterLogTokensLocked is a free log retrieval operation binding the contract event 0x84b445260a99044cc9529b3033663c078031a14e31f3c255ff02c62667bab14b. +// +// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount) +func (_WormholeBridge *WormholeBridgeFilterer) FilterLogTokensLocked(opts *bind.FilterOpts, token [][32]byte, sender [][32]byte) (*WormholeBridgeLogTokensLockedIterator, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _WormholeBridge.contract.FilterLogs(opts, "LogTokensLocked", tokenRule, senderRule) + if err != nil { + return nil, err + } + return &WormholeBridgeLogTokensLockedIterator{contract: _WormholeBridge.contract, event: "LogTokensLocked", logs: logs, sub: sub}, nil +} + +// WatchLogTokensLocked is a free log subscription operation binding the contract event 0x84b445260a99044cc9529b3033663c078031a14e31f3c255ff02c62667bab14b. +// +// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount) +func (_WormholeBridge *WormholeBridgeFilterer) WatchLogTokensLocked(opts *bind.WatchOpts, sink chan<- *WormholeBridgeLogTokensLocked, token [][32]byte, sender [][32]byte) (event.Subscription, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _WormholeBridge.contract.WatchLogs(opts, "LogTokensLocked", tokenRule, senderRule) + 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(WormholeBridgeLogTokensLocked) + if err := _WormholeBridge.contract.UnpackLog(event, "LogTokensLocked", 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 +} + +// ParseLogTokensLocked is a log parse operation binding the contract event 0x84b445260a99044cc9529b3033663c078031a14e31f3c255ff02c62667bab14b. +// +// Solidity: event LogTokensLocked(uint8 target_chain, uint8 token_chain, bytes32 indexed token, bytes32 indexed sender, bytes32 recipient, uint256 amount) +func (_WormholeBridge *WormholeBridgeFilterer) ParseLogTokensLocked(log types.Log) (*WormholeBridgeLogTokensLocked, error) { + event := new(WormholeBridgeLogTokensLocked) + if err := _WormholeBridge.contract.UnpackLog(event, "LogTokensLocked", log); err != nil { + return nil, err + } + return event, nil +} + +// WormholeBridgeLogTokensUnlockedIterator is returned from FilterLogTokensUnlocked and is used to iterate over the raw logs and unpacked data for LogTokensUnlocked events raised by the WormholeBridge contract. +type WormholeBridgeLogTokensUnlockedIterator struct { + Event *WormholeBridgeLogTokensUnlocked // 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 *WormholeBridgeLogTokensUnlockedIterator) 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(WormholeBridgeLogTokensUnlocked) + 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(WormholeBridgeLogTokensUnlocked) + 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 *WormholeBridgeLogTokensUnlockedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *WormholeBridgeLogTokensUnlockedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// WormholeBridgeLogTokensUnlocked represents a LogTokensUnlocked event raised by the WormholeBridge contract. +type WormholeBridgeLogTokensUnlocked struct { + Token common.Address + Sender [32]byte + Recipient common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterLogTokensUnlocked is a free log retrieval operation binding the contract event 0x762f40dceecda77485ebb6a807ba1ba35b861c5d34fe9bffbb94826a0dc0f101. +// +// Solidity: event LogTokensUnlocked(address indexed token, bytes32 indexed sender, address recipient, uint256 amount) +func (_WormholeBridge *WormholeBridgeFilterer) FilterLogTokensUnlocked(opts *bind.FilterOpts, token []common.Address, sender [][32]byte) (*WormholeBridgeLogTokensUnlockedIterator, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _WormholeBridge.contract.FilterLogs(opts, "LogTokensUnlocked", tokenRule, senderRule) + if err != nil { + return nil, err + } + return &WormholeBridgeLogTokensUnlockedIterator{contract: _WormholeBridge.contract, event: "LogTokensUnlocked", logs: logs, sub: sub}, nil +} + +// WatchLogTokensUnlocked is a free log subscription operation binding the contract event 0x762f40dceecda77485ebb6a807ba1ba35b861c5d34fe9bffbb94826a0dc0f101. +// +// Solidity: event LogTokensUnlocked(address indexed token, bytes32 indexed sender, address recipient, uint256 amount) +func (_WormholeBridge *WormholeBridgeFilterer) WatchLogTokensUnlocked(opts *bind.WatchOpts, sink chan<- *WormholeBridgeLogTokensUnlocked, token []common.Address, sender [][32]byte) (event.Subscription, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _WormholeBridge.contract.WatchLogs(opts, "LogTokensUnlocked", tokenRule, senderRule) + 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(WormholeBridgeLogTokensUnlocked) + if err := _WormholeBridge.contract.UnpackLog(event, "LogTokensUnlocked", 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 +} + +// ParseLogTokensUnlocked is a log parse operation binding the contract event 0x762f40dceecda77485ebb6a807ba1ba35b861c5d34fe9bffbb94826a0dc0f101. +// +// Solidity: event LogTokensUnlocked(address indexed token, bytes32 indexed sender, address recipient, uint256 amount) +func (_WormholeBridge *WormholeBridgeFilterer) ParseLogTokensUnlocked(log types.Log) (*WormholeBridgeLogTokensUnlocked, error) { + event := new(WormholeBridgeLogTokensUnlocked) + if err := _WormholeBridge.contract.UnpackLog(event, "LogTokensUnlocked", log); err != nil { + return nil, err + } + return event, nil +} diff --git a/bridge/pkg/ethereum/watcher.go b/bridge/pkg/ethereum/watcher.go new file mode 100644 index 000000000..8691f1bad --- /dev/null +++ b/bridge/pkg/ethereum/watcher.go @@ -0,0 +1,77 @@ +package ethereum + +import ( + "context" + "fmt" + "github.com/certusone/wormhole/bridge/pkg/common" + "github.com/certusone/wormhole/bridge/pkg/ethereum/abi" + "github.com/certusone/wormhole/bridge/pkg/supervisor" + "github.com/certusone/wormhole/bridge/pkg/vaa" + eth_common "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "sync" +) + +type ( + EthBridgeWatcher struct { + url string + bridge eth_common.Address + + evChan chan *common.ChainLock + } +) + +func NewEthBridgeWatcher(url string, bridge eth_common.Address, events chan *common.ChainLock) *EthBridgeWatcher { + return &EthBridgeWatcher{url: url, bridge: bridge, evChan: events} +} + +func (e *EthBridgeWatcher) Run(ctx context.Context) error { + c, err := ethclient.Dial(e.url) + if err != nil { + return fmt.Errorf("dialing eth client failed: %w", err) + } + + f, err := abi.NewWormholeBridgeFilterer(e.bridge, c) + if err != nil { + return fmt.Errorf("could not create wormhole bridge filter: %w", err) + } + + sink := make(chan *abi.WormholeBridgeLogTokensLocked) + subscription, err := f.WatchLogTokensLocked(nil, sink, nil, nil) + if err != nil { + return fmt.Errorf("failed to subscribe to eth events: %w", err) + } + defer subscription.Unsubscribe() + + wg := sync.WaitGroup{} + wg.Add(1) + + go func() { + defer wg.Done() + + for { + select { + case e := <-subscription.Err(): + err = e + return + case ev := <-sink: + lock := &common.ChainLock{ + SourceAddress: ev.Sender, + TargetAddress: ev.Recipient, + SourceChain: vaa.ChainIDEthereum, + TargetChain: vaa.ChainID(ev.TargetChain), + TokenChain: vaa.ChainID(ev.TokenChain), + TokenAddress: ev.Token, + Amount: ev.Amount, + } + e.evChan <- lock + } + } + + }() + + supervisor.Signal(ctx, supervisor.SignalHealthy) + wg.Wait() + + return err +} diff --git a/docs/protocol.md b/docs/protocol.md index 464e856aa..f4e2626e8 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -184,7 +184,7 @@ uint8 target_chain [32]uint8 target_address uint8 token_chain [32]uint8 token_address -uint64 amount +uint256 amount ``` ### Cross-Chain Transfers diff --git a/ethereum/contracts/Wormhole.sol b/ethereum/contracts/Wormhole.sol index bd9fcc043..5b79a1e66 100644 --- a/ethereum/contracts/Wormhole.sol +++ b/ethereum/contracts/Wormhole.sol @@ -18,7 +18,7 @@ contract Wormhole { address public wrappedAssetMaster; // Chain ID of Ethereum - uint256 CHAIN_ID = 2; + uint8 CHAIN_ID = 2; // Address of the official WETH contract address constant WETHAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; @@ -35,16 +35,18 @@ contract Wormhole { ); event LogTokensLocked( - address indexed token, - bytes32 indexed recipient, - uint8 indexed target_chain, + uint8 target_chain, + uint8 token_chain, + bytes32 indexed token, + bytes32 indexed sender, + bytes32 recipient, uint256 amount ); event LogTokensUnlocked( address indexed token, bytes32 indexed sender, - address indexed recipient, + address recipient, uint256 amount ); @@ -205,12 +207,24 @@ contract Wormhole { ) public { require(amount != 0, "amount must not be 0"); + uint8 asset_chain = CHAIN_ID; + bytes32 asset_address; if (isWrappedAsset[asset]) { WrappedAsset(asset).burn(msg.sender, amount); + asset_chain = WrappedAsset(asset).assetChain(); + asset_address = WrappedAsset(asset).assetAddress(); } else { IERC20(asset).safeTransferFrom(msg.sender, address(this), amount); + asset_address = bytes32(uint256(asset)); } - emit LogTokensLocked(asset, recipient, target_chain, amount); + + // uint8 indexed target_chain, + // bytes32 indexed sender, + // bytes32 indexed recipient, + // uint8 indexed token_chain, + // address indexed token, + // uint256 amount + emit LogTokensLocked(target_chain, asset_chain, asset_address, recipient, bytes32(uint256(msg.sender)), amount); } function lockETH( @@ -223,7 +237,7 @@ contract Wormhole { WETH(WETHAddress).deposit{value : msg.value}(); // Log deposit of WETH - emit LogTokensLocked(WETHAddress, recipient, target_chain, msg.value); + emit LogTokensLocked(target_chain, CHAIN_ID, bytes32(uint256(WETHAddress)), recipient, bytes32(uint256(msg.sender)), msg.value); }