From 95312a192cdb276fdf094296084e99ae4e1f2a68 Mon Sep 17 00:00:00 2001 From: Benjamin Bouchet Date: Fri, 27 Jan 2023 17:11:39 +0100 Subject: [PATCH 01/39] Manually overwrite solc path (#212) --- lib/eth/solidity.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/eth/solidity.rb b/lib/eth/solidity.rb index bfbfd87d..e6f4bd7b 100644 --- a/lib/eth/solidity.rb +++ b/lib/eth/solidity.rb @@ -28,10 +28,10 @@ class CompilerError < StandardError; end # Instantiates a Solidity `solc` system compiler binding that can be # used to compile Solidity contracts. - def initialize + def initialize(manual_path = nil) # Currently only supports `solc`. - solc = get_compiler_path + solc = manual_path || get_compiler_path raise SystemCallError, "Unable to find the solc compiler path!" if solc.nil? @compiler = solc end From 0ba05f99658d1b4bee700700dad5c444eafd9087 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Fri, 27 Jan 2023 17:28:42 +0100 Subject: [PATCH 02/39] eth/solidity: add docs for solc path override (#213) --- lib/eth/solidity.rb | 8 +++++--- spec/eth/solidity_spec.rb | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/eth/solidity.rb b/lib/eth/solidity.rb index e6f4bd7b..bd3dbd76 100644 --- a/lib/eth/solidity.rb +++ b/lib/eth/solidity.rb @@ -28,10 +28,12 @@ class CompilerError < StandardError; end # Instantiates a Solidity `solc` system compiler binding that can be # used to compile Solidity contracts. - def initialize(manual_path = nil) + # + # @param path [String] optional override of the solidity compiler path. + def initialize(path = nil) - # Currently only supports `solc`. - solc = manual_path || get_compiler_path + # Currently only supports `solc`. Try to override with `path`. + solc = path || get_compiler_path raise SystemCallError, "Unable to find the solc compiler path!" if solc.nil? @compiler = solc end diff --git a/spec/eth/solidity_spec.rb b/spec/eth/solidity_spec.rb index 94e99dce..a92759c6 100644 --- a/spec/eth/solidity_spec.rb +++ b/spec/eth/solidity_spec.rb @@ -5,6 +5,7 @@ # This fails if no `solc` is in the $PATH. expect(Solidity.new).to be + expect(Solidity.new(system("which", "solc"))).to be end subject(:solc) { Solidity.new } From a6fa05ad753d6aab6f582e0617db78b0eedc272a Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Thu, 2 Feb 2023 12:08:30 +0100 Subject: [PATCH 03/39] deps: update secp256k1 to 6 (#214) --- eth.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth.gemspec b/eth.gemspec index 201bc563..b928f826 100644 --- a/eth.gemspec +++ b/eth.gemspec @@ -41,7 +41,7 @@ Gem::Specification.new do |spec| spec.add_dependency "konstructor", "~> 1.0" # rbsecp256k1 for key-pairs and signatures - spec.add_dependency "rbsecp256k1", "~> 5.1" + spec.add_dependency "rbsecp256k1", "~> 6.0" # openssl for encrypted key derivation spec.add_dependency "openssl", ">= 2.2", "< 4.0" From f6f1c7d5b62f692d433119b84aa9e943e520b895 Mon Sep 17 00:00:00 2001 From: Benjamin Bouchet Date: Mon, 6 Feb 2023 14:26:31 +0100 Subject: [PATCH 04/39] Manual default_account (#215) --- lib/eth/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eth/client.rb b/lib/eth/client.rb index d1c90f11..10b40095 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -74,7 +74,7 @@ def initialize(_) # # @return [Eth::Address] the coinbase account address. def default_account - raise ArgumentError, "The default account is not available on remote connections!" unless local? + raise ArgumentError, "The default account is not available on remote connections!" unless local? || @default_account @default_account ||= Address.new eth_coinbase["result"] end From c50fc9ab2b28f50178e8bd9dfae13accbdba7d1a Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:36:54 +0100 Subject: [PATCH 05/39] eth/abi: add abicoder gem tests collection (#218) --- spec/eth/abi_spec.rb | 78 +++++++++++++++++++++++++++++++++++ spec/eth/ens/resolver_spec.rb | 3 +- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/spec/eth/abi_spec.rb b/spec/eth/abi_spec.rb index ae369734..3d623233 100644 --- a/spec/eth/abi_spec.rb +++ b/spec/eth/abi_spec.rb @@ -310,4 +310,82 @@ expect(Util.bin_to_hex Abi.encode ["bytes10"], ["1234567890".b]).to eq "3132333435363738393000000000000000000000000000000000000000000000" end end + + describe "abicoder tests" do + # https://github.com/rubycocos/blockchain/blob/ccef43a600e0832fb5e662bb0840656c974c0dc5/abicoder/test/test_spec.rb + def assert(data, types, args) + expect(data).to eq Abi.encode(types, args) + expect(args).to eq Abi.decode(types, data) + expect(args).to eq Abi.decode(types, Abi.encode(types, args)) + end + + it "test_baz" do + types = ["uint32", "bool"] + args = [69, true] + data = Util.hex_to_bin "00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001" + assert(data, types, args) + end + + it "test_bar" do + types = ["bytes3[2]"] + args = [["abc".b, "def".b]] + data = Util.hex_to_bin "61626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000" + assert(data, types, args) + end + + it "test_sam" do + types = ["bytes", "bool", "uint256[]"] + args = ["dave".b, true, [1, 2, 3]] + data = Util.hex_to_bin "0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003" + assert(data, types, args) + end + + it "test_f" do + types = ["uint256", "uint32[]", "bytes10", "bytes"] + args = [0x123, [0x456, 0x789], "1234567890".b, "Hello, world!".b] + data = Util.hex_to_bin "00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + assert(data, types, args) + end + + it "test_g" do + types = ["uint256[][]", "string[]"] + args = [[[1, 2], [3]], ["one", "two", "three"]] + data = Util.hex_to_bin "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000036f6e650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000374776f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057468726565000000000000000000000000000000000000000000000000000000" + pending("https://github.com/q9f/eth.rb/issues/217") + assert(data, types, args) + end + + it "test_hello" do + types = ["uint256", "uint32[]", "bytes10", "bytes"] + args = [291, [1110, 1929], "1234567890".b, "Hello, world!".b] + data = Util.hex_to_bin "00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + assert(data, types, args) + end + + it "test_githubwiki" do + types = ["uint256", "string"] + args = [1234, "Hello World"] + data = Util.hex_to_bin "00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000" + assert(data, types, args) + + types = ["uint256[]", "string"] + args = [[1234, 5678], "Hello World"] + data = Util.hex_to_bin "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004d2000000000000000000000000000000000000000000000000000000000000162e000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000" + assert(data, types, args) + end + + it "test_tuples" do + types = ["uint256", "(uint256,string)"] + args = [1234, [5678, "Hello World"]] + data = Util.hex_to_bin "00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000162e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000" + pending("https://github.com/q9f/eth.rb/issues/102") + assert(data, types, args) + + types = ["uint256", "(address,uint256)[]", "string"] + args = [66, [["18a475d6741215709ed6cc5f4d064732379b5a58", 1]], "QmWBiSE9ByR6vrx4hvrjqS3SG5r6wE4SRq7CP2RVpafZWV"] + data = Util.hex_to_bin "0000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000018a475d6741215709ed6cc5f4d064732379b5a580000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002e516d57426953453942795236767278346876726a71533353473572367745345352713743503252567061665a5756000000000000000000000000000000000000" + pending("https://github.com/q9f/eth.rb/issues/102") + assert(data, types, args) + end + end end diff --git a/spec/eth/ens/resolver_spec.rb b/spec/eth/ens/resolver_spec.rb index a7be946a..f27fd4b5 100644 --- a/spec/eth/ens/resolver_spec.rb +++ b/spec/eth/ens/resolver_spec.rb @@ -56,8 +56,7 @@ expect { resolver.resolve("ncWc6Edqldzy6Mlo.eth", Ens::CoinType::BITCOIN) }.to raise_error NotImplementedError, "Coin type 0 not implemented!" - # https://ethereum.stackexchange.com/questions/142016/does-ens-implement-eip-2304-yet - pending("there seems to be an issue with eip-2304") + pending("https://github.com/ensdomains/ens-app/issues/1588") expect(resolver.resolve("ncWc6Edqldzy6Mlo.eth", Ens::CoinType::ETHEREUM_CLASSIC)).to eq "0x37287f68aC899b769FAa57033c78B78c76C68dc0" end end From d405bf13d52138feadc9619b9bd8ac9c85d8eed0 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:40:15 +0100 Subject: [PATCH 06/39] ens/resolver: remove pending for etc coin type (#219) --- spec/eth/ens/resolver_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/eth/ens/resolver_spec.rb b/spec/eth/ens/resolver_spec.rb index f27fd4b5..135c9d6b 100644 --- a/spec/eth/ens/resolver_spec.rb +++ b/spec/eth/ens/resolver_spec.rb @@ -56,8 +56,7 @@ expect { resolver.resolve("ncWc6Edqldzy6Mlo.eth", Ens::CoinType::BITCOIN) }.to raise_error NotImplementedError, "Coin type 0 not implemented!" - pending("https://github.com/ensdomains/ens-app/issues/1588") - expect(resolver.resolve("ncWc6Edqldzy6Mlo.eth", Ens::CoinType::ETHEREUM_CLASSIC)).to eq "0x37287f68aC899b769FAa57033c78B78c76C68dc0" + expect(resolver.resolve("ncWc6Edqldzy6Mlo.eth", Ens::CoinType::ETHEREUM_CLASSIC)).to eq "0x37287f68ac899b769faa57033c78b78c76c68dc0" end end end From d7f7faf7b9adf1398362e2efa31cbe8af6d10f61 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sat, 25 Mar 2023 10:53:23 +0100 Subject: [PATCH 07/39] deps: require forwardable for contracts (#227) --- eth.gemspec | 3 +++ lib/eth/contract.rb | 2 ++ 2 files changed, 5 insertions(+) diff --git a/eth.gemspec b/eth.gemspec index b928f826..56c341ab 100644 --- a/eth.gemspec +++ b/eth.gemspec @@ -34,6 +34,9 @@ Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.required_ruby_version = ">= 2.7", "< 4.0" + # forwardable for contracts meta programming + spec.add_dependency "forwardable", "~> 1.3" + # keccak for hashing everything in ethereum spec.add_dependency "keccak", "~> 1.3" diff --git a/lib/eth/contract.rb b/lib/eth/contract.rb index c74ce409..64aee711 100644 --- a/lib/eth/contract.rb +++ b/lib/eth/contract.rb @@ -14,6 +14,8 @@ # -*- encoding : ascii-8bit -*- +require "forwardable" + # Provides the {Eth} module. module Eth From b93b1ecd409232c2163dd3c14b3b490e61423b8a Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sat, 25 Mar 2023 10:54:48 +0100 Subject: [PATCH 08/39] eth/contract: ensure contract name is title case (#228) --- lib/eth/contract.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/eth/contract.rb b/lib/eth/contract.rb index 64aee711..e455e015 100644 --- a/lib/eth/contract.rb +++ b/lib/eth/contract.rb @@ -29,11 +29,19 @@ class Contract # Constructor of the {Eth::Contract} class. # + # Do not use this directly. Use + # {from_abi}, {from_bin}, or {from_file}! + # # @param name [String] contract name. # @param bin [String] contract bin string. # @param abi [String] contract abi string. def initialize(name, bin, abi) - @name = name + + # The contract name will be the class name and needs title casing. + _name = name.dup + _name[0] = name[0].upcase + + @name = _name @bin = bin @abi = abi @constructor_inputs, @functions, @events = parse_abi(abi) From 3c3c8063f948a63fbc4c923c3c876a64f34bc848 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sat, 25 Mar 2023 11:09:25 +0100 Subject: [PATCH 09/39] docs: minor fixups (#229) --- lib/eth/client.rb | 6 +++--- lib/eth/contract.rb | 2 +- lib/eth/version.rb | 13 +++++++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/eth/client.rb b/lib/eth/client.rb index 10b40095..9904942c 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -174,7 +174,7 @@ def transfer_erc20_and_wait(erc20_contract, destination, amount, **kwargs) # @param amount [Integer] the transfer amount (mind the `decimals()`). # @param **sender_key [Eth::Key] the sender private key. # @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559). - # @param **gas_limit [Integer] optional gas limit override for deploying the contract. + # @param **gas_limit [Integer] optional gas limit override for the transfer. # @param **nonce [Integer] optional specific nonce for transaction. # @param **tx_value [Integer] optional transaction value field filling. # @return [Object] returns the result of the transaction. @@ -252,7 +252,7 @@ def deploy(contract, *args, **kwargs) # @param *args optional function arguments. # @param **sender_key [Eth::Key] the sender private key. # @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559). - # @param **gas_limit [Integer] optional gas limit override for deploying the contract. + # @param **gas_limit [Integer] optional gas limit override for calling the contract. # @return [Object] returns the result of the call. def call(contract, function, *args, **kwargs) func = contract.functions.select { |func| func.name == function } @@ -291,7 +291,7 @@ def call(contract, function, *args, **kwargs) # @param **sender_key [Eth::Key] the sender private key. # @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559). # @param **address [Eth::Address] contract address. - # @param **gas_limit [Integer] optional gas limit override for deploying the contract. + # @param **gas_limit [Integer] optional gas limit override for transacting with the contract. # @param **nonce [Integer] optional specific nonce for transaction. # @param **tx_value [Integer] optional transaction value field filling. # @return [Object] returns the result of the transaction. diff --git a/lib/eth/contract.rb b/lib/eth/contract.rb index e455e015..54ebc6df 100644 --- a/lib/eth/contract.rb +++ b/lib/eth/contract.rb @@ -29,7 +29,7 @@ class Contract # Constructor of the {Eth::Contract} class. # - # Do not use this directly. Use + # **Note**, do not use this directly. Use # {from_abi}, {from_bin}, or {from_file}! # # @param name [String] contract name. diff --git a/lib/eth/version.rb b/lib/eth/version.rb index 17e76139..92f475eb 100644 --- a/lib/eth/version.rb +++ b/lib/eth/version.rb @@ -15,6 +15,15 @@ # Provides the {Eth} module. module Eth - # Defines the version of the {Eth} module. - VERSION = "0.5.11".freeze + # Defines the major version of the {Eth} module. + MAJOR = 0.freeze + + # Defines the minor version of the {Eth} module. + MINOR = 5.freeze + + # Defines the patch version of the {Eth} module. + PATCH = 11.freeze + + # Defines the version string of the {Eth} module. + VERSION = [MAJOR, MINOR, PATCH].join(".").freeze end From dcac24bff6c4c353a60f5c98e1069bf3801d7e99 Mon Sep 17 00:00:00 2001 From: Yuta Kurotaki Date: Mon, 8 May 2023 22:17:20 +0900 Subject: [PATCH 10/39] Checking userinfo with the uri method (#233) --- lib/eth/client/http.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eth/client/http.rb b/lib/eth/client/http.rb index 96ced341..3f9cef61 100644 --- a/lib/eth/client/http.rb +++ b/lib/eth/client/http.rb @@ -46,7 +46,7 @@ def initialize(host) @host = uri.host @port = uri.port @ssl = uri.scheme == "https" - if Regexp.new(":.*@.*:", Regexp::IGNORECASE).match host + if !(uri.user.nil? && uri.password.nil?) @user = uri.user @password = uri.password @uri = URI("#{uri.scheme}://#{uri.user}:#{uri.password}@#{@host}:#{@port}#{uri.path}") From 50137024883136195d0b71b3d0baaee77c6a9951 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Mon, 8 May 2023 16:01:35 +0200 Subject: [PATCH 11/39] eth/solidity: enable --via-ir (#232) * eth/solidity: enable --via-ir * spec: adjust tests --- lib/eth/solidity.rb | 3 ++- spec/eth/solidity_spec.rb | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/eth/solidity.rb b/lib/eth/solidity.rb index bd3dbd76..88669c65 100644 --- a/lib/eth/solidity.rb +++ b/lib/eth/solidity.rb @@ -45,9 +45,10 @@ def initialize(path = nil) def compile(contract) raise Errno::ENOENT, "Contract file not found: #{contract}" unless File.exist? contract flag_opt = "--optimize" + flag_ir = "--via-ir" flag_json = "--combined-json=bin,abi" path = File.realpath contract - output, error, status = Open3.capture3 @compiler, flag_opt, flag_json, path + output, error, status = Open3.capture3 @compiler, flag_opt, flag_ir, flag_json, path raise SystemCallError, "Unable to run solc compiler!" if status.exitstatus === 127 raise CompilerError, error unless status.success? json = JSON.parse output diff --git a/spec/eth/solidity_spec.rb b/spec/eth/solidity_spec.rb index a92759c6..4663f737 100644 --- a/spec/eth/solidity_spec.rb +++ b/spec/eth/solidity_spec.rb @@ -15,7 +15,7 @@ result = solc.compile contract expect(result.keys).to eq ["Dummy"] expect(result["Dummy"]["abi"]).to eq JSON.parse '[{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]' - expect(result["Dummy"]["bin"]).to start_with "608060405234801561001057600080fd5b50" + expect(result["Dummy"]["bin"]).to start_with "608080604052" end it "compiles the greeter contract" do @@ -24,8 +24,8 @@ expect(result.keys).to eq ["Greeter", "Mortal"] expect(result["Mortal"]["abi"]).to eq JSON.parse '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"}]' expect(result["Greeter"]["abi"]).to eq JSON.parse '[{"inputs":[{"internalType":"string","name":"message","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type": "function"}]' - expect(result["Mortal"]["bin"]).to start_with "6080604052348015600f57600080fd5b5060" - expect(result["Greeter"]["bin"]).to start_with "608060405234801561001057600080fd5b5060" + expect(result["Mortal"]["bin"]).to start_with "608080604052" + expect(result["Greeter"]["bin"]).to start_with "6080604052" end it "deploys an ethereum-consensus deposit contract" do @@ -34,7 +34,7 @@ result = solc.compile contract expect(result["DepositContract"]).to be payload = result["DepositContract"]["bin"] - expect(payload).to start_with "60806040523480156200001157600080fd5b5060" + expect(payload).to start_with "604060808152" params = { from: geth.default_account, priority_fee: 0, From 041aa92e50d59663b410698601dc9c92a6ccb23f Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Wed, 10 May 2023 13:28:53 +0200 Subject: [PATCH 12/39] eth/client: remove default gas limit attribute (#235) --- lib/eth/client.rb | 14 ++++---------- spec/eth/client_spec.rb | 1 - 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/eth/client.rb b/lib/eth/client.rb index 9904942c..5d003fc5 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -34,19 +34,15 @@ class Client # The default transaction max fee per gas in Wei, defaults to {Tx::DEFAULT_GAS_PRICE}. attr_accessor :max_fee_per_gas - # The default gas limit for the transaction, defaults to {Tx::DEFAULT_GAS_LIMIT}. - attr_accessor :gas_limit - # A custom error type if a contract interaction fails. class ContractExecutionError < StandardError; end # Creates a new RPC-Client, either by providing an HTTP/S host or # an IPC path. Supports basic authentication with username and password. # - # **Note**, this sets the folling gas defaults: {Tx::DEFAULT_PRIORITY_FEE}, - # {Tx::DEFAULT_GAS_PRICE}, and {Tx::DEFAULT_GAS_LIMIT}. Use - # {#max_priority_fee_per_gas}, {#max_fee_per_gas}, and {#gas_limit} to set - # custom values prior to submitting transactions. + # **Note**, this sets the folling gas defaults: {Tx::DEFAULT_PRIORITY_FEE} + # and {Tx::DEFAULT_GAS_PRICE. Use {#max_priority_fee_per_gas} and + # {#max_fee_per_gas} to set custom values prior to submitting transactions. # # @param host [String] either an HTTP/S host or an IPC path. # @return [Eth::Client::Ipc] an IPC client. @@ -64,7 +60,6 @@ def initialize(_) @id = 0 @max_priority_fee_per_gas = Tx::DEFAULT_PRIORITY_FEE @max_fee_per_gas = Tx::DEFAULT_GAS_PRICE - @gas_limit = Tx::DEFAULT_GAS_LIMIT end # Gets the default account (coinbase) of the connected client. @@ -144,7 +139,7 @@ def transfer(destination, amount, **kwargs) params = { value: amount, to: destination, - gas_limit: gas_limit, + gas_limit: Tx::DEFAULT_GAS_LIMIT, chain_id: chain_id, } send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce]) @@ -252,7 +247,6 @@ def deploy(contract, *args, **kwargs) # @param *args optional function arguments. # @param **sender_key [Eth::Key] the sender private key. # @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559). - # @param **gas_limit [Integer] optional gas limit override for calling the contract. # @return [Object] returns the result of the call. def call(contract, function, *args, **kwargs) func = contract.functions.select { |func| func.name == function } diff --git a/spec/eth/client_spec.rb b/spec/eth/client_spec.rb index cb8e95ca..f2861681 100644 --- a/spec/eth/client_spec.rb +++ b/spec/eth/client_spec.rb @@ -60,7 +60,6 @@ expect(geth_ipc.default_account).to be_instance_of Address expect(geth_ipc.max_priority_fee_per_gas).to eq Tx::DEFAULT_PRIORITY_FEE expect(geth_ipc.max_fee_per_gas).to eq Tx::DEFAULT_GAS_PRICE - expect(geth_ipc.gas_limit).to eq Tx::DEFAULT_GAS_LIMIT end it "http can query basic methods" do From 92e6c5463df26b4493b448dba1ecca965ae7b484 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Fri, 26 May 2023 15:05:07 +0200 Subject: [PATCH 13/39] eth/tx: update tx initcode cost for shanghai (#237) * eth/tx: update tx initcode cost for shanghai * ci: patch geth * ci: patch geth * ci: patch geth * spec: update ethereum/test fixtures --- .github/workflows/spec.yml | 9 ++++++-- lib/eth/client.rb | 2 +- lib/eth/tx.rb | 11 +++++++-- spec/eth/tx/eip2930_spec.rb | 20 ++++++++-------- spec/eth/tx/legacy_spec.rb | 44 ++++++++++++++++++------------------ spec/eth/tx_spec.rb | 13 ++++++++--- spec/fixtures/ethereum/tests | 2 +- 7 files changed, 60 insertions(+), 41 deletions(-) diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index c2db7b1d..023d9547 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -37,10 +37,15 @@ jobs: sudo apt-get update sudo apt-get install ethereum solc libyaml-dev if: startsWith(matrix.os, 'Ubuntu') + - name: Patch Geth # https://github.com/ethereum/go-ethereum/pull/27360 + run: | + git clone https://github.com/q9f/go-ethereum.git -b q9f/params/shanghai + pushd go-ethereum/ + make geth + popd - name: Run Geth run: | - geth --dev --http --ipcpath /tmp/geth.ipc & - disown & + ./go-ethereum/build/bin/geth --dev --http --ipcpath /tmp/geth.ipc & - name: Gem Dependencies run: | git submodule update --init diff --git a/lib/eth/client.rb b/lib/eth/client.rb index 5d003fc5..b75bccad 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -293,7 +293,7 @@ def transact(contract, function, *args, **kwargs) gas_limit = if kwargs[:gas_limit] kwargs[:gas_limit] else - Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS + Tx.estimate_intrinsic_gas(contract.bin) end fun = contract.functions.select { |func| func.name == function }[0] params = { diff --git a/lib/eth/tx.rb b/lib/eth/tx.rb index 196431cd..500255f3 100644 --- a/lib/eth/tx.rb +++ b/lib/eth/tx.rb @@ -51,6 +51,9 @@ class ParameterError < TypeError; end # The calldata gas cost of a zero byte. COST_ZERO_BYTE = 4.freeze + # The initcode gas cost for each word (32 bytes). + COST_INITCODE_WORD = 2.freeze + # The access list gas cost of a storage key as per EIP-2930. COST_STORAGE_KEY = 1_900.freeze @@ -156,7 +159,7 @@ def unsigned_copy(tx) end # Estimates intrinsic gas for provided call data (EIP-2028) and - # access lists (EIP-2930). + # access lists (EIP-2930). Respects initcode word cost (EIP-3860). # # @param data [String] the call data. # @param list [Array] the access list. @@ -173,6 +176,10 @@ def estimate_intrinsic_gas(data = "", list = []) # count non-zero bytes none = data.size - zero gas += none * COST_NON_ZERO_BYTE + + # count "words" as per EIP-3860 + word_count = (data.length.to_f / 32.0).ceil + gas += word_count * COST_INITCODE_WORD end unless list.nil? or list.empty? list.each do |entry| @@ -187,7 +194,7 @@ def estimate_intrinsic_gas(data = "", list = []) end end end - return gas + return gas.to_i end # Validates the common transaction fields such as nonce, gas limit, diff --git a/spec/eth/tx/eip2930_spec.rb b/spec/eth/tx/eip2930_spec.rb index 08049b23..4eefb7e1 100644 --- a/spec/eth/tx/eip2930_spec.rb +++ b/spec/eth/tx/eip2930_spec.rb @@ -248,7 +248,7 @@ sample = Tx.new({ nonce: 0, gas_price: 0x0BA43B7400, - gas_limit: 0x073a0, + gas_limit: 0x07b50, to: "0x7917bc33eea648809c285607579c9919fb864f8f", value: 0x03BAF82D03A000, access_list: list, @@ -257,8 +257,8 @@ expected_address = Address.new "8d900bfa2353548a4631be870f99939575551b60" # a secp256k1 signature over keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])) - expected_sign_data = "01f89d0180850ba43b74008273a0947917bc33eea648809c285607579c9919fb864f8f8703baf82d03a00080f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c0" - expected_sign_hash = "c129e3830bdfca2973a26d718b92b5f10564b2f57a02fa0f3888de3273d5b974" + expected_sign_data = "01f89d0180850ba43b7400827b50947917bc33eea648809c285607579c9919fb864f8f8703baf82d03a00080f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c0" + expected_sign_hash = "81f7bf86cf365f50ef2a9663c4f3001c22394ccd891cd1be8ef59af94d2b1b45" # first byte is type 01 as per EIP-2930 expect(Util.bin_to_hex (sample.unsigned_encoded)[0, 1]).to eq "01" @@ -288,15 +288,15 @@ -4153010759215853346544872368790226810347211436084119296615430562753409734914, ] } - subject(:expected_hex) { "01f90181018001827b448080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d2100000000000000000000000000000000000000f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c001a047abf8694b14a2d84b5b8e0d8270e79f7772ac71419f5770ce7318bfaae952dba0141d4c6e7cad5af300a1c90924704394a09f9f3476b7c1a219ee799ffb91e765" } - subject(:expected_hash) { "800a8f4816dffee600600cd36f202b14e2d802ae7369aafae9ecb0e5c4906cbc" } + subject(:expected_hex) { "01f90181018001827b508080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d2100000000000000000000000000000000000000f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c080a001101750fe4fc89a413e65c7e9ef459180742ef68bda055c35761c84302cad13a037dd31afd4c84fb46fec9120934247662399aa053ce7b5b724275239b15ee3ad" } + subject(:expected_hash) { "3559540a9e9a228a240643f2f3b3823e5c8ffa41a5d8f37907ea91d50ef59979" } it "can create transactions with binary data" do abi = Abi.encode types, args some = Tx.new({ nonce: 0, gas_price: 1, - gas_limit: 31_556, + gas_limit: 31_568, data: abi, access_list: list, }) @@ -322,7 +322,7 @@ some = Tx.new({ nonce: 0, gas_price: 1, - gas_limit: 31_556, + gas_limit: 31_568, data: hex, access_list: list, }) @@ -349,13 +349,13 @@ some = Tx.new({ nonce: 0, gas_price: 1, - gas_limit: 29_808, + gas_limit: 29_810, data: lorem, access_list: list, }) some.sign cow - expect(some.hex).to eq "01f8cd01800182747080808d4c6f72656d2c20497073756d21f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c080a0aa8fb1b77d26ee25034e7012fd5098c2ffb46ec26f852f492adf38d0ce4480a3a019b93db449c5320f237d6ffa2612055afb8b4286c5f7fe9123c78a287b61af91" - expect(some.hash).to eq "8a3ac899feaf2260701cd92fb094924b0453552286e867f56c4d0c41d1214c25" + expect(some.hex).to eq "01f8cd01800182747280808d4c6f72656d2c20497073756d21f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c080a017c081b8c851fa7df558054e93539f20b0937aafed06586a2ad59824d52a6f55a00f40033eec8ff5ce9d37270a4170a5d2349142d1f5d0df5064439f60ee6c78be" + expect(some.hash).to eq "52d5c664c2c2da1970f192a2bc8097f22a77035fceef26a9bd00a00473fe035b" # expect to match both decoded transaction and decoded abi other = Tx.decode some.hex diff --git a/spec/eth/tx/legacy_spec.rb b/spec/eth/tx/legacy_spec.rb index 16b4438e..72459804 100644 --- a/spec/eth/tx/legacy_spec.rb +++ b/spec/eth/tx/legacy_spec.rb @@ -12,22 +12,22 @@ } subject(:cow) { Key.new(priv: Util.keccak256("cow")) } - # ref https://goerli.etherscan.io/tx/0x1975df4e715ce4af46c604e3fafb763a51cc133a42e43566779b4d5608bb4af1 + # ref https://sepolia.etherscan.io/tx/0x7613b4de482fcff616e11907d16ddba1aa950a020ec58e99ab28ba0c5926ec53 subject(:legacy) { Tx.new( { nonce: 1, gas_price: 40 * Unit::GWEI, - gas_limit: 21576, + gas_limit: 21580, to: "0xcaa29806044a08e533963b2e573c1230a2cd9a2d", value: BigDecimal("0.123456789012345678") * Unit::ETHER, data: "Lorem Ipsum Ruby Ethereum Test 1-2-3", }, - Chain::GOERLI + Chain::SEPOLIA ) } - # ref https://goerli.etherscan.io/tx/0x047e319fc8e587a77f6e9a13c30d90b5a741d93e8b35a54b12c91d6149eda359 + # ref https://sepolia.etherscan.io/tx/0x01fa6584df6326dc503ca809c9c4643cd753ba8ae63473a9994770f403cda447 subject(:ruby) { Tx.new( { @@ -38,12 +38,12 @@ value: 0.0069 * Unit::ETHER, data: "Foo Bar Ruby Ethereum", }, - Chain::GOERLI + Chain::SEPOLIA ) } - # ref https://goerli.etherscan.io/address/0x4762119a7249823d18aec7eab73258b2d5061dd8 - subject(:testnet) { Key.new(priv: "0xc6c633f85d3f9a4705623b1d9bd1122a1a9196cd53dd352505e895fcbb8452ef") } + # ref https://sepolia.etherscan.io/address/0xc3c8fd0f04b629c5e2297b79c54dd57b85a721e3 + subject(:testnet) { Key.new(priv: "0xa0d1f18547caa1fb5c121c862d8ac66d9d6afe0afa79b291ca197e64fdccfd23") } describe ".decode" do it "decodes the first mainnet transaction" do @@ -76,11 +76,11 @@ expect(tx.hash).to eq expected_hash end - it "decodes a known goerli transaction signed by ruby eth gem" do + it "decodes a known sepolia transaction signed by ruby eth gem" do - # ref https://goerli.etherscan.io/getRawTx?tx=0x047e319fc8e587a77f6e9a13c30d90b5a741d93e8b35a54b12c91d6149eda359 - expected_hex = "0xf880038509c76524008259d894caa29806044a08e533963b2e573c1230a2cd9a2d8718838370f3400095466f6f20426172205275627920457468657265756d2ea0a0133bf9a770032e18a2ce0eda0d8562abbd88920d696d02373e901967f9956da075e6ce3e86db8391524a7dff0331e90c2bf18cedfbd4164f177a86c53e5be4fa" - expected_hash = "0x047e319fc8e587a77f6e9a13c30d90b5a741d93e8b35a54b12c91d6149eda359" + # ref https://sepolia.etherscan.io/getRawTx?tx=0x01fa6584df6326dc503ca809c9c4643cd753ba8ae63473a9994770f403cda447 + expected_hex = "0xf884038509c76524008259d894caa29806044a08e533963b2e573c1230a2cd9a2d8718838370f3400095466f6f20426172205275627920457468657265756d8401546d72a0980cf5d1c20ed6a44a57b4ec70301da608247013be59be740e471579a16b9963a053a2e3b95f666c38ec26c0be6105affd23c037c67277ed920aae79f93c020fa6" + expected_hash = "0x01fa6584df6326dc503ca809c9c4643cd753ba8ae63473a9994770f403cda447" decoded = Tx.decode(expected_hex) expect(decoded.hex).to eq Util.remove_hex_prefix expected_hex expect(decoded.hash).to eq Util.remove_hex_prefix expected_hash @@ -167,7 +167,7 @@ it "it does not sign a transaction twice" do expect { legacy.hash }.to raise_error StandardError, "Transaction is not signed!" - expect(testnet.address.to_s).to eq "0x4762119a7249823D18aec7EAB73258B2D5061Dd8" + expect(testnet.address.to_s).to eq "0xC3c8Fd0f04B629c5E2297b79c54DD57b85A721e3" legacy.sign(testnet) expect { legacy.sign(testnet) }.to raise_error StandardError, "Transaction is already signed!" end @@ -198,7 +198,7 @@ it "encodes a known goerli transaction" do expect { legacy.encoded }.to raise_error StandardError, "Transaction is not signed!" legacy.sign(testnet) - expect(legacy.encoded).to eq "\xF8\x90\x01\x85\tP/\x90\x00\x82TH\x94\xCA\xA2\x98\x06\x04J\b\xE53\x96;.W<\x120\xA2\xCD\x9A-\x88\x01\xB6\x9BK\xA60\xF3N\xA4Lorem Ipsum Ruby Ethereum Test 1-2-3.\xA0\xFBM0\x8F=?\x97p\xF2e.\xF4\x0E\xA86\x9A\xB3r\xE5\x9B\xAD\x81O\xB2'\xFA\xE1\xFD\xFD\xFAM:\xA0f\xC8\xA2\xA2\xA2\xAB\xCD9\e\xAC\x869\x99Z\x10\xF1Tj\x87>\xF5\xB4R\xBF\xE5\xFC6y\x01\xD9\xF4\xAB" + expect(legacy.encoded).to eq "\xF8\x94\x01\x85\tP/\x90\x00\x82TL\x94\xCA\xA2\x98\x06\x04J\b\xE53\x96;.W<\x120\xA2\xCD\x9A-\x88\x01\xB6\x9BK\xA60\xF3N\xA4Lorem Ipsum Ruby Ethereum Test 1-2-3\x84\x01Tmr\xA0\x9Eh\x00\x9D\xBA<\x05MyVM\x82W\x86\xE9Z\xE7t\xF2\xE5\xB2\xF4\xD7_\xA5X\x88=\v#_\x82\xA02\xDCc\t\xDD\xD9)\xF5\xB1\xFC\xE3\xC9\xEC\x04\x13\aR\xF4\n}\xA2\xF2\x82\xE7x\b\xD6\x97\x93\xF6F\xEF" end end @@ -212,7 +212,7 @@ it "hexes a known goerli transaction" do expect { legacy.hex }.to raise_error StandardError, "Transaction is not signed!" legacy.sign(testnet) - expect(legacy.hex).to eq "f890018509502f900082544894caa29806044a08e533963b2e573c1230a2cd9a2d8801b69b4ba630f34ea44c6f72656d20497073756d205275627920457468657265756d205465737420312d322d332ea0fb4d308f3d3f9770f2652ef40ea8369ab372e59bad814fb227fae1fdfdfa4d3aa066c8a2a2a2abcd391bac8639995a10f1546a873ef5b452bfe5fc367901d9f4ab" + expect(legacy.hex).to eq "f894018509502f900082544c94caa29806044a08e533963b2e573c1230a2cd9a2d8801b69b4ba630f34ea44c6f72656d20497073756d205275627920457468657265756d205465737420312d322d338401546d72a09e68009dba3c054d79564d825786e95ae774f2e5b2f4d75fa558883d0b235f82a032dc6309ddd929f5b1fce3c9ec04130752f40a7da2f282e77808d69793f646ef" end end @@ -226,7 +226,7 @@ it "hashes a known goerli transaction" do expect { legacy.hash }.to raise_error StandardError, "Transaction is not signed!" legacy.sign(testnet) - expect(legacy.hash).to eq "1975df4e715ce4af46c604e3fafb763a51cc133a42e43566779b4d5608bb4af1" + expect(legacy.hash).to eq "7613b4de482fcff616e11907d16ddba1aa950a020ec58e99ab28ba0c5926ec53" end end @@ -300,15 +300,15 @@ -4153010759215853346544872368790226810347211436084119296615430562753409734914, ] } - subject(:expected_hex) { "f9010c80018259ac8080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d210000000000000000000000000000000000000026a0b06ef4fc47e80f9fc70143b558dfa31d6dae04661f37715b982718f8185aefa3a032a8b187b43ae4af3755e7158f7cbb9585485e05ffdd38fe3343767a61730026" } - subject(:expected_hash) { "3ac265b9741844e04f9ac8436469393a44d89d6fe20343fc9ea60f0f377db9ec" } + subject(:expected_hex) { "f9010c80018259b88080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d210000000000000000000000000000000000000026a081bf91ec0984c884c7d8afe094743d30c73e08c6d6c2da497dbb75193dd0cd07a059189d3f14ea3e0eccc6240e019f09d6b277e1525a0aa7e5bdca9e444f6f4630" } + subject(:expected_hash) { "045ef9de4ed1aee2274be36a92db5293063bee674ec46ad94ad4d057250db536" } it "can create transactions with binary data" do abi = Abi.encode types, args some = Tx.new({ nonce: 0, gas_price: 1, - gas_limit: 22_956, + gas_limit: 22_968, data: abi, }) @@ -332,7 +332,7 @@ some = Tx.new({ nonce: 0, gas_price: 1, - gas_limit: 22_956, + gas_limit: 22_968, data: hex, }) @@ -357,12 +357,12 @@ some = Tx.new({ nonce: 0, gas_price: 1, - gas_limit: 21_208, + gas_limit: 21_210, data: lorem, }) some.sign cow - expect(some.hex).to eq "f85880018252d880808d4c6f72656d2c20497073756d2125a08540db1627dd415d4b53f2d0b7835510ab9cfc77ad40a785e7e0dc6ec63cef79a02822730ee77dfddee349d720e3cfa955b38f7ffb9e482e4c15931bb9cca0c33c" - expect(some.hash).to eq "37aa7c93ffcbc92bcd016796ae231ea2ddce5c0daa5f5646784d3cbb9b6cff3b" + expect(some.hex).to eq "f85880018252da80808d4c6f72656d2c20497073756d2125a0121ceae143464734a25416294fd324e8a5a8578bbad84b7044a2f84b2ae0c0e8a00f72e5a58b1c40a9484026137e69a00746d613bd9445d767a7d7a305a2947e2d" + expect(some.hash).to eq "43bbce3dc6bc845bfbf052df40bfbf20086eb6f983c5aacf9a964a0cc6b1c22a" # expect to match both decoded transaction and decoded abi other = Tx.decode some.hex diff --git a/spec/eth/tx_spec.rb b/spec/eth/tx_spec.rb index 6a7b8182..1383457b 100644 --- a/spec/eth/tx_spec.rb +++ b/spec/eth/tx_spec.rb @@ -49,15 +49,22 @@ it "can estimate intrinsic gas for call data" do # EIP-2028 - expect(Tx.estimate_intrinsic_gas "Lorem, Ipsum!").to eq 21_208 - expect(Tx.estimate_intrinsic_gas "bf010c80018252088080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d210000000000000000000000000000000000000026a0e06be7a71c58beebfae09372083865f49fbacb6dfd93f10329f2ca925057fba3a0036c90afd27ea5d2383e319f7091aa23d3e77b09114d7e1d610d04dce8e8169f", []).to eq 24_220 + expect(Tx.estimate_intrinsic_gas "Lorem, Ipsum!").to eq 21_210 + expect(Tx.estimate_intrinsic_gas "bf010c80018252088080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d210000000000000000000000000000000000000026a0e06be7a71c58beebfae09372083865f49fbacb6dfd93f10329f2ca925057fba3a0036c90afd27ea5d2383e319f7091aa23d3e77b09114d7e1d610d04dce8e8169f", []).to eq 24_238 end it "can estimate intrinsic gas for access lists" do # EIP-2930 expect(Tx.estimate_intrinsic_gas "", list).to eq 29_600 - expect(Tx.estimate_intrinsic_gas "bf010c80018252088080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d210000000000000000000000000000000000000026a0e06be7a71c58beebfae09372083865f49fbacb6dfd93f10329f2ca925057fba3a0036c90afd27ea5d2383e319f7091aa23d3e77b09114d7e1d610d04dce8e8169f", list).to eq 32_820 + expect(Tx.estimate_intrinsic_gas "bf010c80018252088080b8c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000003ea1e26a2119b038eaf9b27e65cdb401502ae7a43d8bfb1368aee2693eb325af9f81244b19304b087b4941a1e892da50bd48dfe1f6d17aad7aff1c87e8481f30395a1595a07b483032affed044e698bf7c43a6fe000000000000000000000000000000000000000000000000000000000000000d4c6f72656d2c20497073756d210000000000000000000000000000000000000026a0e06be7a71c58beebfae09372083865f49fbacb6dfd93f10329f2ca925057fba3a0036c90afd27ea5d2383e319f7091aa23d3e77b09114d7e1d610d04dce8e8169f", list).to eq 32_838 + end + + it "can estimate intrinsic gas for initcode" do + + # EIP-3860 + expect(Tx.estimate_intrinsic_gas "5f").to eq 21_018 + expect(Tx.estimate_intrinsic_gas "60406080815234610104575f5b601f8110610022575051610be090816101098239f35b6020808210156100c0576021820154835182810182905280850191909152838152606080820191906001600160401b038311828410176100f05782865281515f5b8181106100de5750918493915f8094830191820152039060025afa156100d4575f5190600183018084116100ac5710156100c05760228201555f1981146100ac5760010161000c565b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b82513d5f823e3d90fd5b83810180870151908401528501610063565b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe6080604081815260049182361015610015575f80fd5b5f92833560e01c91826301ffc9a7146102685750816322895118146101ea57508063621fd130146101a95763c5f2892f1461004e575f80fd5b346101a557816003193601126101a557818060209384918483548084905b8682106100f4575050906100c4605861009267ffffffffffffffff6100d4969516610ab1565b876100af85519586938385019889528051938492860191016102f1565b8101878b820152036038810184520182610389565b86519283928392519283916102f1565b8101039060025afa156100e957519051908152f35b9051903d90823e3d90fd5b92509294909360019586808516145f1461016a57610138908554908a51908582019283528b8201528a815261012881610359565b8a519283928392519283916102f1565b8101039060025afa1561016057610154908551945b1c91610337565b8492869188959361006c565b85513d86823e3d90fd5b61018c9085602101548a51908582019283528b8201528a815261012881610359565b8101039060025afa15610160576101549085519461014d565b5080fd5b50346101a557816003193601126101a5576101e6906101d367ffffffffffffffff60205416610ab1565b9051918291602083526020830190610312565b0390f35b839060803660031901126101a55767ffffffffffffffff81358181116102645761021790369084016102bf565b6024929192358281116102605761023190369086016102bf565b909260443590811161025c576102599561024d913691016102bf565b939092606435956103cb565b80f35b8680fd5b8580fd5b8380fd5b8491346102bb5760203660031901126102bb573563ffffffff60e01b81168091036102bb57602092506301ffc9a760e01b81149081156102aa575b5015158152f35b638564090760e01b149050836102a3565b8280fd5b9181601f840112156102ed5782359167ffffffffffffffff83116102ed57602083818601950101116102ed57565b5f80fd5b5f5b8381106103025750505f910152565b81810151838201526020016102f3565b9060209161032b815180928185528580860191016102f1565b601f01601f1916010190565b5f1981146103455760010190565b634e487b7160e01b5f52601160045260245ffd5b6060810190811067ffffffffffffffff82111761037557604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761037557604052565b908060209392818452848401375f828201840152601f01601f1916010190565b919694929695939560308203610a5d576020978881036109f957606085036109a257670de0b6b3a7640000341061094e57633b9aca008034066108ed5734049767ffffffffffffffff988981116108985790898b96959493921661042e90610ab1565b9186549a8b1661043d90610ab1565b976040988951809160a0825260a08201610458908a8c6103ab565b8281038c84015261046a90868a6103ab565b8281038d84015261047b9088610312565b828103606084015261048e9085886103ab565b828103608084015261049f91610312565b037f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c591a188519c868e97828901998a3787015f9e8f98828a8581950152036010810182526030016104f09082610389565b8b51998a9151809b610501926102f1565b8060029a810103908a5afa1561088457855192818a1161025c57888761054c8c8051848101918087843784606083015280825261053d82610359565b519283928392519283916102f1565b810103908b5afa1561088e578661059d8b8b9361058d84519683519287840194603f19830191018537820182601f1991878382015203908101835282610389565b8d519283928392519283916102f1565b810103908a5afa1561088457856105da899282518c51908582019283528d8201528c81526105ca81610359565b8c519283928392519283916102f1565b81010390895afa1561087a57610618879286926101288b8551988382519485928a840197885284840137810187838201520387810184520182610389565b81010390875afa156108705782610675869282519461066560588b5180938861064a81840197888151938492016102f1565b820190888a8301526038820152036038810184520182610389565b89519283928392519283916102f1565b81010390865afa1561086457906106b284928251875190858201928352888201528781526106a281610359565b87519283928392519283916102f1565b81010390845afa1561085a5786519384036107d45763ffffffff861015610787576001958681018091116107735780835587945b83861061070157634e487b7160e01b89526004889052602489fd5b8780831614610767578861073c85928854908851908582019283528982015288815261072c81610359565b88519283928392519283916102f1565b81010390855afa1561075d57610756885191881c95610337565b94906106e6565b83513d89823e3d90fd5b96505050505090925055565b634e487b7160e01b88526011600452602488fd5b50608491519062461bcd60e51b82526004820152602160248201527f4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6044820152601b60fa1b6064820152fd5b5060a491519062461bcd60e51b82526004820152605460248201527f4465706f736974436f6e74726163743a207265636f6e7374727563746564204460448201527f65706f7369744461746120646f6573206e6f74206d6174636820737570706c6960648201527319590819195c1bdcda5d17d9185d1857dc9bdbdd60621b6084820152fd5b82513d88823e3d90fd5b508351903d90823e3d90fd5b85513d84823e3d90fd5b87513d86823e3d90fd5b88513d87823e3d90fd5b89513d88823e3d90fd5b60405162461bcd60e51b8152600481018c9052602760248201527f4465706f736974436f6e74726163743a206465706f7369742076616c756520746044820152660dede40d0d2ced60cb1b6064820152608490fd5b60405162461bcd60e51b8152600481018b9052603360248201527f4465706f736974436f6e74726163743a206465706f7369742076616c7565206e6044820152726f74206d756c7469706c65206f66206777656960681b6064820152608490fd5b60405162461bcd60e51b8152600481018a9052602660248201527f4465706f736974436f6e74726163743a206465706f7369742076616c756520746044820152656f6f206c6f7760d01b6064820152608490fd5b60405162461bcd60e51b8152600481018a9052602960248201527f4465706f736974436f6e74726163743a20696e76616c6964207369676e6174756044820152680e4ca40d8cadccee8d60bb1b6064820152608490fd5b60405162461bcd60e51b8152600481018a9052603660248201527f4465706f736974436f6e74726163743a20696e76616c696420776974686472616044820152750eec2d8bec6e4cac8cadce8d2c2d8e640d8cadccee8d60531b6064820152608490fd5b60405162461bcd60e51b815260206004820152602660248201527f4465706f736974436f6e74726163743a20696e76616c6964207075626b6579206044820152650d8cadccee8d60d31b6064820152608490fd5b906040516040810181811067ffffffffffffffff8211176103755760405260088152602081016020368237819367ffffffffffffffff60c01b9060c01b1690825115610b96578160071a9053815160011015610b96578060061a6021830153815160021015610b96578060051a6022830153815160031015610b9657600481811a60238401538251811015610b83578160031a6024840153825160051015610b83578160021a6025840153825160061015610b83578160011a6026840153825160071015610b8357505f1a9060270153565b603290634e487b7160e01b5f525260245ffd5b634e487b7160e01b5f52603260045260245ffdfea26469706673582212200584f7fb382c7d9bfcca41cab149ea1be65871bb543ccee53cc9834690ff28a764736f6c63430008140033").to eq 73_800 end end diff --git a/spec/fixtures/ethereum/tests b/spec/fixtures/ethereum/tests index 38d5c932..e4fc8f6c 160000 --- a/spec/fixtures/ethereum/tests +++ b/spec/fixtures/ethereum/tests @@ -1 +1 @@ -Subproject commit 38d5c93203e84da1611739b4736ca816cb193407 +Subproject commit e4fc8f6cabec7cab0a7a92f8ba3a00a450b2b90b From 4b09acc776e794811d373776c940349db5ad5e39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 12:54:47 +0200 Subject: [PATCH 14/39] build(deps): bump JamesIves/github-pages-deploy-action (#240) Bumps [JamesIves/github-pages-deploy-action](https://github.com/JamesIves/github-pages-deploy-action) from 4.4.1 to 4.4.2. - [Release notes](https://github.com/JamesIves/github-pages-deploy-action/releases) - [Commits](https://github.com/JamesIves/github-pages-deploy-action/compare/v4.4.1...v4.4.2) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index caf42cd0..921fb7d7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: gem install yard yard doc - name: Deploy GH Pages - uses: JamesIves/github-pages-deploy-action@v4.4.1 + uses: JamesIves/github-pages-deploy-action@v4.4.2 with: branch: gh-pages folder: doc/ From 6f19a287c22e9d65e55010844fb4e33dc9584b4f Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:00:37 +0200 Subject: [PATCH 15/39] eth/abi: allow encoding address types (#242) * eth/abi: allow encoding address types * eth/abi: add test --- lib/eth/abi/encoder.rb | 10 +++++++--- spec/eth/abi/encoder_spec.rb | 1 + spec/fixtures/ethereum/tests | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index b1aa9612..eb7d4d62 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -257,7 +257,11 @@ def hash(arg, type) # Properly encodes addresses. def address(arg) - if arg.is_a? Integer + if arg.is_a? Address + + # from checksummed address with 0x prefix + Util.zpad_hex arg.to_s[2..-1] + elsif arg.is_a? Integer # address from integer Util.zpad_int arg @@ -267,11 +271,11 @@ def address(arg) Util.zpad arg, 32 elsif arg.size == 40 - # address from hexadecimal address with 0x prefix + # address from hexadecimal address Util.zpad_hex arg elsif arg.size == 42 and arg[0, 2] == "0x" - # address from hexadecimal address + # address from hexadecimal address with 0x prefix Util.zpad_hex arg[2..-1] else raise EncodingError, "Could not parse address: #{arg}" diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 95cfd629..79e68d66 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -50,6 +50,7 @@ expect(Abi::Encoder.type t_address, "\xff" * 20).to eq Util.zpad("\xff" * 20, 32) expect(Abi::Encoder.type t_address, "ff" * 20).to eq Util.zpad("\xff" * 20, 32) expect(Abi::Encoder.type t_address, "0x" + "ff" * 20).to eq Util.zpad("\xff" * 20, 32) + expect(Abi::Encoder.type t_address, Address.new("0x" + "ff" * 20)).to eq Util.zpad("\xff" * 20, 32) end it "can encode primitive types" do diff --git a/spec/fixtures/ethereum/tests b/spec/fixtures/ethereum/tests index e4fc8f6c..0ec53d02 160000 --- a/spec/fixtures/ethereum/tests +++ b/spec/fixtures/ethereum/tests @@ -1 +1 @@ -Subproject commit e4fc8f6cabec7cab0a7a92f8ba3a00a450b2b90b +Subproject commit 0ec53d024c2b1f5175e70aba8f750cd4d37d9999 From 723977c56c923577b8a7922119f894e69ecd93f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:58:44 +0200 Subject: [PATCH 16/39] build(deps): bump JamesIves/github-pages-deploy-action (#244) Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.2...v4.4.3) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 921fb7d7..e6a18e0f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: gem install yard yard doc - name: Deploy GH Pages - uses: JamesIves/github-pages-deploy-action@v4.4.2 + uses: JamesIves/github-pages-deploy-action@v4.4.3 with: branch: gh-pages folder: doc/ From 031652b7397e0c0f448f4a9cb8678695665b34ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Pie=C5=84kowski?= Date: Thu, 3 Aug 2023 16:59:00 +0200 Subject: [PATCH 17/39] Fix the decoding of unsigned transactions (#243) * Recover sender address only for signed transactions * Add a test case for decoding an unsigned transaction * Add a test case for an EIP-2930 transaction --- lib/eth/tx/eip1559.rb | 15 ++++++++++----- lib/eth/tx/eip2930.rb | 15 ++++++++++----- lib/eth/tx/legacy.rb | 2 -- spec/eth/tx_spec.rb | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/lib/eth/tx/eip1559.rb b/lib/eth/tx/eip1559.rb index a8800beb..a1ea1c19 100644 --- a/lib/eth/tx/eip1559.rb +++ b/lib/eth/tx/eip1559.rb @@ -186,11 +186,16 @@ def decode(hex) # last but not least, set the type. @type = TYPE_1559 - # recover sender address - v = Chain.to_v recovery_id, chain_id - public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id) - address = Util.public_key_to_address(public_key).to_s - @sender = Tx.sanitize_address address + unless recovery_id.nil? + # recover sender address + v = Chain.to_v recovery_id, chain_id + public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id) + address = Util.public_key_to_address(public_key).to_s + @sender = Tx.sanitize_address address + else + # keep the 'from' field blank + @sender = Tx.sanitize_address nil + end end # Creates an unsigned copy of a transaction payload. diff --git a/lib/eth/tx/eip2930.rb b/lib/eth/tx/eip2930.rb index eec8a8ac..60e857ab 100644 --- a/lib/eth/tx/eip2930.rb +++ b/lib/eth/tx/eip2930.rb @@ -181,11 +181,16 @@ def decode(hex) # last but not least, set the type. @type = TYPE_2930 - # recover sender address - v = Chain.to_v recovery_id, chain_id - public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id) - address = Util.public_key_to_address(public_key).to_s - @sender = Tx.sanitize_address address + unless recovery_id.nil? + # recover sender address + v = Chain.to_v recovery_id, chain_id + public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id) + address = Util.public_key_to_address(public_key).to_s + @sender = Tx.sanitize_address address + else + # keep the 'from' field blank + @sender = Tx.sanitize_address nil + end end # Creates an unsigned copy of a transaction payload. diff --git a/lib/eth/tx/legacy.rb b/lib/eth/tx/legacy.rb index 8ed9d64f..14b89023 100644 --- a/lib/eth/tx/legacy.rb +++ b/lib/eth/tx/legacy.rb @@ -154,13 +154,11 @@ def decode(hex) _set_signature(v, r, s) unless chain_id.nil? - # recover sender address public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v}", chain_id) address = Util.public_key_to_address(public_key).to_s @sender = Tx.sanitize_address address else - # keep the 'from' field blank @sender = Tx.sanitize_address nil end diff --git a/spec/eth/tx_spec.rb b/spec/eth/tx_spec.rb index 1383457b..745fd64a 100644 --- a/spec/eth/tx_spec.rb +++ b/spec/eth/tx_spec.rb @@ -122,4 +122,32 @@ expect(tx.chain_id).to eq 56 end end + + describe '.decode an unsigned transaction' do + context 'EIP-1559' do + it "keeps the 'from' field blank" do + raw = "0x02f0050584b2d05e00851010b872008303841494caedbd63fb25c3126bfe96c1af208e4688e9817e87f6a3d9c63df00080c0" + tx = Tx.decode raw + + expect(tx.signature_y_parity).to eq nil + expect(tx.signature_r).to eq 0 + expect(tx.signature_s).to eq 0 + + expect(tx.sender).to eq "" + end + end + + context 'EIP-2930' do + it "keeps the 'from' field blank" do + raw = "01eb050585037e11d6008303841494caedbd63fb25c3126bfe96c1af208e4688e9817e87f6a3d9c63df00080c0" + tx = Tx.decode raw + + expect(tx.signature_y_parity).to eq nil + expect(tx.signature_r).to eq 0 + expect(tx.signature_s).to eq 0 + + expect(tx.sender).to eq "" + end + end + end end From ececa1254d79b11e2eec4de741993f936bdc1ee0 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:28:02 +0200 Subject: [PATCH 18/39] spec: run rufo (#245) --- spec/eth/tx_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/eth/tx_spec.rb b/spec/eth/tx_spec.rb index 745fd64a..0aceeefb 100644 --- a/spec/eth/tx_spec.rb +++ b/spec/eth/tx_spec.rb @@ -123,8 +123,8 @@ end end - describe '.decode an unsigned transaction' do - context 'EIP-1559' do + describe ".decode an unsigned transaction" do + context "EIP-1559" do it "keeps the 'from' field blank" do raw = "0x02f0050584b2d05e00851010b872008303841494caedbd63fb25c3126bfe96c1af208e4688e9817e87f6a3d9c63df00080c0" tx = Tx.decode raw @@ -137,7 +137,7 @@ end end - context 'EIP-2930' do + context "EIP-2930" do it "keeps the 'from' field blank" do raw = "01eb050585037e11d6008303841494caedbd63fb25c3126bfe96c1af208e4688e9817e87f6a3d9c63df00080c0" tx = Tx.decode raw From 523802c9be2ef0507239bddbe2e5fe597fe3a7cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:39:00 +0200 Subject: [PATCH 19/39] build(deps): bump actions/checkout from 3 to 4 (#246) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/spec.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 93044259..3cd92f47 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: - ruby steps: - name: "Checkout repository" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Initialize CodeQL" uses: github/codeql-action/init@v2 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e6a18e0f..5a463d36 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,7 +10,7 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.0' diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index 023d9547..18b1424c 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -21,7 +21,7 @@ jobs: os: [ubuntu-22.04, macos-12] ruby: ['3.0', '3.1', '3.2'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} From 4e6dec8cb2f660190d01593a8bea0a207191c696 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:45:44 +0200 Subject: [PATCH 20/39] ci: unpatch geth (#248) --- .github/workflows/spec.yml | 9 ++------- spec/eth/client_spec.rb | 6 +++--- spec/fixtures/ethereum/tests | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index 18b1424c..1b54754c 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -37,15 +37,10 @@ jobs: sudo apt-get update sudo apt-get install ethereum solc libyaml-dev if: startsWith(matrix.os, 'Ubuntu') - - name: Patch Geth # https://github.com/ethereum/go-ethereum/pull/27360 - run: | - git clone https://github.com/q9f/go-ethereum.git -b q9f/params/shanghai - pushd go-ethereum/ - make geth - popd - name: Run Geth run: | - ./go-ethereum/build/bin/geth --dev --http --ipcpath /tmp/geth.ipc & + geth --dev --http --ipcpath /tmp/geth.ipc & + disown & - name: Gem Dependencies run: | git submodule update --init diff --git a/spec/eth/client_spec.rb b/spec/eth/client_spec.rb index f2861681..46439e07 100644 --- a/spec/eth/client_spec.rb +++ b/spec/eth/client_spec.rb @@ -112,7 +112,7 @@ it "raises exception when nonce incorrect" do expect { geth_http.transfer(another_key.address, 69 * Unit::ETHER, legacy: true, nonce: 0) - }.to raise_error(IOError, "nonce too low") + }.to raise_error(IOError, /nonce too low: next nonce [0-9]+, tx nonce [0-9]+/) end it "funds account twice" do @@ -178,7 +178,7 @@ it "raises exception when nonce incorrect" do expect { geth_http.deploy_and_wait(contract, nonce: 0) - }.to raise_error(IOError, "nonce too low") + }.to raise_error(IOError, /nonce too low: next nonce [0-9]+, tx nonce [0-9]+/) end it "deploys the contract twice" do @@ -340,7 +340,7 @@ it "raises exception when nonce incorrect" do expect { geth_http.transact(contract, "set", 42, address: contract_address, nonce: 0) - }.to raise_error(IOError, "nonce too low") + }.to raise_error(IOError, /nonce too low: next nonce [0-9]+, tx nonce [0-9]+/) end it "transacts function twice" do diff --git a/spec/fixtures/ethereum/tests b/spec/fixtures/ethereum/tests index 0ec53d02..a33949df 160000 --- a/spec/fixtures/ethereum/tests +++ b/spec/fixtures/ethereum/tests @@ -1 +1 @@ -Subproject commit 0ec53d024c2b1f5175e70aba8f750cd4d37d9999 +Subproject commit a33949df17a1c382ffee5666e66d26bde7a089f9 From a5274152d1d9f0d26a849727156078f9ec97d6e2 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:52:51 +0100 Subject: [PATCH 21/39] eth/eip721: fix data type bug for bytes, fix #251 (#252) * eth/eip721: fix data type bug for bytes, fix #251 * spec/eip721: clean up --- lib/eth/eip712.rb | 6 +++++- spec/eth/eip712_spec.rb | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/eth/eip712.rb b/lib/eth/eip712.rb index 7d091b98..7af75d03 100644 --- a/lib/eth/eip712.rb +++ b/lib/eth/eip712.rb @@ -114,9 +114,13 @@ def encode_data(primary_type, data, types) value = data[field[:name].to_sym] type = field[:type] raise NotImplementedError, "Arrays currently unimplemented for EIP-712." if type.end_with? "]" - if type == "string" or type == "bytes" + if type == "string" encoded_types.push "bytes32" encoded_values.push Util.keccak256 value + elsif type == "bytes" + encoded_types.push "bytes32" + value = Util.hex_to_bin value + encoded_values.push Util.keccak256 value elsif !types[type.to_sym].nil? encoded_types.push "bytes32" value = encode_data type, value, types diff --git a/spec/eth/eip712_spec.rb b/spec/eth/eip712_spec.rb index 0d28498a..896eba6c 100644 --- a/spec/eth/eip712_spec.rb +++ b/spec/eth/eip712_spec.rb @@ -127,4 +127,47 @@ expect(Eip712.hash typed_data).to eq Util.hex_to_bin "0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2" end end + + context "end to end" do + it "can hash the correct data type" do + key = Key.new(priv: "0x8e589ba6280400cfa426229684f7c2ac9ebf132f7ad658a82ed57553a0a9dee8") + data = "0xa2d6eae3" + domain = { + name: "Complex Data", + version: "1", + chainId: 421613, + verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + } + + type_bytes = [{ name: "data", type: "bytes" }] + data_bytes = { + types: { + EIP712Domain: eip712_domain, + Data: type_bytes, + }, + primaryType: "Data", + domain: domain, + message: { + data: data, + }, + } + sig_bytes = "ddefbbe703f59949a87ece451321924bb9297100dda63e1f39559b72db3ec9e83dae2056c25b52ddb8bd53ab536e84d2e4f70d98219ed14e46b021a59aefb4eb1c" + expect(key.sign_typed_data data_bytes).to eq sig_bytes + + type_string = [{ name: "data", type: "string" }] + data_string = { + types: { + EIP712Domain: eip712_domain, + Data: type_string, + }, + primaryType: "Data", + domain: domain, + message: { + data: data, + }, + } + sig_string = "8255c17ce6be5fb6ee3430784a52a5163c63fc87e2dcae32251d9c49ba849fad7067454b0d7e694698c02e552fd7af283dcaadc754d58ecba978856de8742e361b" + expect(key.sign_typed_data data_string).to eq sig_string + end + end end From f8cafeaddc374e7799d13c7e1cbc93f13c60c566 Mon Sep 17 00:00:00 2001 From: flanagansteve <32844300+flanagansteve@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:11:36 -0500 Subject: [PATCH 22/39] Fix typo in contract_spec.rb (#253) "contact" -> "contract" --- spec/eth/contract_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/eth/contract_spec.rb b/spec/eth/contract_spec.rb index 909f8abf..45f7d005 100644 --- a/spec/eth/contract_spec.rb +++ b/spec/eth/contract_spec.rb @@ -58,7 +58,7 @@ expect { Contract.from_bin(name: name, abi: abi, bin: bin) }.to raise_error JSON::ParserError end - it "contact index can be specified" do + it "contract index can be specified" do file = "spec/fixtures/contracts/greeter.sol" greeter = Contract.from_file(file: file, contract_index: 0) expect(greeter).to be_instance_of(Eth::Contract::Greeter) From 9d9f9b08ff7b7e96daab9dcba1d748d872cf3738 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:32:46 +0100 Subject: [PATCH 23/39] build(deps): bump JamesIves/github-pages-deploy-action (#256) Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.3 to 4.5.0. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.3...v4.5.0) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5a463d36..4e53f786 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: gem install yard yard doc - name: Deploy GH Pages - uses: JamesIves/github-pages-deploy-action@v4.4.3 + uses: JamesIves/github-pages-deploy-action@v4.5.0 with: branch: gh-pages folder: doc/ From db8becdf8f3c8df743425c23462227e8d873b507 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 13:04:29 +0100 Subject: [PATCH 24/39] build(deps): bump github/codeql-action from 2 to 3 (#257) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3cd92f47..26d1df36 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,13 +26,13 @@ jobs: - name: "Checkout repository" uses: actions/checkout@v4 - name: "Initialize CodeQL" - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: "${{ matrix.language }}" - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: "Perform CodeQL Analysis" - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.0' From b437f591e2f4f4e93f20e98207efda7e77c3ca34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 Jun 2024 19:44:33 +0200 Subject: [PATCH 25/39] build(deps): bump JamesIves/github-pages-deploy-action (#275) Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.5.0 to 4.6.1. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.5.0...v4.6.1) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4e53f786..33cc8179 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: gem install yard yard doc - name: Deploy GH Pages - uses: JamesIves/github-pages-deploy-action@v4.5.0 + uses: JamesIves/github-pages-deploy-action@v4.6.1 with: branch: gh-pages folder: doc/ From 91693b1340c3658c3bc4fd0af80713d0905af1cf Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sun, 23 Jun 2024 20:27:10 +0200 Subject: [PATCH 26/39] eth/api: remove coinbase as default account (#269) * eth/api: remove coinbase as default account * solidity canary * solidity canary * fix codecov --- .github/workflows/spec.yml | 6 +++++- lib/eth/api.rb | 1 - lib/eth/client.rb | 16 ++++++++-------- spec/eth/solidity_spec.rb | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index 1b54754c..c0764932 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -49,5 +49,9 @@ jobs: run: | bundle exec rspec env: - COVERAGE: true INFURA_TOKEN: ${{ secrets.INFURA_TOKEN }} + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/lib/eth/api.rb b/lib/eth/api.rb index 2b6488b9..fe650154 100644 --- a/lib/eth/api.rb +++ b/lib/eth/api.rb @@ -115,7 +115,6 @@ module Api "eth_blockNumber", "eth_call", "eth_chainId", - "eth_coinbase", "eth_compileLLL", "eth_compileSerpent", "eth_compileSolidity", diff --git a/lib/eth/client.rb b/lib/eth/client.rb index b75bccad..ecd9dff6 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -25,7 +25,7 @@ class Client # The connected network's chain ID. attr_reader :chain_id - # The connected network's client coinbase. + # The connected network's client default account. attr_accessor :default_account # The default transaction max priority fee per gas in Wei, defaults to {Tx::DEFAULT_PRIORITY_FEE}. @@ -62,15 +62,15 @@ def initialize(_) @max_fee_per_gas = Tx::DEFAULT_GAS_PRICE end - # Gets the default account (coinbase) of the connected client. + # Gets the default account (first account) of the connected client. # # **Note**, that many remote providers (e.g., Infura) do not provide # any accounts. # - # @return [Eth::Address] the coinbase account address. + # @return [Eth::Address] the default account address. def default_account raise ArgumentError, "The default account is not available on remote connections!" unless local? || @default_account - @default_account ||= Address.new eth_coinbase["result"] + @default_account ||= Address.new eth_accounts["result"].first end # Gets the chain ID of the connected network. @@ -108,7 +108,7 @@ def resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type = Ens::Coin end # Simply transfer Ether to an account and waits for it to be mined. - # Uses `eth_coinbase` and external signer if no sender key is + # Uses `eth_accounts` and external signer if no sender key is # provided. # # See {#transfer} for params and overloads. @@ -119,7 +119,7 @@ def transfer_and_wait(destination, amount, **kwargs) end # Simply transfer Ether to an account without any call data or - # access lists attached. Uses `eth_coinbase` and external signer + # access lists attached. Uses `eth_accounts` and external signer # if no sender key is provided. # # **Note**, that many remote providers (e.g., Infura) do not provide @@ -179,7 +179,7 @@ def transfer_erc20(erc20_contract, destination, amount, **kwargs) end # Deploys a contract and waits for it to be mined. Uses - # `eth_coinbase` or external signer if no sender key is provided. + # `eth_accounts` or external signer if no sender key is provided. # # See {#deploy} for params and overloads. # @@ -190,7 +190,7 @@ def deploy_and_wait(contract, *args, **kwargs) contract.address = Address.new(addr).to_s end - # Deploys a contract. Uses `eth_coinbase` or external signer + # Deploys a contract. Uses `eth_accounts` or external signer # if no sender key is provided. # # **Note**, that many remote providers (e.g., Infura) do not provide diff --git a/spec/eth/solidity_spec.rb b/spec/eth/solidity_spec.rb index 4663f737..106eb2b3 100644 --- a/spec/eth/solidity_spec.rb +++ b/spec/eth/solidity_spec.rb @@ -34,7 +34,8 @@ result = solc.compile contract expect(result["DepositContract"]).to be payload = result["DepositContract"]["bin"] - expect(payload).to start_with "604060808152" + expect(payload).to start_with "60" + expect(payload).to end_with "33" params = { from: geth.default_account, priority_fee: 0, From 634b5b99922f306ba1159f9da57bdbf05a28503e Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sun, 23 Jun 2024 20:34:10 +0200 Subject: [PATCH 27/39] ci: update ruby version (#271) * ci: update ruby version * remove ruby 3.0 for eol notice --- .github/workflows/codeql.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/spec.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 26d1df36..54634bf6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,7 +35,7 @@ jobs: uses: github/codeql-action/analyze@v3 - uses: ruby/setup-ruby@v1 with: - ruby-version: '3.0' + ruby-version: '3.3' bundler-cache: true - name: "Run rufo code formatting checks" run: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 33cc8179..f33819c5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: - ruby-version: '3.0' + ruby-version: '3.3' bundler-cache: true - name: Run Yard Doc run: | diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index c0764932..ae9c6daa 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -18,8 +18,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, macos-12] - ruby: ['3.0', '3.1', '3.2'] + os: [ubuntu-latest, macos-latest] + ruby: ['3.1', '3.2', '3.3'] steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 From 502aa91f0067cc55c94656320f4e17ad417581c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=B9=8B=E5=8A=9B?= <9770+ShiningRay@users.noreply.github.com> Date: Mon, 24 Jun 2024 02:47:39 +0800 Subject: [PATCH 28/39] Support tuple params in EventLog (#276) * support tuple with dynamic fields * fix the topic hash generation for tuple params * add spec for event signature --------- Co-authored-by: Afri <58883403+q9f@users.noreply.github.com> --- lib/eth/abi/decoder.rb | 19 ++++++++ lib/eth/abi/event.rb | 17 +++++-- lib/eth/abi/type.rb | 10 ++++- lib/eth/contract/event.rb | 15 ++++++- spec/eth/abi/event_spec.rb | 1 + spec/eth/contract/event_spec.rb | 80 +++++++++++++++++++++++++++++++++ 6 files changed, 137 insertions(+), 5 deletions(-) diff --git a/lib/eth/abi/decoder.rb b/lib/eth/abi/decoder.rb index 5f104c7f..27911a60 100644 --- a/lib/eth/abi/decoder.rb +++ b/lib/eth/abi/decoder.rb @@ -51,6 +51,25 @@ def type(type, arg) type(Type.parse(type.base_type), arg[pointer + 32, Util.ceil32(data_l) + 32]) end end + elsif type.base_type == 'tuple' + offset = 0 + data = {} + type.components.each do |c| + if c.dynamic? + pointer = Util.deserialize_big_endian_to_int arg[offset, 32] # Pointer to the size of the array's element + data_len = Util.deserialize_big_endian_to_int arg[pointer, 32] # length of the element + + data[c.name] = type(c, arg[pointer, Util.ceil32(data_len) + 32]) + # puts data + offset += 32 + else + size = c.size + data[c.name] = type(c, arg[offset, size]) + offset += size + # puts data + end + end + data elsif type.dynamic? l = Util.deserialize_big_endian_to_int arg[0, 32] nested_sub = type.nested_sub diff --git a/lib/eth/abi/event.rb b/lib/eth/abi/event.rb index bf8b5b93..d0af31d8 100644 --- a/lib/eth/abi/event.rb +++ b/lib/eth/abi/event.rb @@ -116,9 +116,20 @@ def decode_logs(interfaces, logs) def decode_log(inputs, data, topics, anonymous = false) topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] } - topic_types = topic_inputs.map { |i| i["type"] } - data_types = data_inputs.map { |i| i["type"] } - + topic_types = topic_inputs.map do |i| + if i['type'] == 'tuple' + Type.parse(i['type'], i['components'], i['name']) + else + i['type'] + end + end + data_types = data_inputs.map do |i| + if i['type'] == 'tuple' + Type.parse(i['type'], i['components'], i['name']) + else + i['type'] + end + end # If event is anonymous, all topics are arguments. Otherwise, the first # topic will be the event signature. if anonymous == false diff --git a/lib/eth/abi/type.rb b/lib/eth/abi/type.rb index 62b862cd..94ea4013 100644 --- a/lib/eth/abi/type.rb +++ b/lib/eth/abi/type.rb @@ -71,7 +71,15 @@ def initialize(base_type, sub_type, dimensions, components = nil, component_name # @return [Eth::Abi::Type] a parsed Type object. # @raise [ParseError] if it fails to parse the type. def parse(type, components = nil, component_name = nil) - return type if type.is_a?(Type) + if type.is_a?(Type) + @base_type = type.base_type + @sub_type = type.sub_type + @dimensions = type.dimensions + @components = type.components + @name = type.name + return + end + _, base_type, sub_type, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a # type dimension can only be numeric diff --git a/lib/eth/contract/event.rb b/lib/eth/contract/event.rb index 727b83db..0106b3e8 100644 --- a/lib/eth/contract/event.rb +++ b/lib/eth/contract/event.rb @@ -26,7 +26,9 @@ class Contract::Event # @param data [Hash] contract event data. def initialize(data) @name = data["name"] - @input_types = data["inputs"].collect { |x| x["type"] } + @input_types = data["inputs"].collect do |x| + type_name x + end @inputs = data["inputs"].collect { |x| x["name"] } @event_string = "#{@name}(#{@input_types.join(",")})" @signature = Digest::Keccak.hexdigest(@event_string, 256) @@ -38,5 +40,16 @@ def initialize(data) def set_address(address) @address = address.nil? ? nil : Eth::Address.new(address).address end + + private + def type_name(x) + type = x["type"] + case type + when "tuple" + "(#{x['components'].collect { |c| type_name(c) }.join(',')})" + else + type + end + end end end diff --git a/spec/eth/abi/event_spec.rb b/spec/eth/abi/event_spec.rb index fe22ba4d..19ed435f 100644 --- a/spec/eth/abi/event_spec.rb +++ b/spec/eth/abi/event_spec.rb @@ -247,6 +247,7 @@ expect(signature).to eq "Transfer(address,address,uint256)" end + it "generates transfer function signature" do abi = erc20_abi.find { |i| i["type"] == "function" && i["name"] == "transfer" } signature = Abi::Event.signature(abi) diff --git a/spec/eth/contract/event_spec.rb b/spec/eth/contract/event_spec.rb index 043f0032..f5e78c91 100644 --- a/spec/eth/contract/event_spec.rb +++ b/spec/eth/contract/event_spec.rb @@ -13,5 +13,85 @@ expect(contract.events[1].signature).to eq("1f3a0e41bf4d8306f04763663bf025d1824a391571ce3a07186a195f8c4cfd3c") expect(contract.events[1].event_string).to eq("killed()") end + + it "generates signature for event with tuple params" do + event = Eth::Contract::Event.new({ + "anonymous" => false, + "inputs" => [ + { + "components" => [ + { + "internalType" => "uint256", + "name" => "topicId", + "type" => "uint256" + }, + { + "internalType" => "uint256", + "name" => "proposalId", + "type" => "uint256" + }, + { + "internalType" => "string", + "name" => "name", + "type" => "string" + }, + { + "internalType" => "string", + "name" => "symbol", + "type" => "string" + }, + { + "internalType" => "uint256", + "name" => "duration", + "type" => "uint256" + }, + { + "internalType" => "uint256", + "name" => "totalSupply", + "type" => "uint256" + }, + { + "internalType" => "uint256", + "name" => "miniStakeValue", + "type" => "uint256" + }, + { + "internalType" => "uint256", + "name" => "maxStakeValue", + "type" => "uint256" + }, + { + "internalType" => "uint256", + "name" => "maxParticipants", + "type" => "uint256" + }, + { + "internalType" => "uint256", + "name" => "whitelistIndex", + "type" => "uint256" + }, + { + "internalType" => "address", + "name" => "proposer", + "type" => "address" + }, + { + "internalType" => "bool", + "name" => "useWhitelist", + "type" => "bool" + } + ], + "indexed" => false, + "internalType" => "struct VoteContract.ProposalCreatedParams", + "name" => "params", + "type" => "tuple" + } + ], + "name" => "ProposalCreated", + "type" => "event" + }) + expect(event.event_string).to eq('ProposalCreated((uint256,uint256,string,string,uint256,uint256,uint256,uint256,uint256,uint256,address,bool))') + expect(event.signature).to eq('4449031b77cbe261580701c097fb63211e768f685581e616330dfff20493536c') + end end end From 9333e1acb2c3bc52a756d851c1823cdbc3d5219b Mon Sep 17 00:00:00 2001 From: Aki Wu Date: Mon, 24 Jun 2024 02:55:06 +0800 Subject: [PATCH 29/39] fix event signature (#250) * add struct support in event topic calc * fix event signature in contract.rb --------- Co-authored-by: Afri <58883403+q9f@users.noreply.github.com> --- lib/eth/abi/event.rb | 12 +++++++++++- lib/eth/contract/event.rb | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/eth/abi/event.rb b/lib/eth/abi/event.rb index d0af31d8..d61b429d 100644 --- a/lib/eth/abi/event.rb +++ b/lib/eth/abi/event.rb @@ -40,10 +40,20 @@ def compute_topic(interface) def signature(interface) name = interface.fetch("name") inputs = interface.fetch("inputs", []) - types = inputs.map { |i| i.fetch("type") } + types = inputs.map { |i| type(i) } "#{name}(#{types.join(",")})" end + def type(input) + if input["type"] == "tuple" + "(#{input["components"].map {|c| type(c) }.join(",")})" + elsif input["type"] == "enum" + "uint8" + else + input["type"] + end + end + # A decoded event log. class LogDescription # The event ABI interface used to decode the log. diff --git a/lib/eth/contract/event.rb b/lib/eth/contract/event.rb index 0106b3e8..60233ee4 100644 --- a/lib/eth/contract/event.rb +++ b/lib/eth/contract/event.rb @@ -30,7 +30,7 @@ def initialize(data) type_name x end @inputs = data["inputs"].collect { |x| x["name"] } - @event_string = "#{@name}(#{@input_types.join(",")})" + @event_string = Abi::Event.signature(data) @signature = Digest::Keccak.hexdigest(@event_string, 256) end From 992ab5796e1516bd80cb5a5a69ffcfe772c3051a Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sun, 23 Jun 2024 21:03:28 +0200 Subject: [PATCH 30/39] gem: run rufo (#277) * gem: run rufo * gem: run rufo --- lib/eth/abi/decoder.rb | 2 +- lib/eth/abi/event.rb | 14 +-- lib/eth/contract/event.rb | 3 +- spec/eth/abi/event_spec.rb | 1 - spec/eth/contract/event_spec.rb | 150 ++++++++++++++++---------------- 5 files changed, 85 insertions(+), 85 deletions(-) diff --git a/lib/eth/abi/decoder.rb b/lib/eth/abi/decoder.rb index 27911a60..45da7f46 100644 --- a/lib/eth/abi/decoder.rb +++ b/lib/eth/abi/decoder.rb @@ -51,7 +51,7 @@ def type(type, arg) type(Type.parse(type.base_type), arg[pointer + 32, Util.ceil32(data_l) + 32]) end end - elsif type.base_type == 'tuple' + elsif type.base_type == "tuple" offset = 0 data = {} type.components.each do |c| diff --git a/lib/eth/abi/event.rb b/lib/eth/abi/event.rb index d61b429d..a44bcbcb 100644 --- a/lib/eth/abi/event.rb +++ b/lib/eth/abi/event.rb @@ -46,7 +46,7 @@ def signature(interface) def type(input) if input["type"] == "tuple" - "(#{input["components"].map {|c| type(c) }.join(",")})" + "(#{input["components"].map { |c| type(c) }.join(",")})" elsif input["type"] == "enum" "uint8" else @@ -127,17 +127,17 @@ def decode_log(inputs, data, topics, anonymous = false) topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] } topic_types = topic_inputs.map do |i| - if i['type'] == 'tuple' - Type.parse(i['type'], i['components'], i['name']) + if i["type"] == "tuple" + Type.parse(i["type"], i["components"], i["name"]) else - i['type'] + i["type"] end end data_types = data_inputs.map do |i| - if i['type'] == 'tuple' - Type.parse(i['type'], i['components'], i['name']) + if i["type"] == "tuple" + Type.parse(i["type"], i["components"], i["name"]) else - i['type'] + i["type"] end end # If event is anonymous, all topics are arguments. Otherwise, the first diff --git a/lib/eth/contract/event.rb b/lib/eth/contract/event.rb index 60233ee4..01dc7649 100644 --- a/lib/eth/contract/event.rb +++ b/lib/eth/contract/event.rb @@ -42,11 +42,12 @@ def set_address(address) end private + def type_name(x) type = x["type"] case type when "tuple" - "(#{x['components'].collect { |c| type_name(c) }.join(',')})" + "(#{x["components"].collect { |c| type_name(c) }.join(",")})" else type end diff --git a/spec/eth/abi/event_spec.rb b/spec/eth/abi/event_spec.rb index 19ed435f..fe22ba4d 100644 --- a/spec/eth/abi/event_spec.rb +++ b/spec/eth/abi/event_spec.rb @@ -247,7 +247,6 @@ expect(signature).to eq "Transfer(address,address,uint256)" end - it "generates transfer function signature" do abi = erc20_abi.find { |i| i["type"] == "function" && i["name"] == "transfer" } signature = Abi::Event.signature(abi) diff --git a/spec/eth/contract/event_spec.rb b/spec/eth/contract/event_spec.rb index f5e78c91..94ba35ec 100644 --- a/spec/eth/contract/event_spec.rb +++ b/spec/eth/contract/event_spec.rb @@ -16,82 +16,82 @@ it "generates signature for event with tuple params" do event = Eth::Contract::Event.new({ - "anonymous" => false, - "inputs" => [ - { - "components" => [ - { - "internalType" => "uint256", - "name" => "topicId", - "type" => "uint256" - }, - { - "internalType" => "uint256", - "name" => "proposalId", - "type" => "uint256" - }, - { - "internalType" => "string", - "name" => "name", - "type" => "string" - }, - { - "internalType" => "string", - "name" => "symbol", - "type" => "string" - }, - { - "internalType" => "uint256", - "name" => "duration", - "type" => "uint256" - }, - { - "internalType" => "uint256", - "name" => "totalSupply", - "type" => "uint256" - }, - { - "internalType" => "uint256", - "name" => "miniStakeValue", - "type" => "uint256" - }, - { - "internalType" => "uint256", - "name" => "maxStakeValue", - "type" => "uint256" - }, - { - "internalType" => "uint256", - "name" => "maxParticipants", - "type" => "uint256" - }, - { - "internalType" => "uint256", - "name" => "whitelistIndex", - "type" => "uint256" - }, - { - "internalType" => "address", - "name" => "proposer", - "type" => "address" - }, - { - "internalType" => "bool", - "name" => "useWhitelist", - "type" => "bool" - } - ], - "indexed" => false, - "internalType" => "struct VoteContract.ProposalCreatedParams", - "name" => "params", - "type" => "tuple" - } + "anonymous" => false, + "inputs" => [ + { + "components" => [ + { + "internalType" => "uint256", + "name" => "topicId", + "type" => "uint256", + }, + { + "internalType" => "uint256", + "name" => "proposalId", + "type" => "uint256", + }, + { + "internalType" => "string", + "name" => "name", + "type" => "string", + }, + { + "internalType" => "string", + "name" => "symbol", + "type" => "string", + }, + { + "internalType" => "uint256", + "name" => "duration", + "type" => "uint256", + }, + { + "internalType" => "uint256", + "name" => "totalSupply", + "type" => "uint256", + }, + { + "internalType" => "uint256", + "name" => "miniStakeValue", + "type" => "uint256", + }, + { + "internalType" => "uint256", + "name" => "maxStakeValue", + "type" => "uint256", + }, + { + "internalType" => "uint256", + "name" => "maxParticipants", + "type" => "uint256", + }, + { + "internalType" => "uint256", + "name" => "whitelistIndex", + "type" => "uint256", + }, + { + "internalType" => "address", + "name" => "proposer", + "type" => "address", + }, + { + "internalType" => "bool", + "name" => "useWhitelist", + "type" => "bool", + }, ], - "name" => "ProposalCreated", - "type" => "event" - }) - expect(event.event_string).to eq('ProposalCreated((uint256,uint256,string,string,uint256,uint256,uint256,uint256,uint256,uint256,address,bool))') - expect(event.signature).to eq('4449031b77cbe261580701c097fb63211e768f685581e616330dfff20493536c') + "indexed" => false, + "internalType" => "struct VoteContract.ProposalCreatedParams", + "name" => "params", + "type" => "tuple", + }, + ], + "name" => "ProposalCreated", + "type" => "event", + }) + expect(event.event_string).to eq("ProposalCreated((uint256,uint256,string,string,uint256,uint256,uint256,uint256,uint256,uint256,address,bool))") + expect(event.signature).to eq("4449031b77cbe261580701c097fb63211e768f685581e616330dfff20493536c") end end end From 0b46d9c103a7e22c85b5f080ad6a00f9c8e94162 Mon Sep 17 00:00:00 2001 From: Chris Wawer Date: Sun, 23 Jun 2024 21:50:55 +0200 Subject: [PATCH 31/39] Allow to call JSON RPC with custom block number (#268) --- lib/eth/client.rb | 6 +++++- spec/eth/client_spec.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/eth/client.rb b/lib/eth/client.rb index ecd9dff6..7b203b5b 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -34,6 +34,9 @@ class Client # The default transaction max fee per gas in Wei, defaults to {Tx::DEFAULT_GAS_PRICE}. attr_accessor :max_fee_per_gas + # The block number used for archive calls. + attr_accessor :block_number + # A custom error type if a contract interaction fails. class ContractExecutionError < StandardError; end @@ -469,7 +472,8 @@ def encode_constructor_params(contract, args) # Prepares parameters and sends the command to the client. def send_command(command, args) - args << "latest" if ["eth_getBalance", "eth_call"].include? command + @block_number ||= "latest" + args << block_number if ["eth_getBalance", "eth_call"].include? command payload = { jsonrpc: "2.0", method: command, diff --git a/spec/eth/client_spec.rb b/spec/eth/client_spec.rb index 46439e07..c9a6a2c5 100644 --- a/spec/eth/client_spec.rb +++ b/spec/eth/client_spec.rb @@ -220,6 +220,35 @@ expect(geth_http.call(erc20_contract, "balanceOf", address)).to be_nil end + it "allows to call client with custom block numberreturn nil if raw result is 0x" do + block_number = 123 + + geth_http.block_number = block_number + + expected_payload = { + jsonrpc: "2.0", + method: "eth_call", + params: [{ + data: "0x70a08231000000000000000000000000d496b23d61f88a8c7758fca7560dcfac7b3b01f9", + to: "0xD496b23D61F88A8C7758fca7560dCFac7b3b01F9" + }, "0x#{block_number.to_s(16)}"], + id: 1 + }.to_json + + mock_response = { + jsonrpc: "2.0", + id: 1, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + expect_any_instance_of(Eth::Client::Http) + .to receive(:send_request) + .with(expected_payload) + .and_return(mock_response.to_json) + + geth_http.call(erc20_contract, "balanceOf", address) + end + it "called function name not defined" do expect { geth_http.call(contract, "ge") From fbd70c1d4f42020e6b5fab116654c8bcfdc8a2d7 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sun, 23 Jun 2024 21:51:19 +0200 Subject: [PATCH 32/39] abi/event: confirm decoding tuples works (#278) --- spec/eth/abi/event_spec.rb | 94 ++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/spec/eth/abi/event_spec.rb b/spec/eth/abi/event_spec.rb index fe22ba4d..f1eda41c 100644 --- a/spec/eth/abi/event_spec.rb +++ b/spec/eth/abi/event_spec.rb @@ -136,35 +136,73 @@ ] expect(kwargs[:values]).to eq [1, 1] end - end - it "can decode anonymous Transfer event" do - interface = { - "type" => "event", - "name" => "Transfer", - "anonymous" => true, - "inputs" => [ - { "indexed" => true, "internalType" => "address", "name" => "from", "type" => "address" }, - { "indexed" => true, "internalType" => "address", "name" => "to", "type" => "address" }, - { "indexed" => false, "internalType" => "uint256", "name" => "value", "type" => "uint256" }, - ], - } - - data = "0x00000000000000000000000000000000000000000000000000000002540be400" - topics = [ - "0x00000000000000000000000071660c4005ba85c37ccec55d0c4493e66fe775d3", - "0x000000000000000000000000639671019ddd8ec28d35113d8d1c5f1bbfd7e0be", - ] - - args, kwargs = Abi::Event.decode_log(interface["inputs"], data, topics, true) - - expect(args[0]).to eq "0x71660c4005ba85c37ccec55d0c4493e66fe775d3" - expect(args[1]).to eq "0x639671019ddd8ec28d35113d8d1c5f1bbfd7e0be" - expect(args[2]).to eq 10000000000 - - expect(kwargs[:from]).to eq "0x71660c4005ba85c37ccec55d0c4493e66fe775d3" - expect(kwargs[:to]).to eq "0x639671019ddd8ec28d35113d8d1c5f1bbfd7e0be" - expect(kwargs[:value]).to eq 10000000000 + it "can decode anonymous Transfer event" do + interface = { + "type" => "event", + "name" => "Transfer", + "anonymous" => true, + "inputs" => [ + { "indexed" => true, "internalType" => "address", "name" => "from", "type" => "address" }, + { "indexed" => true, "internalType" => "address", "name" => "to", "type" => "address" }, + { "indexed" => false, "internalType" => "uint256", "name" => "value", "type" => "uint256" }, + ], + } + + data = "0x00000000000000000000000000000000000000000000000000000002540be400" + topics = [ + "0x00000000000000000000000071660c4005ba85c37ccec55d0c4493e66fe775d3", + "0x000000000000000000000000639671019ddd8ec28d35113d8d1c5f1bbfd7e0be", + ] + + args, kwargs = Abi::Event.decode_log(interface["inputs"], data, topics, true) + + expect(args[0]).to eq "0x71660c4005ba85c37ccec55d0c4493e66fe775d3" + expect(args[1]).to eq "0x639671019ddd8ec28d35113d8d1c5f1bbfd7e0be" + expect(args[2]).to eq 10000000000 + + expect(kwargs[:from]).to eq "0x71660c4005ba85c37ccec55d0c4493e66fe775d3" + expect(kwargs[:to]).to eq "0x639671019ddd8ec28d35113d8d1c5f1bbfd7e0be" + expect(kwargs[:value]).to eq 10000000000 + end + + it "can decode wuminzhe's log" do + # https://github.com/q9f/eth.rb/issues/247 + abi = JSON.parse '[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"msgHash","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"root","type":"bytes32"},{"components":[{"internalType":"address","name":"channel","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"fromChainId","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"toChainId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"encoded","type":"bytes"}],"indexed":false,"internalType":"structMessage","name":"message","type":"tuple"}],"name":"MessageAccepted","type":"event"}]' + log = { + "address" => "0x0000000000bd9dcfda5c60697039e2b3b28b079b", + "blockHash" => "0xf9c70715305172f0d7ae0e335c38df5582c6138d96b742183c02a69ff3c11304", + "blockNumber" => "0xddcb4d", + "data" => "0xfc2a07bae9b75d5a817aa5ff752d263d213286dda48387a2e818814f4557d61200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000bd9dcfda5c60697039e2b3b28b079b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000066eed0000000000000000000000000f14341a7f464320319025540e8fe48ad0fe5aec000000000000000000000000000000000000000000000000000000000000002b0000000000000000000000000000000000bd9dcfda5c60697039e2b3b28b079b00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", + "logIndex" => "0xcd", + "removed" => false, + "topics" => [ + "0xa047cf3271a6e55d76e7706ca90d011a4f2f641f7c46dfd31f6abe4cd94db53f", + "0xf654c17ea89108d7183eaf31c762fe0c125d476aa8130938d8a1895307b7db5a", + ], + "transactionHash" => "0x215e05a6260a5fbca5ebf866bf8612868c50691e0ff24be54f96f8192ca9b968", + "transactionIndex" => "0x8c", + } + int = abi.find { |i| i["type"] == "event" && i["name"] == "MessageAccepted" } + args, kwargs = Abi::Event.decode_log(int["inputs"], log["data"], log["topics"]) + + expect(args[0]).to eq "\xF6T\xC1~\xA8\x91\b\xD7\x18>\xAF1\xC7b\xFE\f\x12]Gj\xA8\x13\t8\xD8\xA1\x89S\a\xB7\xDBZ" + expect(args[1]).to eq "\xFC*\a\xBA\xE9\xB7]Z\x81z\xA5\xFFu-&=!2\x86\xDD\xA4\x83\x87\xA2\xE8\x18\x81OEW\xD6\x12" + expect(args[2]["channel"]).to eq "0x0000000000bd9dcfda5c60697039e2b3b28b079b" + expect(args[2]["index"]).to eq 1 + expect(args[2]["fromChainId"]).to eq 421613 + expect(args[2]["from"]).to eq "0x0f14341a7f464320319025540e8fe48ad0fe5aec" + expect(args[2]["toChainId"]).to eq 43 + expect(args[2]["to"]).to eq "0x0000000000bd9dcfda5c60697039e2b3b28b079b" + expect(kwargs[:msgHash]).to eq "\xF6T\xC1~\xA8\x91\b\xD7\x18>\xAF1\xC7b\xFE\f\x12]Gj\xA8\x13\t8\xD8\xA1\x89S\a\xB7\xDBZ" + expect(kwargs[:root]).to eq "\xFC*\a\xBA\xE9\xB7]Z\x81z\xA5\xFFu-&=!2\x86\xDD\xA4\x83\x87\xA2\xE8\x18\x81OEW\xD6\x12" + expect(kwargs[:message]["channel"]).to eq "0x0000000000bd9dcfda5c60697039e2b3b28b079b" + expect(kwargs[:message]["index"]).to eq 1 + expect(kwargs[:message]["fromChainId"]).to eq 421613 + expect(kwargs[:message]["from"]).to eq "0x0f14341a7f464320319025540e8fe48ad0fe5aec" + expect(kwargs[:message]["toChainId"]).to eq 43 + expect(kwargs[:message]["to"]).to eq "0x0000000000bd9dcfda5c60697039e2b3b28b079b" + end end describe ".decode_logs" do From a89936e045cf4f323438de1eb0b46c54821dd1f6 Mon Sep 17 00:00:00 2001 From: Chris Wawer Date: Sun, 23 Jun 2024 21:52:24 +0200 Subject: [PATCH 33/39] Support negative number from JSON RPC (#267) --- lib/eth/abi/decoder.rb | 2 +- lib/eth/abi/encoder.rb | 2 +- spec/eth/abi_spec.rb | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/eth/abi/decoder.rb b/lib/eth/abi/decoder.rb index 45da7f46..35cf6baa 100644 --- a/lib/eth/abi/decoder.rb +++ b/lib/eth/abi/decoder.rb @@ -125,7 +125,7 @@ def primitive_type(type, data) Util.deserialize_big_endian_to_int data when "int" u = Util.deserialize_big_endian_to_int data - i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** type.sub_type.to_i) : u + i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** 256) : u # decoded integer i diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index eb7d4d62..4b4247ec 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -138,7 +138,7 @@ def int(arg, type) real_size = type.sub_type.to_i i = arg.to_i raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1) - Util.zpad_int(i % 2 ** type.sub_type.to_i) + Util.zpad_int(i < 0 ? (i + 2 ** 256) : i % 2 ** type.sub_type.to_i) end # Properly encodes booleans. diff --git a/spec/eth/abi_spec.rb b/spec/eth/abi_spec.rb index 3d623233..044b2c1a 100644 --- a/spec/eth/abi_spec.rb +++ b/spec/eth/abi_spec.rb @@ -387,5 +387,12 @@ def assert(data, types, args) pending("https://github.com/q9f/eth.rb/issues/102") assert(data, types, args) end + + it "test negative number" do + types = ["int24"] + args = [-887220] + data = Util.hex_to_bin "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2764c" + assert(data, types, args) + end end end From c80d7f933daa92b6fa705b4ec0a989e32fc7da35 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sun, 23 Jun 2024 22:53:10 +0200 Subject: [PATCH 34/39] eth/abi: fix negative integer *coding (#279) * eth/abi: fix negative integer *coding * uncomment test cases :) --- lib/eth/abi/encoder.rb | 2 +- spec/eth/abi/encoder_spec.rb | 4 ++-- spec/eth/abi_spec.rb | 18 +++++++++++++++++- spec/eth/client_spec.rb | 13 ++++++------- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index 4b4247ec..f2ee06d8 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -138,7 +138,7 @@ def int(arg, type) real_size = type.sub_type.to_i i = arg.to_i raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1) - Util.zpad_int(i < 0 ? (i + 2 ** 256) : i % 2 ** type.sub_type.to_i) + Util.zpad_int(i % 2 ** 256) end # Properly encodes booleans. diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 79e68d66..910e0d56 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -23,7 +23,7 @@ expect(Abi::Encoder.type t_uint_8, 255).to eq Util.zpad_int 255 expect { Abi::Encoder.type t_uint_8, 256 }.to raise_error Abi::ValueOutOfBounds - expect(Abi::Encoder.type t_int_8, -128).to eq Util.zpad "\x80", 32 + expect(Abi::Encoder.type t_int_8, -128).to eq Util.zpad "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80", 32 expect(Abi::Encoder.type t_int_8, 127).to eq Util.zpad "\x7f", 32 expect { Abi::Encoder.type t_int_8, -129 }.to raise_error Abi::ValueOutOfBounds expect { Abi::Encoder.type t_int_8, 128 }.to raise_error Abi::ValueOutOfBounds @@ -62,7 +62,7 @@ expect(Abi::Encoder.primitive_type t_uint_8, 255).to eq Util.zpad_int 255 expect { Abi::Encoder.primitive_type t_uint_8, 256 }.to raise_error Abi::ValueOutOfBounds - expect(Abi::Encoder.primitive_type t_int_8, -128).to eq Util.zpad "\x80", 32 + expect(Abi::Encoder.primitive_type t_int_8, -128).to eq Util.zpad "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80", 32 expect(Abi::Encoder.primitive_type t_int_8, 127).to eq Util.zpad "\x7f", 32 expect { Abi::Encoder.primitive_type t_int_8, -129 }.to raise_error Abi::ValueOutOfBounds expect { Abi::Encoder.primitive_type t_int_8, 128 }.to raise_error Abi::ValueOutOfBounds diff --git a/spec/eth/abi_spec.rb b/spec/eth/abi_spec.rb index 044b2c1a..cf188649 100644 --- a/spec/eth/abi_spec.rb +++ b/spec/eth/abi_spec.rb @@ -387,12 +387,28 @@ def assert(data, types, args) pending("https://github.com/q9f/eth.rb/issues/102") assert(data, types, args) end + end + describe "edge cases" do it "test negative number" do types = ["int24"] args = [-887220] data = Util.hex_to_bin "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2764c" - assert(data, types, args) + expect(data).to eq Abi.encode(types, args) + expect(args).to eq Abi.decode(types, data) + expect(args).to eq Abi.decode(types, Abi.encode(types, args)) + + expect(Abi.encode(["int8"], [0])).to eq Util.hex_to_bin "0000000000000000000000000000000000000000000000000000000000000000" + expect(Abi.encode(["int8"], [1])).to eq Util.hex_to_bin "0000000000000000000000000000000000000000000000000000000000000001" + expect(Abi.encode(["int8"], [-1])).to eq Util.hex_to_bin "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + expect(Abi.encode(["int24"], [887220])).to eq Util.hex_to_bin "00000000000000000000000000000000000000000000000000000000000d89b4" + expect(Abi.encode(["int24"], [-887220])).to eq Util.hex_to_bin "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2764c" + + expect(Abi.decode(["int8"], "0000000000000000000000000000000000000000000000000000000000000000")).to eq [0] + expect(Abi.decode(["int8"], "0000000000000000000000000000000000000000000000000000000000000001")).to eq [1] + expect(Abi.decode(["int8"], "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")).to eq [-1] + expect(Abi.decode(["int24"], "00000000000000000000000000000000000000000000000000000000000d89b4")).to eq [887220] + expect(Abi.decode(["int24"], "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2764c")).to eq [-887220] end end end diff --git a/spec/eth/client_spec.rb b/spec/eth/client_spec.rb index c9a6a2c5..d5fe029d 100644 --- a/spec/eth/client_spec.rb +++ b/spec/eth/client_spec.rb @@ -230,21 +230,20 @@ method: "eth_call", params: [{ data: "0x70a08231000000000000000000000000d496b23d61f88a8c7758fca7560dcfac7b3b01f9", - to: "0xD496b23D61F88A8C7758fca7560dCFac7b3b01F9" + to: "0xD496b23D61F88A8C7758fca7560dCFac7b3b01F9", }, "0x#{block_number.to_s(16)}"], - id: 1 + id: 1, }.to_json mock_response = { jsonrpc: "2.0", id: 1, - result: "0x0000000000000000000000000000000000000000000000000000000000000000" + result: "0x0000000000000000000000000000000000000000000000000000000000000000", } - expect_any_instance_of(Eth::Client::Http) - .to receive(:send_request) - .with(expected_payload) - .and_return(mock_response.to_json) + expect_any_instance_of(Eth::Client::Http).to receive(:send_request) + .with(expected_payload) + .and_return(mock_response.to_json) geth_http.call(erc20_contract, "balanceOf", address) end From 6833faa333a9cd3e6fab8f4be2fc297ca1fb4b44 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:15:25 +0200 Subject: [PATCH 35/39] eth: update version (#280) * eth: update version * update CHANGELOG * fix spec * update changelog --- CHANGELOG.md | 36 ++++++++++++++++++++++++++++++++++++ lib/eth/version.rb | 2 +- spec/eth_spec.rb | 4 ++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 587477fd..ad8847e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,42 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.5.12] +### Added +### Changed + +## [0.5.11] +### Added +* Eth/abi: allow encoding address types [#242](https://github.com/q9f/eth.rb/pull/242) +* Eth/solidity: enable --via-ir [#232](https://github.com/q9f/eth.rb/pull/232) +* Checking userinfo with the uri method [#233](https://github.com/q9f/eth.rb/pull/233) +* Eth/abi: add abicoder gem tests collection [#218](https://github.com/q9f/eth.rb/pull/218) +* Manual default_account [#215](https://github.com/q9f/eth.rb/pull/215) +* Add moonbeam networks in [#209](https://github.com/q9f/eth.rb/pull/209) + +### Changed +* Spec: run rufo [#245](https://github.com/q9f/eth.rb/pull/245) +* Fix the decoding of unsigned transactions [#243](https://github.com/q9f/eth.rb/pull/243) +* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.2 to 4.4.3 [#244](https://github.com/q9f/eth.rb/pull/244) +* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.1 to 4.4.2 [#240](https://github.com/q9f/eth.rb/pull/240) +* Eth/tx: update tx initcode cost for shanghai [#237](https://github.com/q9f/eth.rb/pull/237) +* Eth/client: remove default gas limit attribute [#235](https://github.com/q9f/eth.rb/pull/235) +* Docs: minor fixups [#229](https://github.com/q9f/eth.rb/pull/229) +* Eth/contract: ensure contract name is title case [#228](https://github.com/q9f/eth.rb/pull/228) +* Deps: require forwardable for contracts [#227](https://github.com/q9f/eth.rb/pull/227) +* Ens/resolver: remove pending for etc coin type [#219](https://github.com/q9f/eth.rb/pull/219) +* Deps: update secp256k1 to 6 [#214](https://github.com/q9f/eth.rb/pull/214) +* Eth/solidity: add docs for solc path override [#213](https://github.com/q9f/eth.rb/pull/213) +* Manually overwrite solc path [#212](https://github.com/q9f/eth.rb/pull/212) +* Abi.decoder handles arrays of string and bytes [#207](https://github.com/q9f/eth.rb/pull/207) +* Eth/util: fix compressed public key to address in [#206](https://github.com/q9f/eth.rb/pull/206) +* Eth/api: update execution apis to latest spec [#204](https://github.com/q9f/eth.rb/pull/204) +* Eth/abi: split abi class into encoder and decoder [#203](https://github.com/q9f/eth.rb/pull/203) +* Eth/client: deduplicate code [#202](https://github.com/q9f/eth.rb/pull/202) +* Eth/client: rewrite send to send_request [#201](https://github.com/q9f/eth.rb/pull/201) +* Docs: update changelog for 0.5.10 [#200](https://github.com/q9f/eth.rb/pull/200) +* Tested with Ruby 3.2 [#199](https://github.com/q9f/eth.rb/pull/199) + ## [0.5.10] ### Added * Eth/client: add transfer_erc20 function [#197](https://github.com/q9f/eth.rb/pull/197) diff --git a/lib/eth/version.rb b/lib/eth/version.rb index 92f475eb..4d69533f 100644 --- a/lib/eth/version.rb +++ b/lib/eth/version.rb @@ -22,7 +22,7 @@ module Eth MINOR = 5.freeze # Defines the patch version of the {Eth} module. - PATCH = 11.freeze + PATCH = 12.freeze # Defines the version string of the {Eth} module. VERSION = [MAJOR, MINOR, PATCH].join(".").freeze diff --git a/spec/eth_spec.rb b/spec/eth_spec.rb index 2c4e0d06..9b4e3419 100644 --- a/spec/eth_spec.rb +++ b/spec/eth_spec.rb @@ -1,9 +1,9 @@ require "spec_helper" describe Eth do - it "0.5.11 works" do + it "0.5.12 works" do # placeholder to set up spec in future - expect(Eth::VERSION).to eq "0.5.11" + expect(Eth::VERSION).to eq "0.5.12" end end From 89bffbcef39b6a7b2949147aff7e8f78f05c7d40 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:43:08 +0200 Subject: [PATCH 36/39] gem: bump version to 0.5.13 (#281) --- CHANGELOG.md | 20 ++++++++++++++++++++ SECURITY.md | 2 +- eth.gemspec | 2 +- lib/eth/version.rb | 2 +- spec/eth_spec.rb | 4 ++-- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad8847e8..8ac345d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,27 @@ All notable changes to this project will be documented in this file. ## [0.5.12] ### Added +* Allow to call JSON RPC with custom block number [#268](https://github.com/q9f/eth.rb/pull/268) +* Support tuple params in EventLog [#276](https://github.com/q9f/eth.rb/pull/276) + ### Changed +* Eth: update version [#280](https://github.com/q9f/eth.rb/pull/280) +* Eth/abi: fix negative integer coding [#279](https://github.com/q9f/eth.rb/pull/279) +* Support negative number from JSON RPC [#267](https://github.com/q9f/eth.rb/pull/267) +* Abi/event: confirm decoding tuples works [#278](https://github.com/q9f/eth.rb/pull/278) +* Allow to call JSON RPC with custom block number [#268](https://github.com/q9f/eth.rb/pull/268) +* Gem: run rufo [#277](https://github.com/q9f/eth.rb/pull/277) +* Fix event signature [#250](https://github.com/q9f/eth.rb/pull/250) +* Support tuple params in EventLog [#276](https://github.com/q9f/eth.rb/pull/276) +* Ci: update ruby version [#271](https://github.com/q9f/eth.rb/pull/271) +* Eth/api: remove coinbase as default account [#269](https://github.com/q9f/eth.rb/pull/269) +* Build(deps): bump JamesIves/github-pages-deploy-action from 4.5.0 to 4.6.1 [#275](https://github.com/q9f/eth.rb/pull/275) +* Build(deps): bump github/codeql-action from 2 to 3 [#257](https://github.com/q9f/eth.rb/pull/257) +* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.3 to 4.5.0 [#256](https://github.com/q9f/eth.rb/pull/256) +* Fix typo in contract_spec.rb [#253](https://github.com/q9f/eth.rb/pull/253) +* Eth/eip721: fix data type bug for bytes, fix #251 [#252](https://github.com/q9f/eth.rb/pull/252) +* Ci: unpatch geth [#248](https://github.com/q9f/eth.rb/pull/248) +* Build(deps): bump actions/checkout from 3 to 4 [#246](https://github.com/q9f/eth.rb/pull/246) ## [0.5.11] ### Added diff --git a/SECURITY.md b/SECURITY.md index 070d4e35..ec90f8d4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported Versions -Ruby Ethereum 0.5.0 is a complete rewrite of the old `eth` 0.4.x gem. +Ruby Ethereum 0.5.x is a complete rewrite of the old `eth` 0.4.x gem. It also contains modules from `abi`, `rlp` and the `ethereum` gem. None of these gems are maintained anymore except for `eth` 0.5.0 and diff --git a/eth.gemspec b/eth.gemspec index 56c341ab..83fb7996 100644 --- a/eth.gemspec +++ b/eth.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep %r{^(test|spec|features)/} spec.platform = Gem::Platform::RUBY - spec.required_ruby_version = ">= 2.7", "< 4.0" + spec.required_ruby_version = ">= 3.0", "< 4.0" # forwardable for contracts meta programming spec.add_dependency "forwardable", "~> 1.3" diff --git a/lib/eth/version.rb b/lib/eth/version.rb index 4d69533f..236308f9 100644 --- a/lib/eth/version.rb +++ b/lib/eth/version.rb @@ -22,7 +22,7 @@ module Eth MINOR = 5.freeze # Defines the patch version of the {Eth} module. - PATCH = 12.freeze + PATCH = 13.freeze # Defines the version string of the {Eth} module. VERSION = [MAJOR, MINOR, PATCH].join(".").freeze diff --git a/spec/eth_spec.rb b/spec/eth_spec.rb index 9b4e3419..b0c6b827 100644 --- a/spec/eth_spec.rb +++ b/spec/eth_spec.rb @@ -1,9 +1,9 @@ require "spec_helper" describe Eth do - it "0.5.12 works" do + it "0.5.13 works" do # placeholder to set up spec in future - expect(Eth::VERSION).to eq "0.5.12" + expect(Eth::VERSION).to eq "0.5.13" end end From 4182fa7993a9a6c6648e065dcf6748fb8d738895 Mon Sep 17 00:00:00 2001 From: Tom Lehman Date: Mon, 1 Jul 2024 02:24:37 -0400 Subject: [PATCH 37/39] Fix undefined method `raise_error' for an instance of Eth::Tx::Eip1559 (NoMethodError) (#282) * Update eip2930.rb, fix undefined method `raise_error' for an instance of Eth::Tx::Eip1559 (NoMethodError) * Update eip1559.rb --- lib/eth/tx/eip1559.rb | 2 +- lib/eth/tx/eip2930.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/eth/tx/eip1559.rb b/lib/eth/tx/eip1559.rb index a1ea1c19..f0fe3050 100644 --- a/lib/eth/tx/eip1559.rb +++ b/lib/eth/tx/eip1559.rb @@ -180,7 +180,7 @@ def decode(hex) # allows us to force-setting a signature if the transaction is signed already _set_signature(recovery_id, r, s) else - raise_error DecoderError, "Cannot decode EIP-1559 payload!" + raise DecoderError, "Cannot decode EIP-1559 payload!" end # last but not least, set the type. diff --git a/lib/eth/tx/eip2930.rb b/lib/eth/tx/eip2930.rb index 60e857ab..db256101 100644 --- a/lib/eth/tx/eip2930.rb +++ b/lib/eth/tx/eip2930.rb @@ -175,7 +175,7 @@ def decode(hex) # allows us to force-setting a signature if the transaction is signed already _set_signature(recovery_id, r, s) else - raise_error DecoderError, "Cannot decode EIP-2930 payload!" + raise DecoderError, "Cannot decode EIP-2930 payload!" end # last but not least, set the type. From 8547bd1c88e840beaa066b6eba9674ae98369611 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:48:09 +0200 Subject: [PATCH 38/39] eth/chain: update list of chains (#283) --- lib/eth/chain.rb | 43 +++++++++++++++++++++++++++++++----------- spec/eth/chain_spec.rb | 14 ++++++++++---- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/lib/eth/chain.rb b/lib/eth/chain.rb index 7243bcb0..a591b089 100644 --- a/lib/eth/chain.rb +++ b/lib/eth/chain.rb @@ -32,6 +32,15 @@ class ReplayProtectionError < StandardError; end # Chain ID for Optimistic Ethereum mainnet. OPTIMISM = 10.freeze + # Chain ID for Cronos mainnet. + CRONOS = 25.freeze + + # Chain ID for Rootstock mainnet. + RSK = 30.freeze + + # Chain ID for BNB Smart Chain mainnet. + BNB = 56.freeze + # Chain ID for Ethereum Classic mainnet. CLASSIC = 61.freeze @@ -50,9 +59,24 @@ class ReplayProtectionError < StandardError; end # Chain ID for the Polygon mainnet (formerly Matic). POLYGON = MATIC.freeze - # Chain ID for Arbitrum mainnet. + # Chain ID for Filecoin mainnet. + FILECOIN = 314.freeze + + # Chain ID for Moonbeam + MOONBEAM = 1284.freeze + + # Chain ID for Base mainnet. + BASE = 8453.freeze + + # Chain ID for Arbitrum One mainnet. ARBITRUM = 42161.freeze + # Chain ID for Avalance C-Chain mainnet. + AVALANCHE = 43114.freeze + + # Chain ID for Linea mainnet. + LINEA = 59144.freeze + # Chain ID for Morden (Ethereum) testnet. MORDEN = 2.freeze @@ -77,7 +101,7 @@ class ReplayProtectionError < StandardError; end # Chain ID for Mordor testnet. MORDOR = 63.freeze - # Chain ID for Optimistik Kovan testnet. + # Chain ID for Optimistm Kovan testnet. KOVAN_OPTIMISM = 69.freeze # Chain ID for Arbitrum xDAI testnet. @@ -86,6 +110,12 @@ class ReplayProtectionError < StandardError; end # Chain ID for Optimistic Goerli testnet. GOERLI_OPTIMISM = 420.freeze + # Chain ID for Moonriver testnet + MOONRIVER = 1285.freeze + + # Chain ID for Moonbase alpha + MOONBASE = 1287.freeze + # Chain ID for the Polygon Mumbai testnet. MUMBAI = 80001.freeze @@ -104,15 +134,6 @@ class ReplayProtectionError < StandardError; end # Chain ID for the geth private network preset. PRIVATE_GETH = 1337.freeze - # Chain ID for Moonbase - MOONBASE = 1287.freeze - - # Chain ID for Moonriver - MOONRIVER = 1285.freeze - - # Chain ID for Moonbeam - MOONBEAM = 1284.freeze - # Indicates wether the given `v` indicates a legacy chain value # used by ledger wallets without EIP-155 replay protection. # diff --git a/spec/eth/chain_spec.rb b/spec/eth/chain_spec.rb index 997e596b..683ba57a 100644 --- a/spec/eth/chain_spec.rb +++ b/spec/eth/chain_spec.rb @@ -8,13 +8,20 @@ expect(Chain::EXPANSE).to eq 2 expect(Chain::OPTIMISM).to eq 10 expect(Chain::CLASSIC).to eq 61 + expect(Chain::CRONOS).to eq 25 + expect(Chain::RSK).to eq 30 + expect(Chain::BNB).to eq 56 expect(Chain::POA_NET).to eq 99 expect(Chain::XDAI).to eq 100 expect(Chain::GNOSIS).to eq 100 expect(Chain::MATIC).to eq 137 expect(Chain::POLYGON).to eq 137 - expect(Chain::ARBITRUM).to eq 42161 + expect(Chain::FILECOIN).to eq 314 expect(Chain::MOONBEAM).to eq 1284 + expect(Chain::BASE).to eq 8453 + expect(Chain::ARBITRUM).to eq 42161 + expect(Chain::AVALANCHE).to eq 43114 + expect(Chain::LINEA).to eq 59144 # Chain IDs for selected testnets expect(Chain::MORDEN).to eq 2 @@ -28,15 +35,14 @@ expect(Chain::KOVAN_OPTIMISM).to eq 69 expect(Chain::XDAI_ARBITRUM).to eq 200 expect(Chain::GOERLI_OPTIMISM).to eq 420 + expect(Chain::MOONRIVER).to eq 1285 + expect(Chain::MOONBASE).to eq 1287 expect(Chain::MUMBAI).to eq 80001 expect(Chain::RINKEBY_ARBITRUM).to eq 421611 expect(Chain::GOERLI_ARBITRUM).to eq 421613 expect(Chain::SEPOLIA).to eq 11155111 expect(Chain::HOLESOVICE).to eq 11166111 - expect(Chain::MOONRIVER).to eq 1285 - expect(Chain::MOONBASE).to eq 1287 - # Chain IDs for selected private networks expect(Chain::PRIVATE_GETH).to eq 1337 end From fd5b6b577ba3209c41cfb1952a4776ec36d85229 Mon Sep 17 00:00:00 2001 From: Afri <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 17:18:43 +0200 Subject: [PATCH 39/39] eth/client: always return hash even if transaction didn't succeed (#284) --- lib/eth/abi/decoder.rb | 3 +-- lib/eth/client.rb | 4 ++-- spec/eth/client_spec.rb | 31 ++++++++++++++++++++++++------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/eth/abi/decoder.rb b/lib/eth/abi/decoder.rb index 35cf6baa..171e2263 100644 --- a/lib/eth/abi/decoder.rb +++ b/lib/eth/abi/decoder.rb @@ -54,19 +54,18 @@ def type(type, arg) elsif type.base_type == "tuple" offset = 0 data = {} + raise DecodingError, "Cannot decode tuples without known components" if type.components.nil? type.components.each do |c| if c.dynamic? pointer = Util.deserialize_big_endian_to_int arg[offset, 32] # Pointer to the size of the array's element data_len = Util.deserialize_big_endian_to_int arg[pointer, 32] # length of the element data[c.name] = type(c, arg[pointer, Util.ceil32(data_len) + 32]) - # puts data offset += 32 else size = c.size data[c.name] = type(c, arg[offset, size]) offset += size - # puts data end end data diff --git a/lib/eth/client.rb b/lib/eth/client.rb index 7b203b5b..edc24d92 100644 --- a/lib/eth/client.rb +++ b/lib/eth/client.rb @@ -315,11 +315,11 @@ def transact(contract, function, *args, **kwargs) # See {#transact} for params and overloads. # # @raise [Client::ContractExecutionError] if the execution fails. - # @return [Object] returns the result of the transaction. + # @return [Object, Boolean] returns the result of the transaction (hash and execution status). def transact_and_wait(contract, function, *args, **kwargs) begin hash = wait_for_tx(transact(contract, function, *args, **kwargs)) - return hash if tx_succeeded? hash + return hash, tx_succeeded?(hash) rescue IOError => e raise ContractExecutionError, e end diff --git a/spec/eth/client_spec.rb b/spec/eth/client_spec.rb index d5fe029d..de6f9d3c 100644 --- a/spec/eth/client_spec.rb +++ b/spec/eth/client_spec.rb @@ -14,6 +14,8 @@ # it expects an $INFURA_TOKEN in environment let(:infura_api) { "https://mainnet.infura.io/v3/#{ENV["INFURA_TOKEN"]}" } subject(:infura_mainnet) { Client.create infura_api } + let(:infura_api_base) { "https://base-mainnet.infura.io/v3/#{ENV["INFURA_TOKEN"]}" } + subject(:infura_base) { Client.create infura_api_base } describe ".create .initialize" do it "creates an ipc client" do @@ -277,7 +279,8 @@ it "transacts with gas limit override" do address = geth_http.deploy_and_wait(test_contract) - txn_hash = geth_http.transact_and_wait(test_contract, "set", 12, 24, address: address, gas_limit: 100_000_000) + txn_hash, txn_status = geth_http.transact_and_wait(test_contract, "set", 12, 24, address: address, gas_limit: 100_000_000) + expect(txn_status).to be_truthy response = geth_http.eth_get_transaction_by_hash(txn_hash) response = geth_http.call(test_contract, "get") expect(response).to eq([12, 24]) @@ -289,6 +292,15 @@ result = geth_http.call(contract, "greet", address: address) expect(result).to eq("Hello!") end + + it "calls a lendingpool contract on base returning tuple abi" do + pending("https://github.com/q9f/eth.rb/issues/273") + adr = Address.new "0xbb505c54d71e9e599cb8435b4f0ceec05fc71cbd" + nam = "LendingPool" + abi = '[{"inputs":[{"internalType":"address","name":"_addressRegistry","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"eTokenAmount","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"referral","type":"uint16"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":true,"internalType":"address","name":"vaultAddress","type":"address"}],"name":"DisableVaultToBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":true,"internalType":"address","name":"vaultAddress","type":"address"}],"name":"EnableVaultToBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"eTokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"stakingAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"InitReserve","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"eTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingTokenAmount","type":"uint256"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"ReserveActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"ReserveBorrowDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"ReserveBorrowEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"ReserveDeActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"ReserveFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"ReserveUnFreeze","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultId","type":"uint256"},{"indexed":true,"internalType":"address","name":"vaultAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"credit","type":"uint256"}],"name":"SetCreditsOfVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"utilizationA","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"borrowingRateA","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"utilizationB","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"borrowingRateB","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"maxBorrowingRate","type":"uint16"}],"name":"SetInterestRateConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"SetReserveCapacity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"reserveId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeRate","type":"uint256"}],"name":"SetReserveFeeRate","type":"event"},{"anonymous":false,"inputs":[],"name":"UnPaused","type":"event"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"activateReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint256","name":"debtId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"borrowingRateOfReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowingWhiteList","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"credits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"deActivateReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"debtPositions","outputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"borrowed","type":"uint256"},{"internalType":"uint256","name":"borrowedIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"eTokenAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"depositAndStake","outputs":[{"internalType":"uint256","name":"eTokenAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"disableBorrowing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultId","type":"uint256"}],"name":"disableVaultToBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyPauseAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"enableBorrowing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultId","type":"uint256"}],"name":"enableVaultToBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"exchangeRateOfReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"freezeReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"debtId","type":"uint256"}],"name":"getCurrentDebt","outputs":[{"internalType":"uint256","name":"currentDebt","type":"uint256"},{"internalType":"uint256","name":"latestBorrowingIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"getETokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserveIdArr","type":"uint256[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getPositionStatus","outputs":[{"components":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"eTokenStaked","type":"uint256"},{"internalType":"uint256","name":"eTokenUnStaked","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"internalType":"struct ILendingPool.PositionStatus[]","name":"statusArr","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"debtId","type":"uint256"}],"name":"getReserveIdOfDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"reserveIdArr","type":"uint256[]"}],"name":"getReserveStatus","outputs":[{"components":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"address","name":"underlyingTokenAddress","type":"address"},{"internalType":"address","name":"eTokenAddress","type":"address"},{"internalType":"address","name":"stakingAddress","type":"address"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"uint256","name":"totalBorrows","type":"uint256"},{"internalType":"uint256","name":"exchangeRate","type":"uint256"},{"internalType":"uint256","name":"borrowingRate","type":"uint256"}],"internalType":"struct ILendingPool.ReserveStatus[]","name":"statusArr","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"getStakingAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"getUnderlyingTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"initReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"newDebtPosition","outputs":[{"internalType":"uint256","name":"debtId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextDebtPositionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextReserveId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint256","name":"eTokenAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"receiveNativeETH","type":"bool"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint256","name":"debtId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"reserves","outputs":[{"internalType":"uint256","name":"borrowingIndex","type":"uint256"},{"internalType":"uint256","name":"currentBorrowingRate","type":"uint256"},{"internalType":"uint256","name":"totalBorrows","type":"uint256"},{"internalType":"address","name":"underlyingTokenAddress","type":"address"},{"internalType":"address","name":"eTokenAddress","type":"address"},{"internalType":"address","name":"stakingAddress","type":"address"},{"internalType":"uint256","name":"reserveCapacity","type":"uint256"},{"components":[{"internalType":"uint128","name":"utilizationA","type":"uint128"},{"internalType":"uint128","name":"borrowingRateA","type":"uint128"},{"internalType":"uint128","name":"utilizationB","type":"uint128"},{"internalType":"uint128","name":"borrowingRateB","type":"uint128"},{"internalType":"uint128","name":"maxBorrowingRate","type":"uint128"}],"internalType":"struct DataTypes.InterestRateConfig","name":"borrowingRateConfig","type":"tuple"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint128","name":"lastUpdateTimestamp","type":"uint128"},{"internalType":"uint16","name":"reserveFeeRate","type":"uint16"},{"components":[{"internalType":"bool","name":"isActive","type":"bool"},{"internalType":"bool","name":"frozen","type":"bool"},{"internalType":"bool","name":"borrowingEnabled","type":"bool"}],"internalType":"struct DataTypes.Flags","name":"flags","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint16","name":"utilizationA","type":"uint16"},{"internalType":"uint16","name":"borrowingRateA","type":"uint16"},{"internalType":"uint16","name":"utilizationB","type":"uint16"},{"internalType":"uint16","name":"borrowingRateB","type":"uint16"},{"internalType":"uint16","name":"maxBorrowingRate","type":"uint16"}],"name":"setBorrowingRateConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultId","type":"uint256"},{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint256","name":"credit","type":"uint256"}],"name":"setCreditsOfVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"}],"name":"setReserveCapacity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint16","name":"_rate","type":"uint16"}],"name":"setReserveFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"totalBorrowsOfReserve","outputs":[{"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"totalLiquidityOfReserve","outputs":[{"internalType":"uint256","name":"totalLiquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"unFreezeReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unPauseAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"},{"internalType":"uint256","name":"eTokenAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"receiveNativeETH","type":"bool"}],"name":"unStakeAndWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveId","type":"uint256"}],"name":"utilizationRateOfReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]' + lending_pool = Contract.from_abi(name: nam, address: adr, abi: abi) + infura_base.call(lending_pool, "getReserveStatus", [64]) + end end describe ".transact .transact_and_wait" do @@ -315,20 +327,23 @@ it "transact the function with key" do geth_http.transfer_and_wait(test_key.address, 1337 * Unit::ETHER) address = geth_http.deploy_and_wait(contract, sender_key: test_key) - response = geth_http.transact_and_wait(contract, "set", 42, sender_key: test_key, address: address) + response, status = geth_http.transact_and_wait(contract, "set", 42, sender_key: test_key, address: address) + expect(status).to be_truthy expect(response).to start_with "0x" end it "transact the function using legacy transactions" do address = geth_http.deploy_and_wait(contract) - response = geth_http.transact_and_wait(contract, "set", 42, legacy: true, address: address) + response, status = geth_http.transact_and_wait(contract, "set", 42, legacy: true, address: address) + expect(status).to be_truthy expect(response).to start_with "0x" end it "transacts the function with constructor params" do contract = Contract.from_file(file: "spec/fixtures/contracts/greeter.sol", contract_index: 0) address = geth_http.deploy_and_wait(contract, "Hello!") - response = geth_http.transact_and_wait(contract, "setGreeting", "How are you?", address: address) + response, status = geth_http.transact_and_wait(contract, "setGreeting", "How are you?", address: address) + expect(status).to be_truthy expect(response).to start_with "0x" end @@ -336,7 +351,8 @@ geth_http.transfer_and_wait(test_key.address, 0.01 * Unit::ETHER) address = geth_http.deploy_and_wait(contract, sender_key: test_key) tx_value = 1 - tx_hash = geth_http.transact_and_wait(contract, "set", 42, sender_key: test_key, address: address, tx_value: tx_value) + tx_hash, tx_status = geth_http.transact_and_wait(contract, "set", 42, sender_key: test_key, address: address, tx_value: tx_value) + expect(tx_status).to be_truthy tx_value_from_server = geth_http.eth_get_transaction_by_hash(tx_hash)["result"]["value"].to_i(16) expect(tx_value_from_server).to eq(tx_value) end @@ -354,11 +370,12 @@ it "raises if a transaction fails" do addr = geth_http.deploy_and_wait(contract) - hash = geth_http.transact_and_wait(contract, "set", 42, address: addr) + hash, status = geth_http.transact_and_wait(contract, "set", 42, address: addr) + expect(status).to be_truthy expect(geth_http.tx_mined? hash).to be_truthy expect(geth_http.tx_succeeded? hash).to be_truthy expect { - hash = geth_http.transact_and_wait(contract, "set", 138, address: addr) + hash, status = geth_http.transact_and_wait(contract, "set", 138, address: addr) }.to raise_error(Client::ContractExecutionError, "execution reverted") end