From 1b3848091fa7e4dcd192a9aff270b1fe19d50c18 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Wed, 13 Mar 2024 20:18:49 -0300 Subject: [PATCH 01/13] feat: add ENS' UniversalResolver contract --- contracts/universalresolver/contract.abi | 1 + contracts/universalresolver/contract.go | 886 +++++++++++++++++++++++ contracts/universalresolver/generate.go | 3 + universal_resolver.go | 49 ++ 4 files changed, 939 insertions(+) create mode 100644 contracts/universalresolver/contract.abi create mode 100644 contracts/universalresolver/contract.go create mode 100644 contracts/universalresolver/generate.go create mode 100644 universal_resolver.go diff --git a/contracts/universalresolver/contract.abi b/contracts/universalresolver/contract.abi new file mode 100644 index 0000000..bd006b6 --- /dev/null +++ b/contracts/universalresolver/contract.abi @@ -0,0 +1 @@ +[{"type":"constructor","inputs":[{"name":"_registry","type":"address","internalType":"address"},{"name":"_urls","type":"string[]","internalType":"string[]"}],"stateMutability":"nonpayable"},{"type":"function","name":"_resolveSingle","inputs":[{"name":"name","type":"bytes","internalType":"bytes"},{"name":"data","type":"bytes","internalType":"bytes"},{"name":"gateways","type":"string[]","internalType":"string[]"},{"name":"callbackFunction","type":"bytes4","internalType":"bytes4"},{"name":"metaData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"batchGatewayURLs","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"findResolver","inputs":[{"name":"name","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"address","internalType":"contract Resolver"},{"name":"","type":"bytes32","internalType":"bytes32"},{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"registry","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ENS"}],"stateMutability":"view"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"resolve","inputs":[{"name":"name","type":"bytes","internalType":"bytes"},{"name":"data","type":"bytes","internalType":"bytes"},{"name":"gateways","type":"string[]","internalType":"string[]"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"resolve","inputs":[{"name":"name","type":"bytes","internalType":"bytes"},{"name":"data","type":"bytes[]","internalType":"bytes[]"}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct Result[]","components":[{"name":"success","type":"bool","internalType":"bool"},{"name":"returnData","type":"bytes","internalType":"bytes"}]},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"resolve","inputs":[{"name":"name","type":"bytes","internalType":"bytes"},{"name":"data","type":"bytes[]","internalType":"bytes[]"},{"name":"gateways","type":"string[]","internalType":"string[]"}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct Result[]","components":[{"name":"success","type":"bool","internalType":"bool"},{"name":"returnData","type":"bytes","internalType":"bytes"}]},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"resolve","inputs":[{"name":"name","type":"bytes","internalType":"bytes"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"resolveCallback","inputs":[{"name":"response","type":"bytes","internalType":"bytes"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct Result[]","components":[{"name":"success","type":"bool","internalType":"bool"},{"name":"returnData","type":"bytes","internalType":"bytes"}]},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"resolveSingleCallback","inputs":[{"name":"response","type":"bytes","internalType":"bytes"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"reverse","inputs":[{"name":"reverseName","type":"bytes","internalType":"bytes"},{"name":"gateways","type":"string[]","internalType":"string[]"}],"outputs":[{"name":"","type":"string","internalType":"string"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"reverse","inputs":[{"name":"reverseName","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"string","internalType":"string"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"reverseCallback","inputs":[{"name":"response","type":"bytes","internalType":"bytes"},{"name":"extraData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"string","internalType":"string"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"setGatewayURLs","inputs":[{"name":"_urls","type":"string[]","internalType":"string[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"OffchainLookup","inputs":[{"name":"sender","type":"address","internalType":"address"},{"name":"urls","type":"string[]","internalType":"string[]"},{"name":"callData","type":"bytes","internalType":"bytes"},{"name":"callbackFunction","type":"bytes4","internalType":"bytes4"},{"name":"extraData","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ResolverError","inputs":[{"name":"returnData","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ResolverNotContract","inputs":[]},{"type":"error","name":"ResolverNotFound","inputs":[]},{"type":"error","name":"ResolverWildcardNotSupported","inputs":[]}] \ No newline at end of file diff --git a/contracts/universalresolver/contract.go b/contracts/universalresolver/contract.go new file mode 100644 index 0000000..4ea1863 --- /dev/null +++ b/contracts/universalresolver/contract.go @@ -0,0 +1,886 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package universalresolver + +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 +) + +// Result is an auto generated low-level Go binding around an user-defined struct. +type Result struct { + Success bool + ReturnData []byte +} + +// ContractMetaData contains all meta data concerning the Contract contract. +var ContractMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_registry\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_urls\",\"type\":\"string[]\",\"internalType\":\"string[]\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"_resolveSingle\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"gateways\",\"type\":\"string[]\",\"internalType\":\"string[]\"},{\"name\":\"callbackFunction\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"},{\"name\":\"metaData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"batchGatewayURLs\",\"inputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"findResolver\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractResolver\"},{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registry\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractENS\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"resolve\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"gateways\",\"type\":\"string[]\",\"internalType\":\"string[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"resolve\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structResult[]\",\"components\":[{\"name\":\"success\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"returnData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"resolve\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"gateways\",\"type\":\"string[]\",\"internalType\":\"string[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structResult[]\",\"components\":[{\"name\":\"success\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"returnData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"resolve\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"resolveCallback\",\"inputs\":[{\"name\":\"response\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structResult[]\",\"components\":[{\"name\":\"success\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"returnData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"resolveSingleCallback\",\"inputs\":[{\"name\":\"response\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"reverse\",\"inputs\":[{\"name\":\"reverseName\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"gateways\",\"type\":\"string[]\",\"internalType\":\"string[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"reverse\",\"inputs\":[{\"name\":\"reverseName\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"reverseCallback\",\"inputs\":[{\"name\":\"response\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"string\",\"internalType\":\"string\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setGatewayURLs\",\"inputs\":[{\"name\":\"_urls\",\"type\":\"string[]\",\"internalType\":\"string[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"supportsInterface\",\"inputs\":[{\"name\":\"interfaceId\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"OffchainLookup\",\"inputs\":[{\"name\":\"sender\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"urls\",\"type\":\"string[]\",\"internalType\":\"string[]\"},{\"name\":\"callData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"callbackFunction\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ResolverError\",\"inputs\":[{\"name\":\"returnData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"ResolverNotContract\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ResolverNotFound\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ResolverWildcardNotSupported\",\"inputs\":[]}]", +} + +// ContractABI is the input ABI used to generate the binding from. +// Deprecated: Use ContractMetaData.ABI instead. +var ContractABI = ContractMetaData.ABI + +// Contract is an auto generated Go binding around an Ethereum contract. +type Contract struct { + ContractCaller // Read-only binding to the contract + ContractTransactor // Write-only binding to the contract + ContractFilterer // Log filterer for contract events +} + +// ContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type ContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ContractSession struct { + Contract *Contract // 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 +} + +// ContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ContractCallerSession struct { + Contract *ContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ContractTransactorSession struct { + Contract *ContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type ContractRaw struct { + Contract *Contract // Generic contract binding to access the raw methods on +} + +// ContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ContractCallerRaw struct { + Contract *ContractCaller // Generic read-only contract binding to access the raw methods on +} + +// ContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ContractTransactorRaw struct { + Contract *ContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewContract creates a new instance of Contract, bound to a specific deployed contract. +func NewContract(address common.Address, backend bind.ContractBackend) (*Contract, error) { + contract, err := bindContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil +} + +// NewContractCaller creates a new read-only instance of Contract, bound to a specific deployed contract. +func NewContractCaller(address common.Address, caller bind.ContractCaller) (*ContractCaller, error) { + contract, err := bindContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ContractCaller{contract: contract}, nil +} + +// NewContractTransactor creates a new write-only instance of Contract, bound to a specific deployed contract. +func NewContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ContractTransactor, error) { + contract, err := bindContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ContractTransactor{contract: contract}, nil +} + +// NewContractFilterer creates a new log filterer instance of Contract, bound to a specific deployed contract. +func NewContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ContractFilterer, error) { + contract, err := bindContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ContractFilterer{contract: contract}, nil +} + +// bindContract binds a generic wrapper to an already deployed contract. +func bindContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ContractMetaData.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 (_Contract *ContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Contract.Contract.ContractCaller.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 (_Contract *ContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.Contract.ContractTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Contract *ContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Contract.Contract.ContractTransactor.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 (_Contract *ContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Contract.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 (_Contract *ContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Contract *ContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Contract.Contract.contract.Transact(opts, method, params...) +} + +// ResolveSingle is a free data retrieval call binding the contract method 0x8e25a0f3. +// +// Solidity: function _resolveSingle(bytes name, bytes data, string[] gateways, bytes4 callbackFunction, bytes metaData) view returns(bytes, address) +func (_Contract *ContractCaller) ResolveSingle(opts *bind.CallOpts, name []byte, data []byte, gateways []string, callbackFunction [4]byte, metaData []byte) ([]byte, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "_resolveSingle", name, data, gateways, callbackFunction, metaData) + + if err != nil { + return *new([]byte), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// ResolveSingle is a free data retrieval call binding the contract method 0x8e25a0f3. +// +// Solidity: function _resolveSingle(bytes name, bytes data, string[] gateways, bytes4 callbackFunction, bytes metaData) view returns(bytes, address) +func (_Contract *ContractSession) ResolveSingle(name []byte, data []byte, gateways []string, callbackFunction [4]byte, metaData []byte) ([]byte, common.Address, error) { + return _Contract.Contract.ResolveSingle(&_Contract.CallOpts, name, data, gateways, callbackFunction, metaData) +} + +// ResolveSingle is a free data retrieval call binding the contract method 0x8e25a0f3. +// +// Solidity: function _resolveSingle(bytes name, bytes data, string[] gateways, bytes4 callbackFunction, bytes metaData) view returns(bytes, address) +func (_Contract *ContractCallerSession) ResolveSingle(name []byte, data []byte, gateways []string, callbackFunction [4]byte, metaData []byte) ([]byte, common.Address, error) { + return _Contract.Contract.ResolveSingle(&_Contract.CallOpts, name, data, gateways, callbackFunction, metaData) +} + +// BatchGatewayURLs is a free data retrieval call binding the contract method 0xa6b16419. +// +// Solidity: function batchGatewayURLs(uint256 ) view returns(string) +func (_Contract *ContractCaller) BatchGatewayURLs(opts *bind.CallOpts, arg0 *big.Int) (string, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "batchGatewayURLs", arg0) + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// BatchGatewayURLs is a free data retrieval call binding the contract method 0xa6b16419. +// +// Solidity: function batchGatewayURLs(uint256 ) view returns(string) +func (_Contract *ContractSession) BatchGatewayURLs(arg0 *big.Int) (string, error) { + return _Contract.Contract.BatchGatewayURLs(&_Contract.CallOpts, arg0) +} + +// BatchGatewayURLs is a free data retrieval call binding the contract method 0xa6b16419. +// +// Solidity: function batchGatewayURLs(uint256 ) view returns(string) +func (_Contract *ContractCallerSession) BatchGatewayURLs(arg0 *big.Int) (string, error) { + return _Contract.Contract.BatchGatewayURLs(&_Contract.CallOpts, arg0) +} + +// FindResolver is a free data retrieval call binding the contract method 0xa1cbcbaf. +// +// Solidity: function findResolver(bytes name) view returns(address, bytes32, uint256) +func (_Contract *ContractCaller) FindResolver(opts *bind.CallOpts, name []byte) (common.Address, [32]byte, *big.Int, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "findResolver", name) + + if err != nil { + return *new(common.Address), *new([32]byte), *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + out2 := *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + + return out0, out1, out2, err + +} + +// FindResolver is a free data retrieval call binding the contract method 0xa1cbcbaf. +// +// Solidity: function findResolver(bytes name) view returns(address, bytes32, uint256) +func (_Contract *ContractSession) FindResolver(name []byte) (common.Address, [32]byte, *big.Int, error) { + return _Contract.Contract.FindResolver(&_Contract.CallOpts, name) +} + +// FindResolver is a free data retrieval call binding the contract method 0xa1cbcbaf. +// +// Solidity: function findResolver(bytes name) view returns(address, bytes32, uint256) +func (_Contract *ContractCallerSession) FindResolver(name []byte) (common.Address, [32]byte, *big.Int, error) { + return _Contract.Contract.FindResolver(&_Contract.CallOpts, name) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Contract *ContractCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Contract *ContractSession) Owner() (common.Address, error) { + return _Contract.Contract.Owner(&_Contract.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Contract *ContractCallerSession) Owner() (common.Address, error) { + return _Contract.Contract.Owner(&_Contract.CallOpts) +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_Contract *ContractCaller) Registry(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "registry") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_Contract *ContractSession) Registry() (common.Address, error) { + return _Contract.Contract.Registry(&_Contract.CallOpts) +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_Contract *ContractCallerSession) Registry() (common.Address, error) { + return _Contract.Contract.Registry(&_Contract.CallOpts) +} + +// Resolve is a free data retrieval call binding the contract method 0x0667cfea. +// +// Solidity: function resolve(bytes name, bytes data, string[] gateways) view returns(bytes, address) +func (_Contract *ContractCaller) Resolve(opts *bind.CallOpts, name []byte, data []byte, gateways []string) ([]byte, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolve", name, data, gateways) + + if err != nil { + return *new([]byte), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// Resolve is a free data retrieval call binding the contract method 0x0667cfea. +// +// Solidity: function resolve(bytes name, bytes data, string[] gateways) view returns(bytes, address) +func (_Contract *ContractSession) Resolve(name []byte, data []byte, gateways []string) ([]byte, common.Address, error) { + return _Contract.Contract.Resolve(&_Contract.CallOpts, name, data, gateways) +} + +// Resolve is a free data retrieval call binding the contract method 0x0667cfea. +// +// Solidity: function resolve(bytes name, bytes data, string[] gateways) view returns(bytes, address) +func (_Contract *ContractCallerSession) Resolve(name []byte, data []byte, gateways []string) ([]byte, common.Address, error) { + return _Contract.Contract.Resolve(&_Contract.CallOpts, name, data, gateways) +} + +// Resolve0 is a free data retrieval call binding the contract method 0x206c74c9. +// +// Solidity: function resolve(bytes name, bytes[] data) view returns((bool,bytes)[], address) +func (_Contract *ContractCaller) Resolve0(opts *bind.CallOpts, name []byte, data [][]byte) ([]Result, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolve0", name, data) + + if err != nil { + return *new([]Result), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]Result)).(*[]Result) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// Resolve0 is a free data retrieval call binding the contract method 0x206c74c9. +// +// Solidity: function resolve(bytes name, bytes[] data) view returns((bool,bytes)[], address) +func (_Contract *ContractSession) Resolve0(name []byte, data [][]byte) ([]Result, common.Address, error) { + return _Contract.Contract.Resolve0(&_Contract.CallOpts, name, data) +} + +// Resolve0 is a free data retrieval call binding the contract method 0x206c74c9. +// +// Solidity: function resolve(bytes name, bytes[] data) view returns((bool,bytes)[], address) +func (_Contract *ContractCallerSession) Resolve0(name []byte, data [][]byte) ([]Result, common.Address, error) { + return _Contract.Contract.Resolve0(&_Contract.CallOpts, name, data) +} + +// Resolve1 is a free data retrieval call binding the contract method 0x76286c00. +// +// Solidity: function resolve(bytes name, bytes[] data, string[] gateways) view returns((bool,bytes)[], address) +func (_Contract *ContractCaller) Resolve1(opts *bind.CallOpts, name []byte, data [][]byte, gateways []string) ([]Result, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolve1", name, data, gateways) + + if err != nil { + return *new([]Result), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]Result)).(*[]Result) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// Resolve1 is a free data retrieval call binding the contract method 0x76286c00. +// +// Solidity: function resolve(bytes name, bytes[] data, string[] gateways) view returns((bool,bytes)[], address) +func (_Contract *ContractSession) Resolve1(name []byte, data [][]byte, gateways []string) ([]Result, common.Address, error) { + return _Contract.Contract.Resolve1(&_Contract.CallOpts, name, data, gateways) +} + +// Resolve1 is a free data retrieval call binding the contract method 0x76286c00. +// +// Solidity: function resolve(bytes name, bytes[] data, string[] gateways) view returns((bool,bytes)[], address) +func (_Contract *ContractCallerSession) Resolve1(name []byte, data [][]byte, gateways []string) ([]Result, common.Address, error) { + return _Contract.Contract.Resolve1(&_Contract.CallOpts, name, data, gateways) +} + +// Resolve2 is a free data retrieval call binding the contract method 0x9061b923. +// +// Solidity: function resolve(bytes name, bytes data) view returns(bytes, address) +func (_Contract *ContractCaller) Resolve2(opts *bind.CallOpts, name []byte, data []byte) ([]byte, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolve2", name, data) + + if err != nil { + return *new([]byte), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// Resolve2 is a free data retrieval call binding the contract method 0x9061b923. +// +// Solidity: function resolve(bytes name, bytes data) view returns(bytes, address) +func (_Contract *ContractSession) Resolve2(name []byte, data []byte) ([]byte, common.Address, error) { + return _Contract.Contract.Resolve2(&_Contract.CallOpts, name, data) +} + +// Resolve2 is a free data retrieval call binding the contract method 0x9061b923. +// +// Solidity: function resolve(bytes name, bytes data) view returns(bytes, address) +func (_Contract *ContractCallerSession) Resolve2(name []byte, data []byte) ([]byte, common.Address, error) { + return _Contract.Contract.Resolve2(&_Contract.CallOpts, name, data) +} + +// ResolveCallback is a free data retrieval call binding the contract method 0xb4a85801. +// +// Solidity: function resolveCallback(bytes response, bytes extraData) view returns((bool,bytes)[], address) +func (_Contract *ContractCaller) ResolveCallback(opts *bind.CallOpts, response []byte, extraData []byte) ([]Result, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolveCallback", response, extraData) + + if err != nil { + return *new([]Result), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]Result)).(*[]Result) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// ResolveCallback is a free data retrieval call binding the contract method 0xb4a85801. +// +// Solidity: function resolveCallback(bytes response, bytes extraData) view returns((bool,bytes)[], address) +func (_Contract *ContractSession) ResolveCallback(response []byte, extraData []byte) ([]Result, common.Address, error) { + return _Contract.Contract.ResolveCallback(&_Contract.CallOpts, response, extraData) +} + +// ResolveCallback is a free data retrieval call binding the contract method 0xb4a85801. +// +// Solidity: function resolveCallback(bytes response, bytes extraData) view returns((bool,bytes)[], address) +func (_Contract *ContractCallerSession) ResolveCallback(response []byte, extraData []byte) ([]Result, common.Address, error) { + return _Contract.Contract.ResolveCallback(&_Contract.CallOpts, response, extraData) +} + +// ResolveSingleCallback is a free data retrieval call binding the contract method 0xe0a85412. +// +// Solidity: function resolveSingleCallback(bytes response, bytes extraData) view returns(bytes, address) +func (_Contract *ContractCaller) ResolveSingleCallback(opts *bind.CallOpts, response []byte, extraData []byte) ([]byte, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolveSingleCallback", response, extraData) + + if err != nil { + return *new([]byte), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + + return out0, out1, err + +} + +// ResolveSingleCallback is a free data retrieval call binding the contract method 0xe0a85412. +// +// Solidity: function resolveSingleCallback(bytes response, bytes extraData) view returns(bytes, address) +func (_Contract *ContractSession) ResolveSingleCallback(response []byte, extraData []byte) ([]byte, common.Address, error) { + return _Contract.Contract.ResolveSingleCallback(&_Contract.CallOpts, response, extraData) +} + +// ResolveSingleCallback is a free data retrieval call binding the contract method 0xe0a85412. +// +// Solidity: function resolveSingleCallback(bytes response, bytes extraData) view returns(bytes, address) +func (_Contract *ContractCallerSession) ResolveSingleCallback(response []byte, extraData []byte) ([]byte, common.Address, error) { + return _Contract.Contract.ResolveSingleCallback(&_Contract.CallOpts, response, extraData) +} + +// Reverse is a free data retrieval call binding the contract method 0xb241d0d3. +// +// Solidity: function reverse(bytes reverseName, string[] gateways) view returns(string, address, address, address) +func (_Contract *ContractCaller) Reverse(opts *bind.CallOpts, reverseName []byte, gateways []string) (string, common.Address, common.Address, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "reverse", reverseName, gateways) + + if err != nil { + return *new(string), *new(common.Address), *new(common.Address), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + + return out0, out1, out2, out3, err + +} + +// Reverse is a free data retrieval call binding the contract method 0xb241d0d3. +// +// Solidity: function reverse(bytes reverseName, string[] gateways) view returns(string, address, address, address) +func (_Contract *ContractSession) Reverse(reverseName []byte, gateways []string) (string, common.Address, common.Address, common.Address, error) { + return _Contract.Contract.Reverse(&_Contract.CallOpts, reverseName, gateways) +} + +// Reverse is a free data retrieval call binding the contract method 0xb241d0d3. +// +// Solidity: function reverse(bytes reverseName, string[] gateways) view returns(string, address, address, address) +func (_Contract *ContractCallerSession) Reverse(reverseName []byte, gateways []string) (string, common.Address, common.Address, common.Address, error) { + return _Contract.Contract.Reverse(&_Contract.CallOpts, reverseName, gateways) +} + +// Reverse0 is a free data retrieval call binding the contract method 0xec11c823. +// +// Solidity: function reverse(bytes reverseName) view returns(string, address, address, address) +func (_Contract *ContractCaller) Reverse0(opts *bind.CallOpts, reverseName []byte) (string, common.Address, common.Address, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "reverse0", reverseName) + + if err != nil { + return *new(string), *new(common.Address), *new(common.Address), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + + return out0, out1, out2, out3, err + +} + +// Reverse0 is a free data retrieval call binding the contract method 0xec11c823. +// +// Solidity: function reverse(bytes reverseName) view returns(string, address, address, address) +func (_Contract *ContractSession) Reverse0(reverseName []byte) (string, common.Address, common.Address, common.Address, error) { + return _Contract.Contract.Reverse0(&_Contract.CallOpts, reverseName) +} + +// Reverse0 is a free data retrieval call binding the contract method 0xec11c823. +// +// Solidity: function reverse(bytes reverseName) view returns(string, address, address, address) +func (_Contract *ContractCallerSession) Reverse0(reverseName []byte) (string, common.Address, common.Address, common.Address, error) { + return _Contract.Contract.Reverse0(&_Contract.CallOpts, reverseName) +} + +// ReverseCallback is a free data retrieval call binding the contract method 0x6dc4fb73. +// +// Solidity: function reverseCallback(bytes response, bytes extraData) view returns(string, address, address, address) +func (_Contract *ContractCaller) ReverseCallback(opts *bind.CallOpts, response []byte, extraData []byte) (string, common.Address, common.Address, common.Address, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "reverseCallback", response, extraData) + + if err != nil { + return *new(string), *new(common.Address), *new(common.Address), *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + out1 := *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + out2 := *abi.ConvertType(out[2], new(common.Address)).(*common.Address) + out3 := *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + + return out0, out1, out2, out3, err + +} + +// ReverseCallback is a free data retrieval call binding the contract method 0x6dc4fb73. +// +// Solidity: function reverseCallback(bytes response, bytes extraData) view returns(string, address, address, address) +func (_Contract *ContractSession) ReverseCallback(response []byte, extraData []byte) (string, common.Address, common.Address, common.Address, error) { + return _Contract.Contract.ReverseCallback(&_Contract.CallOpts, response, extraData) +} + +// ReverseCallback is a free data retrieval call binding the contract method 0x6dc4fb73. +// +// Solidity: function reverseCallback(bytes response, bytes extraData) view returns(string, address, address, address) +func (_Contract *ContractCallerSession) ReverseCallback(response []byte, extraData []byte) (string, common.Address, common.Address, common.Address, error) { + return _Contract.Contract.ReverseCallback(&_Contract.CallOpts, response, extraData) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Contract *ContractCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Contract *ContractSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _Contract.Contract.SupportsInterface(&_Contract.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Contract *ContractCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _Contract.Contract.SupportsInterface(&_Contract.CallOpts, interfaceId) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Contract *ContractTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Contract *ContractSession) RenounceOwnership() (*types.Transaction, error) { + return _Contract.Contract.RenounceOwnership(&_Contract.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_Contract *ContractTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _Contract.Contract.RenounceOwnership(&_Contract.TransactOpts) +} + +// SetGatewayURLs is a paid mutator transaction binding the contract method 0x8e5ea8df. +// +// Solidity: function setGatewayURLs(string[] _urls) returns() +func (_Contract *ContractTransactor) SetGatewayURLs(opts *bind.TransactOpts, _urls []string) (*types.Transaction, error) { + return _Contract.contract.Transact(opts, "setGatewayURLs", _urls) +} + +// SetGatewayURLs is a paid mutator transaction binding the contract method 0x8e5ea8df. +// +// Solidity: function setGatewayURLs(string[] _urls) returns() +func (_Contract *ContractSession) SetGatewayURLs(_urls []string) (*types.Transaction, error) { + return _Contract.Contract.SetGatewayURLs(&_Contract.TransactOpts, _urls) +} + +// SetGatewayURLs is a paid mutator transaction binding the contract method 0x8e5ea8df. +// +// Solidity: function setGatewayURLs(string[] _urls) returns() +func (_Contract *ContractTransactorSession) SetGatewayURLs(_urls []string) (*types.Transaction, error) { + return _Contract.Contract.SetGatewayURLs(&_Contract.TransactOpts, _urls) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Contract *ContractTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _Contract.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Contract *ContractSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _Contract.Contract.TransferOwnership(&_Contract.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_Contract *ContractTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _Contract.Contract.TransferOwnership(&_Contract.TransactOpts, newOwner) +} + +// ContractOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the Contract contract. +type ContractOwnershipTransferredIterator struct { + Event *ContractOwnershipTransferred // 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 *ContractOwnershipTransferredIterator) 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(ContractOwnershipTransferred) + 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(ContractOwnershipTransferred) + 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 *ContractOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ContractOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ContractOwnershipTransferred represents a OwnershipTransferred event raised by the Contract contract. +type ContractOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Contract *ContractFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*ContractOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _Contract.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &ContractOwnershipTransferredIterator{contract: _Contract.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Contract *ContractFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ContractOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _Contract.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + 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(ContractOwnershipTransferred) + if err := _Contract.contract.UnpackLog(event, "OwnershipTransferred", 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 +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_Contract *ContractFilterer) ParseOwnershipTransferred(log types.Log) (*ContractOwnershipTransferred, error) { + event := new(ContractOwnershipTransferred) + if err := _Contract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/contracts/universalresolver/generate.go b/contracts/universalresolver/generate.go new file mode 100644 index 0000000..f1e9c51 --- /dev/null +++ b/contracts/universalresolver/generate.go @@ -0,0 +1,3 @@ +package universalresolver + +//go:generate abigen -abi contract.abi -out contract.go -pkg universalresolver -type Contract diff --git a/universal_resolver.go b/universal_resolver.go new file mode 100644 index 0000000..9e2dc6f --- /dev/null +++ b/universal_resolver.go @@ -0,0 +1,49 @@ +package ens + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/wealdtech/go-ens/v3/contracts/universalresolver" +) + +type UniversalResolver struct { + backend bind.ContractBackend + Contract *universalresolver.Contract + ContractAddr common.Address +} + +// NewUniversalResolver obtains the ENS UniversalResolver. +func NewUniversalResolver(backend bind.ContractBackend) (*UniversalResolver, error) { + address, err := UniversalResolverContractAddress(backend, "mainnet") + if err != nil { + return nil, err + } + + return NewUniversalResolverAt(backend, address) +} + +// NewUniversalResolverAt obtains the ENS UniversalResolver at a given address. +func NewUniversalResolverAt(backend bind.ContractBackend, address common.Address) (*UniversalResolver, error) { + contract, err := universalresolver.NewContract(address, backend) + if err != nil { + return nil, err + } + return &UniversalResolver{ + backend: backend, + Contract: contract, + ContractAddr: address, + }, nil +} + +// UniversalResolverContractAddress obtains the address of the UniversalResolver contract for a chain. +func UniversalResolverContractAddress(_ bind.ContractBackend, network string) (common.Address, error) { + switch network { + case "mainnet": + return common.HexToAddress("0xce01f8eee7E479C928F8919abD53E553a36CeF67"), nil + case "sepolia": + return common.HexToAddress("0xc8af999e38273d658be1b921b88a9ddf005769cc"), nil + } + return common.Address{}, fmt.Errorf("universal resolver not found on %s", network) +} From 7c398adb76bd92dded09e5ff85632903c68743cb Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Wed, 13 Mar 2024 20:19:23 -0300 Subject: [PATCH 02/13] feat: add DNS encode utility function --- namehash.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/namehash.go b/namehash.go index dd5e8ff..fe07d16 100644 --- a/namehash.go +++ b/namehash.go @@ -102,3 +102,32 @@ func nameHashPart(currentHash [32]byte, name string) ([32]byte, error) { return hash, nil } + +func DNSEncode(name string) ([]byte, error) { + name = strings.Trim(name, ".") + + encoded := make([]byte, len(name)+2) + offset := 0 + + // split name into labels + labels := strings.Split(name, ".") + + for _, label := range labels { + l := len(label) + + // length must be less than 64 + if l > 63 { + return nil, errors.New("label too long") + } + + // write length + encoded[offset] = byte(l) + offset++ + + // write label + copy(encoded[offset:offset+l], []byte(label)) + offset += l + } + + return encoded, nil +} From c37ae63fe67fbe7a95b5e12e86b22a33dbcde5cf Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Wed, 13 Mar 2024 20:19:55 -0300 Subject: [PATCH 03/13] refactor: resolveHash relying on UniversalResolver --- resolver.go | 32 ++++++++++++++++++++++++++++---- resolver_test.go | 13 +++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/resolver.go b/resolver.go index d6b03c3..9839d8c 100644 --- a/resolver.go +++ b/resolver.go @@ -214,21 +214,45 @@ func resolveName(backend bind.ContractBackend, input string) (common.Address, er } func resolveHash(backend bind.ContractBackend, domain string) (common.Address, error) { - resolver, err := NewResolver(backend, domain) + r, err := NewUniversalResolver(backend) if err != nil { return UnknownAddress, err } // Resolve the domain. - address, err := resolver.Address() + hash, err := NameHash(domain) if err != nil { return UnknownAddress, err } - if bytes.Equal(address.Bytes(), UnknownAddress.Bytes()) { + abi, err := resolver.ContractMetaData.GetAbi() + if err != nil { + return UnknownAddress, err + } + input, err := abi.Pack("addr", hash) + if err != nil { + return UnknownAddress, err + } + dnsdomain, err := DNSEncode(domain) + if err != nil { + return UnknownAddress, err + } + address, r1, err := r.Contract.Resolve( + nil, + dnsdomain, + input, + []string{}, + ) + if err != nil { + return UnknownAddress, errors.New("unregistered name") + } + if r1 == UnknownAddress { + return UnknownAddress, errors.New("no resolver") + } + if bytes.Equal(address, zeroHash) { return UnknownAddress, errors.New("no address") } - return address, nil + return common.BytesToAddress(address), nil } // SetText sets the text associated with a name. diff --git a/resolver_test.go b/resolver_test.go index 3da9009..53a0dde 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -101,3 +101,16 @@ func TestReverseResolveTestEnsTest(t *testing.T) { require.Nil(t, err, "Error resolving address") assert.Equal(t, expected, actual, "Did not receive expected result") } + +func TestSubdomainResolveAddress(t *testing.T) { + expected := "51050ec063d393217b436747617ad1c2285aeeee" + actual, err := Resolve(client, "331.moo.nft-owner.eth") + require.Nil(t, err, "Error resolving address") + assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") +} + +func TestInvalidSubdomainResolveAddress(t *testing.T) { + _, err := Resolve(client, "a.b.c.d.e.eth") + require.NotNil(t, err, "Error resolving address") + assert.Equal(t, "unregistered name", err.Error(), "Unexpected error") +} From 80fea4fb8686ed3def66c9f63fb109e6523b522f Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Thu, 14 Mar 2024 10:40:37 -0300 Subject: [PATCH 04/13] refactor: handle offchain lookup revert error --- resolver.go | 8 ++++++++ resolver_test.go | 6 ++++++ tokenid_test.go | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/resolver.go b/resolver.go index 9839d8c..819c6f7 100644 --- a/resolver.go +++ b/resolver.go @@ -18,6 +18,7 @@ import ( "bytes" "compress/zlib" "errors" + "fmt" "io" "math/big" "strings" @@ -25,10 +26,12 @@ import ( "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/rpc" "github.com/wealdtech/go-ens/v3/contracts/resolver" ) var zeroHash = make([]byte, 32) +var offchainLookupSignature = "0x556f1830" // UnknownAddress is the address to which unknown entries resolve. var UnknownAddress = common.HexToAddress("00") @@ -243,6 +246,11 @@ func resolveHash(backend bind.ContractBackend, domain string) (common.Address, e []string{}, ) if err != nil { + var jsonErr = err.(rpc.DataError) + errData := fmt.Sprintf("%v", jsonErr.ErrorData()) + if errData[:10] == offchainLookupSignature { + return UnknownAddress, errors.New("external resolver") + } return UnknownAddress, errors.New("unregistered name") } if r1 == UnknownAddress { diff --git a/resolver_test.go b/resolver_test.go index 53a0dde..028c011 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -109,6 +109,12 @@ func TestSubdomainResolveAddress(t *testing.T) { assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") } +func TestExternalResolverAddress(t *testing.T) { + _, err := Resolve(client, "jesse.cb.id") + require.NotNil(t, err, "Error resolving address") + assert.Equal(t, "external resolver", err.Error(), "Unexpected error") +} + func TestInvalidSubdomainResolveAddress(t *testing.T) { _, err := Resolve(client, "a.b.c.d.e.eth") require.NotNil(t, err, "Error resolving address") diff --git a/tokenid_test.go b/tokenid_test.go index eb08d3d..b394606 100644 --- a/tokenid_test.go +++ b/tokenid_test.go @@ -22,7 +22,7 @@ func TestDeriveTokenId(t *testing.T) { { name: "Invalid ENS domain", expected: "", - input: "foo.bar", + input: "invalid.domain", err: "unregistered name", }, { From 230bcec1b05ac83d177f6cf28f5708561f8e6ac8 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Mon, 18 Mar 2024 19:25:56 -0300 Subject: [PATCH 05/13] feat(WIP): offchain resolution through coinbase --- ccip.go | 77 ++++++++ contracts/offchainresolver/contract.abi | 50 +++++ contracts/offchainresolver/contract.go | 243 ++++++++++++++++++++++++ contracts/offchainresolver/generate.go | 3 + resolver.go | 116 +++++++---- resolver_test.go | 8 +- 6 files changed, 454 insertions(+), 43 deletions(-) create mode 100644 ccip.go create mode 100644 contracts/offchainresolver/contract.abi create mode 100644 contracts/offchainresolver/contract.go create mode 100644 contracts/offchainresolver/generate.go diff --git a/ccip.go b/ccip.go new file mode 100644 index 0000000..e76eb6b --- /dev/null +++ b/ccip.go @@ -0,0 +1,77 @@ +package ens + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +func ccipFetch(sender common.Address, data string, urls []string) (result string, err error) { + for _, url := range urls { + method := "POST" + if strings.Contains(url, "{data}") { + method = "GET" + } + + body := []byte{} + if method == "POST" { + body, err = json.Marshal(map[string]interface{}{ + "data": data, + "sender": sender, + }) + if err != nil { + return "", err + } + } + + url = strings.ReplaceAll(url, "{sender}", sender.String()) + url = strings.ReplaceAll(url, "{data}", data) + req, err := http.NewRequest(method, url, bytes.NewBuffer(body)) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", err + } + + defer resp.Body.Close() + + var responseData map[string]interface{} + var result string + if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") { + err = json.NewDecoder(resp.Body).Decode(&responseData) + if err != nil { + continue + } + var ok bool + result, ok = responseData["data"].(string) + if !ok { + err = errors.New("invalid response from gateway") + continue + } + } else { + responseBytes, err := io.ReadAll(resp.Body) + if err != nil { + continue + } + result = string(responseBytes) + } + + if resp.StatusCode != http.StatusOK { + continue + } + + return result, nil + } + + return "", err +} diff --git a/contracts/offchainresolver/contract.abi b/contracts/offchainresolver/contract.abi new file mode 100644 index 0000000..74f807c --- /dev/null +++ b/contracts/offchainresolver/contract.abi @@ -0,0 +1,50 @@ +[ + { + "type": "function", + "name": "resolve", + "inputs": [ + { + "name": "name", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes", + "internalType": "bytes" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "resolveWithProof", + "inputs": [ + { + "name": "response", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "extraData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes", + "internalType": "bytes" + } + ], + "stateMutability": "view" + } +] diff --git a/contracts/offchainresolver/contract.go b/contracts/offchainresolver/contract.go new file mode 100644 index 0000000..2782ad2 --- /dev/null +++ b/contracts/offchainresolver/contract.go @@ -0,0 +1,243 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package offchainresolver + +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 +) + +// ContractMetaData contains all meta data concerning the Contract contract. +var ContractMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"resolve\",\"inputs\":[{\"name\":\"name\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"resolveWithProof\",\"inputs\":[{\"name\":\"response\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"extraData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"}]", +} + +// ContractABI is the input ABI used to generate the binding from. +// Deprecated: Use ContractMetaData.ABI instead. +var ContractABI = ContractMetaData.ABI + +// Contract is an auto generated Go binding around an Ethereum contract. +type Contract struct { + ContractCaller // Read-only binding to the contract + ContractTransactor // Write-only binding to the contract + ContractFilterer // Log filterer for contract events +} + +// ContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type ContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ContractSession struct { + Contract *Contract // 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 +} + +// ContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ContractCallerSession struct { + Contract *ContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ContractTransactorSession struct { + Contract *ContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type ContractRaw struct { + Contract *Contract // Generic contract binding to access the raw methods on +} + +// ContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ContractCallerRaw struct { + Contract *ContractCaller // Generic read-only contract binding to access the raw methods on +} + +// ContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ContractTransactorRaw struct { + Contract *ContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewContract creates a new instance of Contract, bound to a specific deployed contract. +func NewContract(address common.Address, backend bind.ContractBackend) (*Contract, error) { + contract, err := bindContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil +} + +// NewContractCaller creates a new read-only instance of Contract, bound to a specific deployed contract. +func NewContractCaller(address common.Address, caller bind.ContractCaller) (*ContractCaller, error) { + contract, err := bindContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ContractCaller{contract: contract}, nil +} + +// NewContractTransactor creates a new write-only instance of Contract, bound to a specific deployed contract. +func NewContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ContractTransactor, error) { + contract, err := bindContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ContractTransactor{contract: contract}, nil +} + +// NewContractFilterer creates a new log filterer instance of Contract, bound to a specific deployed contract. +func NewContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ContractFilterer, error) { + contract, err := bindContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ContractFilterer{contract: contract}, nil +} + +// bindContract binds a generic wrapper to an already deployed contract. +func bindContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ContractMetaData.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 (_Contract *ContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Contract.Contract.ContractCaller.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 (_Contract *ContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.Contract.ContractTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Contract *ContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Contract.Contract.ContractTransactor.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 (_Contract *ContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Contract.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 (_Contract *ContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Contract.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Contract *ContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Contract.Contract.contract.Transact(opts, method, params...) +} + +// Resolve is a free data retrieval call binding the contract method 0x9061b923. +// +// Solidity: function resolve(bytes name, bytes data) view returns(bytes) +func (_Contract *ContractCaller) Resolve(opts *bind.CallOpts, name []byte, data []byte) ([]byte, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolve", name, data) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +// Resolve is a free data retrieval call binding the contract method 0x9061b923. +// +// Solidity: function resolve(bytes name, bytes data) view returns(bytes) +func (_Contract *ContractSession) Resolve(name []byte, data []byte) ([]byte, error) { + return _Contract.Contract.Resolve(&_Contract.CallOpts, name, data) +} + +// Resolve is a free data retrieval call binding the contract method 0x9061b923. +// +// Solidity: function resolve(bytes name, bytes data) view returns(bytes) +func (_Contract *ContractCallerSession) Resolve(name []byte, data []byte) ([]byte, error) { + return _Contract.Contract.Resolve(&_Contract.CallOpts, name, data) +} + +// ResolveWithProof is a free data retrieval call binding the contract method 0xf4d4d2f8. +// +// Solidity: function resolveWithProof(bytes response, bytes extraData) view returns(bytes) +func (_Contract *ContractCaller) ResolveWithProof(opts *bind.CallOpts, response []byte, extraData []byte) ([]byte, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "resolveWithProof", response, extraData) + + if err != nil { + return *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return out0, err + +} + +// ResolveWithProof is a free data retrieval call binding the contract method 0xf4d4d2f8. +// +// Solidity: function resolveWithProof(bytes response, bytes extraData) view returns(bytes) +func (_Contract *ContractSession) ResolveWithProof(response []byte, extraData []byte) ([]byte, error) { + return _Contract.Contract.ResolveWithProof(&_Contract.CallOpts, response, extraData) +} + +// ResolveWithProof is a free data retrieval call binding the contract method 0xf4d4d2f8. +// +// Solidity: function resolveWithProof(bytes response, bytes extraData) view returns(bytes) +func (_Contract *ContractCallerSession) ResolveWithProof(response []byte, extraData []byte) ([]byte, error) { + return _Contract.Contract.ResolveWithProof(&_Contract.CallOpts, response, extraData) +} diff --git a/contracts/offchainresolver/generate.go b/contracts/offchainresolver/generate.go new file mode 100644 index 0000000..15984b1 --- /dev/null +++ b/contracts/offchainresolver/generate.go @@ -0,0 +1,3 @@ +package offchainresolver + +//go:generate abigen -abi contract.abi -out contract.go -pkg offchainresolver -type Contract diff --git a/resolver.go b/resolver.go index 819c6f7..7c69eea 100644 --- a/resolver.go +++ b/resolver.go @@ -17,6 +17,7 @@ package ens import ( "bytes" "compress/zlib" + "encoding/hex" "errors" "fmt" "io" @@ -27,7 +28,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + "github.com/wealdtech/go-ens/v3/contracts/offchainresolver" "github.com/wealdtech/go-ens/v3/contracts/resolver" + "github.com/wealdtech/go-ens/v3/contracts/universalresolver" ) var zeroHash = make([]byte, 32) @@ -75,17 +78,17 @@ func NewResolverAt(backend bind.ContractBackend, domain string, address common.A } // Ensure this really is a resolver contract. - nameHash, err := NameHash("test.eth") - if err != nil { - return nil, err - } - _, err = contract.Addr(nil, nameHash) - if err != nil { - if err.Error() == "no contract code at given address" { - return nil, errors.New("no resolver") - } - return nil, err - } + // nameHash, err := NameHash("test.eth") + // if err != nil { + // return nil, err + // } + // _, err = contract.Addr(nil, nameHash) + // if err != nil { + // if err.Error() == "no contract code at given address" { + // return nil, errors.New("no resolver") + // } + // return nil, err + // } return &Resolver{ Contract: contract, @@ -217,50 +220,83 @@ func resolveName(backend bind.ContractBackend, input string) (common.Address, er } func resolveHash(backend bind.ContractBackend, domain string) (common.Address, error) { - r, err := NewUniversalResolver(backend) + ur, err := NewUniversalResolver(backend) if err != nil { return UnknownAddress, err } - // Resolve the domain. hash, err := NameHash(domain) if err != nil { return UnknownAddress, err } - abi, err := resolver.ContractMetaData.GetAbi() - if err != nil { - return UnknownAddress, err - } - input, err := abi.Pack("addr", hash) - if err != nil { - return UnknownAddress, err - } + dnsdomain, err := DNSEncode(domain) if err != nil { return UnknownAddress, err } - address, r1, err := r.Contract.Resolve( - nil, - dnsdomain, - input, - []string{}, - ) - if err != nil { - var jsonErr = err.(rpc.DataError) - errData := fmt.Sprintf("%v", jsonErr.ErrorData()) - if errData[:10] == offchainLookupSignature { - return UnknownAddress, errors.New("external resolver") - } - return UnknownAddress, errors.New("unregistered name") - } - if r1 == UnknownAddress { - return UnknownAddress, errors.New("no resolver") + rAddr, node, _, err := ur.Contract.FindResolver(nil, dnsdomain) + if len(node) == 0 { } - if bytes.Equal(address, zeroHash) { - return UnknownAddress, errors.New("no address") + r, err := NewResolverAt(backend, domain, rAddr) + addr, err := r.Contract.Addr(nil, hash) + + if err == nil { + return addr, nil } - return common.BytesToAddress(address), nil + var jsonErr = err.(rpc.DataError) + errData := jsonErr.ErrorData().(string) + + if errData[:10] == offchainLookupSignature { + hexBytes, err := hex.DecodeString(errData[2:]) + if err != nil { + return UnknownAddress, err + } + + uAbi, err := universalresolver.ContractMetaData.GetAbi() + if err != nil { + return UnknownAddress, err + } + var sig [4]byte + copy(sig[:], hexBytes[:4]) + abiErr, err := uAbi.ErrorByID(sig) + if err != nil { + return UnknownAddress, err + } + errArgs, err := abiErr.Inputs.Unpack(hexBytes[4:]) + if err != nil { + return UnknownAddress, err + } + + sender := errArgs[0].(common.Address) + urls := errArgs[1].([]string) + calldata := common.Bytes2Hex(errArgs[2].([]byte)) + calldataHex := fmt.Sprintf("0x%s", calldata) + // callback := errArgs[3].([4]byte) + extraData := errArgs[4].([]byte) + + resp, err := ccipFetch(sender, calldataHex, urls) + if err != nil || len(resp) == 0 { + } + + offR, err := offchainresolver.NewContract(rAddr, backend) + if err != nil { + return common.Address{}, errors.New("unregistered name") + } + + z, err := offR.ResolveWithProof(nil, common.Hex2Bytes(resp[2:]), extraData) + if len(z) == 0 { + } + + return common.BytesToAddress(z), nil + } + return UnknownAddress, errors.New("unregistered name") + // if r1 == UnknownAddress { + // return UnknownAddress, errors.New("no resolver") + // } + // if bytes.Equal(address, zeroHash) { + // return UnknownAddress, errors.New("no address") + // } } // SetText sets the text associated with a name. diff --git a/resolver_test.go b/resolver_test.go index 028c011..a03c981 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -110,9 +110,11 @@ func TestSubdomainResolveAddress(t *testing.T) { } func TestExternalResolverAddress(t *testing.T) { - _, err := Resolve(client, "jesse.cb.id") - require.NotNil(t, err, "Error resolving address") - assert.Equal(t, "external resolver", err.Error(), "Unexpected error") + expected := "849151d7d0bf1f34b70d5cad5149d28cc2308bf1" + actual, err := Resolve(client, "jesse.cb.id") + require.Nil(t, err, "Error resolving address") + x := hex.EncodeToString(actual[:]) + assert.Equal(t, expected, x, "Did not receive expected result") } func TestInvalidSubdomainResolveAddress(t *testing.T) { From 8e3394d7a4f3f72d72768d210134a34596e790b7 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Mon, 18 Mar 2024 19:55:19 -0300 Subject: [PATCH 06/13] refactor: decouple ccip read from resolver --- ccip.go | 61 ++++++++++++++++++++++++++++++++++++++++- resolver.go | 78 +++++++++-------------------------------------------- 2 files changed, 72 insertions(+), 67 deletions(-) diff --git a/ccip.go b/ccip.go index e76eb6b..4130935 100644 --- a/ccip.go +++ b/ccip.go @@ -2,16 +2,75 @@ package ens import ( "bytes" + "context" + "encoding/hex" "encoding/json" "errors" + "fmt" "io" "net/http" "strings" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/wealdtech/go-ens/v3/contracts/offchainresolver" + "github.com/wealdtech/go-ens/v3/contracts/universalresolver" ) -func ccipFetch(sender common.Address, data string, urls []string) (result string, err error) { +func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData string) ([]byte, error) { + hexBytes, err := hex.DecodeString(revertData[2:]) + if err != nil { + return nil, err + } + + uAbi, err := universalresolver.ContractMetaData.GetAbi() + if err != nil { + return nil, err + } + var sig [4]byte + copy(sig[:], hexBytes[:4]) + abiErr, err := uAbi.ErrorByID(sig) + if err != nil { + return nil, err + } + errArgs, err := abiErr.Inputs.Unpack(hexBytes[4:]) + if err != nil { + return nil, err + } + + sender := errArgs[0].(common.Address) + urls := errArgs[1].([]string) + calldata := common.Bytes2Hex(errArgs[2].([]byte)) + calldataHex := fmt.Sprintf("0x%s", calldata) + callback := errArgs[3].([4]byte) + extraData := errArgs[4].([]byte) + + resp, err := CCIPFetch(sender, calldataHex, urls) + if err != nil || len(resp) == 0 { + return nil, errors.New("unregistered name") + } + + oAbi, err := offchainresolver.ContractMetaData.GetAbi() + if err != nil { + return nil, errors.New("no address") + } + m, err := oAbi.MethodById(callback[:]) + if err != nil { + return nil, errors.New("no address") + } + args, err := m.Inputs.Pack(common.Hex2Bytes(resp[2:]), extraData) + if err != nil { + return nil, errors.New("no address") + } + + return backend.CallContract(context.Background(), ethereum.CallMsg{ + To: &rAddr, + Data: append(callback[:], args...), + }, nil) +} + +func CCIPFetch(sender common.Address, data string, urls []string) (result string, err error) { for _, url := range urls { method := "POST" if strings.Contains(url, "{data}") { diff --git a/resolver.go b/resolver.go index 7c69eea..0ae24bd 100644 --- a/resolver.go +++ b/resolver.go @@ -17,9 +17,7 @@ package ens import ( "bytes" "compress/zlib" - "encoding/hex" "errors" - "fmt" "io" "math/big" "strings" @@ -28,9 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/wealdtech/go-ens/v3/contracts/offchainresolver" "github.com/wealdtech/go-ens/v3/contracts/resolver" - "github.com/wealdtech/go-ens/v3/contracts/universalresolver" ) var zeroHash = make([]byte, 32) @@ -77,19 +73,6 @@ func NewResolverAt(backend bind.ContractBackend, domain string, address common.A return nil, err } - // Ensure this really is a resolver contract. - // nameHash, err := NameHash("test.eth") - // if err != nil { - // return nil, err - // } - // _, err = contract.Addr(nil, nameHash) - // if err != nil { - // if err.Error() == "no contract code at given address" { - // return nil, errors.New("no resolver") - // } - // return nil, err - // } - return &Resolver{ Contract: contract, ContractAddr: address, @@ -234,69 +217,32 @@ func resolveHash(backend bind.ContractBackend, domain string) (common.Address, e if err != nil { return UnknownAddress, err } - rAddr, node, _, err := ur.Contract.FindResolver(nil, dnsdomain) - if len(node) == 0 { + rAddr, _, _, err := ur.Contract.FindResolver(nil, dnsdomain) + if err != nil || rAddr == common.Address(zeroHash) { + return UnknownAddress, errors.New("unregistered name") } r, err := NewResolverAt(backend, domain, rAddr) + if err != nil { + return UnknownAddress, errors.New("no resolver") + } addr, err := r.Contract.Addr(nil, hash) if err == nil { return addr, nil } + // CCIP Read check var jsonErr = err.(rpc.DataError) errData := jsonErr.ErrorData().(string) - - if errData[:10] == offchainLookupSignature { - hexBytes, err := hex.DecodeString(errData[2:]) - if err != nil { - return UnknownAddress, err - } - - uAbi, err := universalresolver.ContractMetaData.GetAbi() - if err != nil { - return UnknownAddress, err - } - var sig [4]byte - copy(sig[:], hexBytes[:4]) - abiErr, err := uAbi.ErrorByID(sig) - if err != nil { - return UnknownAddress, err - } - errArgs, err := abiErr.Inputs.Unpack(hexBytes[4:]) - if err != nil { - return UnknownAddress, err - } - - sender := errArgs[0].(common.Address) - urls := errArgs[1].([]string) - calldata := common.Bytes2Hex(errArgs[2].([]byte)) - calldataHex := fmt.Sprintf("0x%s", calldata) - // callback := errArgs[3].([4]byte) - extraData := errArgs[4].([]byte) - - resp, err := ccipFetch(sender, calldataHex, urls) - if err != nil || len(resp) == 0 { - } - - offR, err := offchainresolver.NewContract(rAddr, backend) - if err != nil { - return common.Address{}, errors.New("unregistered name") - } - - z, err := offR.ResolveWithProof(nil, common.Hex2Bytes(resp[2:]), extraData) - if len(z) == 0 { + if len(errData) >= 10 && errData[:10] == offchainLookupSignature { + rawAddr, err := CCIPRead(backend, rAddr, errData) + if err != nil || bytes.Equal(rawAddr, zeroHash) { + return UnknownAddress, errors.New("no address") } - return common.BytesToAddress(z), nil + return common.BytesToAddress(rawAddr), nil } return UnknownAddress, errors.New("unregistered name") - // if r1 == UnknownAddress { - // return UnknownAddress, errors.New("no resolver") - // } - // if bytes.Equal(address, zeroHash) { - // return UnknownAddress, errors.New("no address") - // } } // SetText sets the text associated with a name. From 59e01bec4f65965324448481c0fd254d5773b1a8 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Tue, 19 Mar 2024 08:49:26 -0300 Subject: [PATCH 07/13] fix: call addr with args on onchain resolve --- resolver.go | 32 ++++++++++++++++++++++++++------ resolver_test.go | 5 ++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/resolver.go b/resolver.go index 0ae24bd..c01c8c0 100644 --- a/resolver.go +++ b/resolver.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + "github.com/wealdtech/go-ens/v3/contracts/offchainresolver" "github.com/wealdtech/go-ens/v3/contracts/resolver" ) @@ -208,16 +209,16 @@ func resolveHash(backend bind.ContractBackend, domain string) (common.Address, e return UnknownAddress, err } - hash, err := NameHash(domain) + nhash, err := NameHash(domain) if err != nil { return UnknownAddress, err } - dnsdomain, err := DNSEncode(domain) + lhash, err := DNSEncode(domain) if err != nil { return UnknownAddress, err } - rAddr, _, _, err := ur.Contract.FindResolver(nil, dnsdomain) + rAddr, _, _, err := ur.Contract.FindResolver(nil, lhash) if err != nil || rAddr == common.Address(zeroHash) { return UnknownAddress, errors.New("unregistered name") } @@ -225,21 +226,40 @@ func resolveHash(backend bind.ContractBackend, domain string) (common.Address, e if err != nil { return UnknownAddress, errors.New("no resolver") } - addr, err := r.Contract.Addr(nil, hash) + addr, err := r.Contract.Addr(nil, nhash) if err == nil { + if addr == common.Address(zeroHash) { + return UnknownAddress, errors.New("unregistered name") + } return addr, nil } // CCIP Read check var jsonErr = err.(rpc.DataError) - errData := jsonErr.ErrorData().(string) + errData, ok := jsonErr.ErrorData().(string) + if !ok || len(errData) < 10 { + offR, err := offchainresolver.NewContract(rAddr, backend) + if err != nil { + return UnknownAddress, errors.New("no address") + } + rAbi, _ := resolver.ContractMetaData.GetAbi() + m := rAbi.Methods["addr"] + args, _ := m.Inputs.Pack(nhash) + rawAddr, err := offR.Resolve(nil, lhash, append(m.ID, args...)) + if err == nil { + // resolved on-chain + return common.BytesToAddress(rawAddr), nil + } + jsonErr = err.(rpc.DataError) + errData = jsonErr.ErrorData().(string) + } + if len(errData) >= 10 && errData[:10] == offchainLookupSignature { rawAddr, err := CCIPRead(backend, rAddr, errData) if err != nil || bytes.Equal(rawAddr, zeroHash) { return UnknownAddress, errors.New("no address") } - return common.BytesToAddress(rawAddr), nil } return UnknownAddress, errors.New("unregistered name") diff --git a/resolver_test.go b/resolver_test.go index a03c981..790c32b 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -51,7 +51,7 @@ func TestResolveNotPresent(t *testing.T) { func TestResolveBadResolver(t *testing.T) { _, err := Resolve(client, "resolvestozero.eth") require.NotNil(t, err, "Resolved name with a bad resolver") - assert.Equal(t, "no address", err.Error(), "Unexpected error") + assert.Equal(t, "unregistered name", err.Error(), "Unexpected error") } func TestResolveTestEnsTest(t *testing.T) { @@ -113,8 +113,7 @@ func TestExternalResolverAddress(t *testing.T) { expected := "849151d7d0bf1f34b70d5cad5149d28cc2308bf1" actual, err := Resolve(client, "jesse.cb.id") require.Nil(t, err, "Error resolving address") - x := hex.EncodeToString(actual[:]) - assert.Equal(t, expected, x, "Did not receive expected result") + assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") } func TestInvalidSubdomainResolveAddress(t *testing.T) { From d1408e38e81d4c816ba6ea308868a70044a9f8d5 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Thu, 21 Mar 2024 15:53:12 -0300 Subject: [PATCH 08/13] test: onchain text reading --- ccip.go | 14 +++++++ resolver.go | 103 +++++++++++++++++++++++++---------------------- resolver_test.go | 20 ++++++++- 3 files changed, 88 insertions(+), 49 deletions(-) diff --git a/ccip.go b/ccip.go index 4130935..26621ef 100644 --- a/ccip.go +++ b/ccip.go @@ -14,10 +14,20 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" "github.com/wealdtech/go-ens/v3/contracts/offchainresolver" "github.com/wealdtech/go-ens/v3/contracts/universalresolver" ) +func getCcipReadError(err error) (bool, string) { + var jsonErr = err.(rpc.DataError) + errData, ok := jsonErr.ErrorData().(string) + if !ok { + return false, "" + } + return (len(errData) >= 10 && errData[:10] == offchainLookupSignature), errData +} + func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData string) ([]byte, error) { hexBytes, err := hex.DecodeString(revertData[2:]) if err != nil { @@ -28,6 +38,8 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str if err != nil { return nil, err } + + // Extracting error details from the revert data var sig [4]byte copy(sig[:], hexBytes[:4]) abiErr, err := uAbi.ErrorByID(sig) @@ -46,6 +58,7 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str callback := errArgs[3].([4]byte) extraData := errArgs[4].([]byte) + // Fetching data from external source using CCIP resp, err := CCIPFetch(sender, calldataHex, urls) if err != nil || len(resp) == 0 { return nil, errors.New("unregistered name") @@ -64,6 +77,7 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str return nil, errors.New("no address") } + // Calling the offchain resolver callback to validate the external source response return backend.CallContract(context.Background(), ethereum.CallMsg{ To: &rAddr, Data: append(callback[:], args...), diff --git a/resolver.go b/resolver.go index c01c8c0..4cb2ad4 100644 --- a/resolver.go +++ b/resolver.go @@ -25,7 +25,6 @@ import ( "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/rpc" "github.com/wealdtech/go-ens/v3/contracts/offchainresolver" "github.com/wealdtech/go-ens/v3/contracts/resolver" ) @@ -41,30 +40,25 @@ type Resolver struct { Contract *resolver.Contract ContractAddr common.Address domain string + backend bind.ContractBackend + offResolver *offchainresolver.Contract } // NewResolver obtains an ENS resolver for a given domain. func NewResolver(backend bind.ContractBackend, domain string) (*Resolver, error) { - registry, err := NewRegistry(backend) + ur, err := NewUniversalResolver(backend) if err != nil { return nil, err } - - // Ensure the name is registered. - ownerAddress, err := registry.Owner(domain) + lhash, err := DNSEncode(domain) if err != nil { return nil, err } - if bytes.Equal(ownerAddress.Bytes(), UnknownAddress.Bytes()) { - return nil, errors.New("unregistered name") - } - - // Obtain the resolver address for this domain. - resolver, err := registry.ResolverAddress(domain) - if err != nil { + rAddr, _, _, err := ur.Contract.FindResolver(nil, lhash) + if err != nil || rAddr == common.Address(zeroHash) { return nil, err } - return NewResolverAt(backend, domain, resolver) + return NewResolverAt(backend, domain, rAddr) } // NewResolverAt obtains an ENS resolver at a given address. @@ -74,10 +68,17 @@ func NewResolverAt(backend bind.ContractBackend, domain string, address common.A return nil, err } + offR, err := offchainresolver.NewContract(address, backend) + if err != nil { + return nil, err + } + return &Resolver{ Contract: contract, ContractAddr: address, domain: domain, + backend: backend, + offResolver: offR, }, nil } @@ -204,30 +205,20 @@ func resolveName(backend bind.ContractBackend, input string) (common.Address, er } func resolveHash(backend bind.ContractBackend, domain string) (common.Address, error) { - ur, err := NewUniversalResolver(backend) + r, err := NewResolver(backend, domain) if err != nil { - return UnknownAddress, err + return UnknownAddress, errors.New("no resolver") } - - nhash, err := NameHash(domain) + node, err := NameHash(domain) if err != nil { return UnknownAddress, err } - lhash, err := DNSEncode(domain) if err != nil { - return UnknownAddress, err + return common.Address{}, err } - rAddr, _, _, err := ur.Contract.FindResolver(nil, lhash) - if err != nil || rAddr == common.Address(zeroHash) { - return UnknownAddress, errors.New("unregistered name") - } - r, err := NewResolverAt(backend, domain, rAddr) - if err != nil { - return UnknownAddress, errors.New("no resolver") - } - addr, err := r.Contract.Addr(nil, nhash) + addr, err := r.Contract.Addr(nil, node) if err == nil { if addr == common.Address(zeroHash) { return UnknownAddress, errors.New("unregistered name") @@ -236,33 +227,28 @@ func resolveHash(backend bind.ContractBackend, domain string) (common.Address, e } // CCIP Read check - var jsonErr = err.(rpc.DataError) - errData, ok := jsonErr.ErrorData().(string) - if !ok || len(errData) < 10 { - offR, err := offchainresolver.NewContract(rAddr, backend) - if err != nil { - return UnknownAddress, errors.New("no address") - } + ccipErr, errData := getCcipReadError(err) + if !ccipErr { rAbi, _ := resolver.ContractMetaData.GetAbi() m := rAbi.Methods["addr"] - args, _ := m.Inputs.Pack(nhash) - rawAddr, err := offR.Resolve(nil, lhash, append(m.ID, args...)) + args, _ := m.Inputs.Pack(node) + rawAddr, err := r.offResolver.Resolve(nil, lhash, append(m.ID, args...)) if err == nil { // resolved on-chain return common.BytesToAddress(rawAddr), nil } - jsonErr = err.(rpc.DataError) - errData = jsonErr.ErrorData().(string) + ccipErr, errData = getCcipReadError(err) } - if len(errData) >= 10 && errData[:10] == offchainLookupSignature { - rawAddr, err := CCIPRead(backend, rAddr, errData) - if err != nil || bytes.Equal(rawAddr, zeroHash) { - return UnknownAddress, errors.New("no address") - } - return common.BytesToAddress(rawAddr), nil + if !ccipErr { + return UnknownAddress, errors.New("unregistered name") + } + + rawAddr, err := CCIPRead(backend, r.ContractAddr, errData) + if err != nil || bytes.Equal(rawAddr, zeroHash) { + return UnknownAddress, errors.New("no address") } - return UnknownAddress, errors.New("unregistered name") + return common.BytesToAddress(rawAddr), nil } // SetText sets the text associated with a name. @@ -276,11 +262,32 @@ func (r *Resolver) SetText(opts *bind.TransactOpts, name string, value string) ( // Text obtains the text associated with a name. func (r *Resolver) Text(name string) (string, error) { - nameHash, err := NameHash(r.domain) + node, err := NameHash(r.domain) if err != nil { return "", err } - return r.Contract.Text(nil, nameHash, name) + text, err := r.Contract.Text(nil, node, name) + if err == nil { + return text, nil + } + rAbi, _ := resolver.ContractMetaData.GetAbi() + m := rAbi.Methods["text"] + + ccipErr, errData := getCcipReadError(err) + if !ccipErr { + lhash, err := DNSEncode(r.domain) + if err != nil { + return "", err + } + + args, _ := m.Inputs.Pack(node, name) + rawResp, err := r.offResolver.Resolve(nil, lhash, append(m.ID, args...)) + if err == nil { + return string(rawResp), err + } + } + rawResp, err := CCIPRead(r.backend, r.ContractAddr, errData) + return string(rawResp), err } // SetABI sets the ABI associated with a name. diff --git a/resolver_test.go b/resolver_test.go index 790c32b..efd4048 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -109,7 +109,7 @@ func TestSubdomainResolveAddress(t *testing.T) { assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") } -func TestExternalResolverAddress(t *testing.T) { +func TestOffchainResolverAddress(t *testing.T) { expected := "849151d7d0bf1f34b70d5cad5149d28cc2308bf1" actual, err := Resolve(client, "jesse.cb.id") require.Nil(t, err, "Error resolving address") @@ -121,3 +121,21 @@ func TestInvalidSubdomainResolveAddress(t *testing.T) { require.NotNil(t, err, "Error resolving address") assert.Equal(t, "unregistered name", err.Error(), "Unexpected error") } + +func TestOnchainReadText(t *testing.T) { + expected := "blockful_io" + r, err := NewResolver(client, "blockful.eth") + require.Nil(t, err, "Error creating resolver") + actual, err := r.Text("com.twitter") + require.Nil(t, err, "Error reading com.twitter") + assert.Equal(t, expected, actual, "Did not receive expected result") +} + +func TestOffchainReadText(t *testing.T) { + expected := "https://twitter.com/sid_coelho" + r, err := NewResolver(client, "jesse.cb.id") + require.Nil(t, err, "Error creating resolver") + actual, err := r.Text("com.twitter") + require.Nil(t, err, "Error reading twitter") + assert.Equal(t, expected, actual, "Did not receive expected result") +} From b7b1f4efad60df4348f312872bbaf2ee6e662e55 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Tue, 26 Mar 2024 09:20:03 -0300 Subject: [PATCH 09/13] fix: decode text output --- ccip.go | 18 ++++++++++++++---- resolver.go | 20 +++++++++++++++++--- resolver_test.go | 2 +- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/ccip.go b/ccip.go index 26621ef..44ccb99 100644 --- a/ccip.go +++ b/ccip.go @@ -56,7 +56,8 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str calldata := common.Bytes2Hex(errArgs[2].([]byte)) calldataHex := fmt.Sprintf("0x%s", calldata) callback := errArgs[3].([4]byte) - extraData := errArgs[4].([]byte) + extraData := common.Bytes2Hex(errArgs[4].([]byte)) + extraDataHex := fmt.Sprintf("0x%s", extraData) // Fetching data from external source using CCIP resp, err := CCIPFetch(sender, calldataHex, urls) @@ -72,16 +73,25 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str if err != nil { return nil, errors.New("no address") } - args, err := m.Inputs.Pack(common.Hex2Bytes(resp[2:]), extraData) + args, err := m.Inputs.Pack(common.FromHex(resp), common.FromHex(extraDataHex)) if err != nil { return nil, errors.New("no address") } - // Calling the offchain resolver callback to validate the external source response - return backend.CallContract(context.Background(), ethereum.CallMsg{ + encodedResp, err := backend.CallContract(context.Background(), ethereum.CallMsg{ To: &rAddr, Data: append(callback[:], args...), }, nil) + + if err != nil { + return nil, err + } + + outputs, err := m.Outputs.Unpack(encodedResp) + if err != nil || len(outputs) == 0 { + return nil, err + } + return outputs[0].([]byte), nil } func CCIPFetch(sender common.Address, data string, urls []string) (result string, err error) { diff --git a/resolver.go b/resolver.go index 4cb2ad4..eea7a19 100644 --- a/resolver.go +++ b/resolver.go @@ -18,6 +18,7 @@ import ( "bytes" "compress/zlib" "errors" + "fmt" "io" "math/big" "strings" @@ -55,9 +56,12 @@ func NewResolver(backend bind.ContractBackend, domain string) (*Resolver, error) return nil, err } rAddr, _, _, err := ur.Contract.FindResolver(nil, lhash) - if err != nil || rAddr == common.Address(zeroHash) { + if err != nil { return nil, err } + if rAddr == common.Address(zeroHash) { + return nil, errors.New("unregistered name") + } return NewResolverAt(backend, domain, rAddr) } @@ -270,10 +274,11 @@ func (r *Resolver) Text(name string) (string, error) { if err == nil { return text, nil } + + ccipErr, errData := getCcipReadError(err) rAbi, _ := resolver.ContractMetaData.GetAbi() m := rAbi.Methods["text"] - ccipErr, errData := getCcipReadError(err) if !ccipErr { lhash, err := DNSEncode(r.domain) if err != nil { @@ -285,9 +290,18 @@ func (r *Resolver) Text(name string) (string, error) { if err == nil { return string(rawResp), err } + _, errData = getCcipReadError(err) } + rawResp, err := CCIPRead(r.backend, r.ContractAddr, errData) - return string(rawResp), err + if err != nil { + return "", err + } + x, err := m.Outputs.Unpack(rawResp) + if err != nil { + return "", err + } + return fmt.Sprint(x[0]), nil } // SetABI sets the ABI associated with a name. diff --git a/resolver_test.go b/resolver_test.go index efd4048..a888a5e 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -133,7 +133,7 @@ func TestOnchainReadText(t *testing.T) { func TestOffchainReadText(t *testing.T) { expected := "https://twitter.com/sid_coelho" - r, err := NewResolver(client, "jesse.cb.id") + r, err := NewResolver(client, "sid.cb.id") require.Nil(t, err, "Error creating resolver") actual, err := r.Text("com.twitter") require.Nil(t, err, "Error reading twitter") From 0241d2b5e739450ab2e7761482fbd535037a3abf Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Tue, 26 Mar 2024 09:29:55 -0300 Subject: [PATCH 10/13] test: add subdomain ccip tests --- resolver.go | 7 ++----- resolver_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/resolver.go b/resolver.go index eea7a19..03a769d 100644 --- a/resolver.go +++ b/resolver.go @@ -56,10 +56,7 @@ func NewResolver(backend bind.ContractBackend, domain string) (*Resolver, error) return nil, err } rAddr, _, _, err := ur.Contract.FindResolver(nil, lhash) - if err != nil { - return nil, err - } - if rAddr == common.Address(zeroHash) { + if err != nil || rAddr == common.Address(zeroHash) { return nil, errors.New("unregistered name") } return NewResolverAt(backend, domain, rAddr) @@ -211,7 +208,7 @@ func resolveName(backend bind.ContractBackend, input string) (common.Address, er func resolveHash(backend bind.ContractBackend, domain string) (common.Address, error) { r, err := NewResolver(backend, domain) if err != nil { - return UnknownAddress, errors.New("no resolver") + return UnknownAddress, err } node, err := NameHash(domain) if err != nil { diff --git a/resolver_test.go b/resolver_test.go index a888a5e..2230b38 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -116,6 +116,13 @@ func TestOffchainResolverAddress(t *testing.T) { assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") } +func TestOffchainResolverAddressMulticoin(t *testing.T) { + expected := "849151d7d0bf1f34b70d5cad5149d28cc2308bf1" + actual, err := Resolve(client, "jesse.cb.id") + require.Nil(t, err, "Error resolving address") + assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") +} + func TestInvalidSubdomainResolveAddress(t *testing.T) { _, err := Resolve(client, "a.b.c.d.e.eth") require.NotNil(t, err, "Error resolving address") @@ -139,3 +146,26 @@ func TestOffchainReadText(t *testing.T) { require.Nil(t, err, "Error reading twitter") assert.Equal(t, expected, actual, "Did not receive expected result") } + +func TestOffchainReadAddressTLD(t *testing.T) { + expected := "179a862703a4adfb29896552df9e307980d19285" + actual, err := Resolve(client, "gregskril.eth") + require.Nil(t, err, "Error resolving address") + assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") +} + +func TestOffchainReadAddressSubdomain(t *testing.T) { + expected := "179a862703a4adfb29896552df9e307980d19285" + actual, err := Resolve(client, "gregskril.uni.eth") + require.Nil(t, err, "Error resolving address") + assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") +} + +func TestOffchainReadTextSubdomain(t *testing.T) { + expected := "gregskril" + r, err := NewResolver(client, "gregskril.uni.eth") + require.Nil(t, err, "Error creating resolver") + actual, err := r.Text("com.twitter") + require.Nil(t, err, "Error reading twitter") + assert.Equal(t, expected, actual, "Did not receive expected result") +} From 3d575230569058f94ad04b4880237bfa0ef83445 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Tue, 26 Mar 2024 11:33:49 -0300 Subject: [PATCH 11/13] refactor: move ccip to resolver.Address method --- ccip.go | 5 ++- resolver.go | 79 +++++++++++++++++++++++------------------------- resolver_test.go | 16 +++++----- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/ccip.go b/ccip.go index 44ccb99..7a61efc 100644 --- a/ccip.go +++ b/ccip.go @@ -20,7 +20,10 @@ import ( ) func getCcipReadError(err error) (bool, string) { - var jsonErr = err.(rpc.DataError) + var jsonErr, ok = err.(rpc.DataError) + if !ok { + return false, "" + } errData, ok := jsonErr.ErrorData().(string) if !ok { return false, "" diff --git a/resolver.go b/resolver.go index 03a769d..21bef86 100644 --- a/resolver.go +++ b/resolver.go @@ -90,11 +90,45 @@ func PublicResolverAddress(backend bind.ContractBackend) (common.Address, error) // Address returns the Ethereum address of the domain. func (r *Resolver) Address() (common.Address, error) { - nameHash, err := NameHash(r.domain) + node, err := NameHash(r.domain) if err != nil { return UnknownAddress, err } - return r.Contract.Addr(nil, nameHash) + addr, err := r.Contract.Addr(nil, node) + if err == nil && addr != common.Address(zeroHash) { + return addr, err + } + ccipErr, errData := getCcipReadError(err) + + // CCIP Read check + if !ccipErr { + rAbi, _ := resolver.ContractMetaData.GetAbi() + m := rAbi.Methods["addr"] + args, _ := m.Inputs.Pack(node) + lhash, err := DNSEncode(r.domain) + if err != nil { + return common.Address{}, err + } + rawAddr, err := r.offResolver.Resolve(nil, lhash, append(m.ID, args...)) + if err == nil { + // resolved on-chain + return common.BytesToAddress(rawAddr), nil + } + ccipErr, errData = getCcipReadError(err) + if errData == "" && addr == common.Address(zeroHash) { + return UnknownAddress, errors.New("unregistered name") + } + } + + if !ccipErr { + return UnknownAddress, errors.New("unregistered name") + } + + rawAddr, err := CCIPRead(r.backend, r.ContractAddr, errData) + if err != nil || bytes.Equal(rawAddr, zeroHash) { + return UnknownAddress, errors.New("no address") + } + return common.BytesToAddress(rawAddr), nil } // SetAddress sets the Ethereum address of the domain. @@ -210,46 +244,7 @@ func resolveHash(backend bind.ContractBackend, domain string) (common.Address, e if err != nil { return UnknownAddress, err } - node, err := NameHash(domain) - if err != nil { - return UnknownAddress, err - } - lhash, err := DNSEncode(domain) - if err != nil { - return common.Address{}, err - } - - addr, err := r.Contract.Addr(nil, node) - if err == nil { - if addr == common.Address(zeroHash) { - return UnknownAddress, errors.New("unregistered name") - } - return addr, nil - } - - // CCIP Read check - ccipErr, errData := getCcipReadError(err) - if !ccipErr { - rAbi, _ := resolver.ContractMetaData.GetAbi() - m := rAbi.Methods["addr"] - args, _ := m.Inputs.Pack(node) - rawAddr, err := r.offResolver.Resolve(nil, lhash, append(m.ID, args...)) - if err == nil { - // resolved on-chain - return common.BytesToAddress(rawAddr), nil - } - ccipErr, errData = getCcipReadError(err) - } - - if !ccipErr { - return UnknownAddress, errors.New("unregistered name") - } - - rawAddr, err := CCIPRead(backend, r.ContractAddr, errData) - if err != nil || bytes.Equal(rawAddr, zeroHash) { - return UnknownAddress, errors.New("no address") - } - return common.BytesToAddress(rawAddr), nil + return r.Address() } // SetText sets the text associated with a name. diff --git a/resolver_test.go b/resolver_test.go index 2230b38..8b5aa6b 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -116,13 +116,6 @@ func TestOffchainResolverAddress(t *testing.T) { assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") } -func TestOffchainResolverAddressMulticoin(t *testing.T) { - expected := "849151d7d0bf1f34b70d5cad5149d28cc2308bf1" - actual, err := Resolve(client, "jesse.cb.id") - require.Nil(t, err, "Error resolving address") - assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") -} - func TestInvalidSubdomainResolveAddress(t *testing.T) { _, err := Resolve(client, "a.b.c.d.e.eth") require.NotNil(t, err, "Error resolving address") @@ -161,6 +154,15 @@ func TestOffchainReadAddressSubdomain(t *testing.T) { assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") } +func TestOffchainReadAddressSubdomainThroughResolver(t *testing.T) { + expected := "179a862703a4adfb29896552df9e307980d19285" + r, err := NewResolver(client, "gregskril.uni.eth") + require.Nil(t, err, "Error resolving address") + actual, err := r.Address() + require.Nil(t, err, "Error resolving address") + assert.Equal(t, expected, hex.EncodeToString(actual[:]), "Did not receive expected result") +} + func TestOffchainReadTextSubdomain(t *testing.T) { expected := "gregskril" r, err := NewResolver(client, "gregskril.uni.eth") From eefee11c7edafe247adbb5cce14255c50452ffb1 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Wed, 27 Mar 2024 10:44:15 -0300 Subject: [PATCH 12/13] test: invalid offchain subdomain --- resolver.go | 2 +- resolver_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resolver.go b/resolver.go index 21bef86..66c92dc 100644 --- a/resolver.go +++ b/resolver.go @@ -126,7 +126,7 @@ func (r *Resolver) Address() (common.Address, error) { rawAddr, err := CCIPRead(r.backend, r.ContractAddr, errData) if err != nil || bytes.Equal(rawAddr, zeroHash) { - return UnknownAddress, errors.New("no address") + return UnknownAddress, errors.New("unregistered name") } return common.BytesToAddress(rawAddr), nil } diff --git a/resolver_test.go b/resolver_test.go index 8b5aa6b..92d8dee 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -117,7 +117,7 @@ func TestOffchainResolverAddress(t *testing.T) { } func TestInvalidSubdomainResolveAddress(t *testing.T) { - _, err := Resolve(client, "a.b.c.d.e.eth") + _, err := Resolve(client, "xxxx.cb.id") require.NotNil(t, err, "Error resolving address") assert.Equal(t, "unregistered name", err.Error(), "Unexpected error") } From 919192827a58fb6fbf66619cf03c880ce4a8d288 Mon Sep 17 00:00:00 2001 From: lucas picollo Date: Wed, 10 Jul 2024 10:03:40 -0300 Subject: [PATCH 13/13] chore: change variable names and add client timeout --- ccip.go | 21 +++++++++------- resolver.go | 24 +++++++++---------- universal_resolver.go => universalresolver.go | 0 3 files changed, 25 insertions(+), 20 deletions(-) rename universal_resolver.go => universalresolver.go (100%) diff --git a/ccip.go b/ccip.go index 7a61efc..7d7476b 100644 --- a/ccip.go +++ b/ccip.go @@ -10,6 +10,7 @@ import ( "io" "net/http" "strings" + "time" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -31,17 +32,20 @@ func getCcipReadError(err error) (bool, string) { return (len(errData) >= 10 && errData[:10] == offchainLookupSignature), errData } -func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData string) ([]byte, error) { - hexBytes, err := hex.DecodeString(revertData[2:]) +func ccipRead(backend bind.ContractBackend, resolverAddr common.Address, revertData string) ([]byte, error) { + hexBytes, err := hex.DecodeString(strings.TrimPrefix(revertData, "0x")) if err != nil { return nil, err } - uAbi, err := universalresolver.ContractMetaData.GetAbi() if err != nil { return nil, err } + if len(revertData) < 5 { + return nil, errors.New("invalid revert data") + } + // Extracting error details from the revert data var sig [4]byte copy(sig[:], hexBytes[:4]) @@ -63,7 +67,7 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str extraDataHex := fmt.Sprintf("0x%s", extraData) // Fetching data from external source using CCIP - resp, err := CCIPFetch(sender, calldataHex, urls) + resp, err := ccipFetch(sender, calldataHex, urls) if err != nil || len(resp) == 0 { return nil, errors.New("unregistered name") } @@ -82,7 +86,7 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str } encodedResp, err := backend.CallContract(context.Background(), ethereum.CallMsg{ - To: &rAddr, + To: &resolverAddr, Data: append(callback[:], args...), }, nil) @@ -97,7 +101,7 @@ func CCIPRead(backend bind.ContractBackend, rAddr common.Address, revertData str return outputs[0].([]byte), nil } -func CCIPFetch(sender common.Address, data string, urls []string) (result string, err error) { +func ccipFetch(sender common.Address, data string, urls []string) (result string, err error) { for _, url := range urls { method := "POST" if strings.Contains(url, "{data}") { @@ -123,7 +127,9 @@ func CCIPFetch(sender common.Address, data string, urls []string) (result string } req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := &http.Client{ + Timeout: 5 * time.Second, + } resp, err := client.Do(req) if err != nil { return "", err @@ -141,7 +147,6 @@ func CCIPFetch(sender common.Address, data string, urls []string) (result string var ok bool result, ok = responseData["data"].(string) if !ok { - err = errors.New("invalid response from gateway") continue } } else { diff --git a/resolver.go b/resolver.go index 66c92dc..a022359 100644 --- a/resolver.go +++ b/resolver.go @@ -90,11 +90,11 @@ func PublicResolverAddress(backend bind.ContractBackend) (common.Address, error) // Address returns the Ethereum address of the domain. func (r *Resolver) Address() (common.Address, error) { - node, err := NameHash(r.domain) + nameHash, err := NameHash(r.domain) if err != nil { return UnknownAddress, err } - addr, err := r.Contract.Addr(nil, node) + addr, err := r.Contract.Addr(nil, nameHash) if err == nil && addr != common.Address(zeroHash) { return addr, err } @@ -104,7 +104,7 @@ func (r *Resolver) Address() (common.Address, error) { if !ccipErr { rAbi, _ := resolver.ContractMetaData.GetAbi() m := rAbi.Methods["addr"] - args, _ := m.Inputs.Pack(node) + args, _ := m.Inputs.Pack(nameHash) lhash, err := DNSEncode(r.domain) if err != nil { return common.Address{}, err @@ -124,7 +124,7 @@ func (r *Resolver) Address() (common.Address, error) { return UnknownAddress, errors.New("unregistered name") } - rawAddr, err := CCIPRead(r.backend, r.ContractAddr, errData) + rawAddr, err := ccipRead(r.backend, r.ContractAddr, errData) if err != nil || bytes.Equal(rawAddr, zeroHash) { return UnknownAddress, errors.New("unregistered name") } @@ -240,11 +240,11 @@ func resolveName(backend bind.ContractBackend, input string) (common.Address, er } func resolveHash(backend bind.ContractBackend, domain string) (common.Address, error) { - r, err := NewResolver(backend, domain) + resolver, err := NewResolver(backend, domain) if err != nil { return UnknownAddress, err } - return r.Address() + return resolver.Address() } // SetText sets the text associated with a name. @@ -258,11 +258,11 @@ func (r *Resolver) SetText(opts *bind.TransactOpts, name string, value string) ( // Text obtains the text associated with a name. func (r *Resolver) Text(name string) (string, error) { - node, err := NameHash(r.domain) + nameHash, err := NameHash(r.domain) if err != nil { return "", err } - text, err := r.Contract.Text(nil, node, name) + text, err := r.Contract.Text(nil, nameHash, name) if err == nil { return text, nil } @@ -277,7 +277,7 @@ func (r *Resolver) Text(name string) (string, error) { return "", err } - args, _ := m.Inputs.Pack(node, name) + args, _ := m.Inputs.Pack(nameHash, name) rawResp, err := r.offResolver.Resolve(nil, lhash, append(m.ID, args...)) if err == nil { return string(rawResp), err @@ -285,15 +285,15 @@ func (r *Resolver) Text(name string) (string, error) { _, errData = getCcipReadError(err) } - rawResp, err := CCIPRead(r.backend, r.ContractAddr, errData) + rawResp, err := ccipRead(r.backend, r.ContractAddr, errData) if err != nil { return "", err } - x, err := m.Outputs.Unpack(rawResp) + outputs, err := m.Outputs.Unpack(rawResp) if err != nil { return "", err } - return fmt.Sprint(x[0]), nil + return fmt.Sprint(outputs[0]), nil } // SetABI sets the ABI associated with a name. diff --git a/universal_resolver.go b/universalresolver.go similarity index 100% rename from universal_resolver.go rename to universalresolver.go