Giter Site home page Giter Site logo

q9f / eth.rb Goto Github PK

View Code? Open in Web Editor NEW
187.0 11.0 79.0 2.91 MB

a straightforward library to build, sign, and broadcast ethereum transactions anywhere you can run ruby.

Home Page: https://q9f.github.io/eth.rb

License: Apache License 2.0

Ruby 99.95% Shell 0.05%
ruby ethereum eip55 eip155 eip712 eip191 secp256k1 transaction abi eip1559

eth.rb's Introduction

eth.rb's People

Contributors

a-moreira avatar ab320012 avatar an-lee avatar beacoding avatar buhrmi avatar chainoperator avatar d4mk0 avatar dansimpson avatar dependabot[bot] avatar dladowitz avatar escoffon avatar etscrivner avatar flanagansteve avatar geeknees avatar iamajvillalobos avatar johnnybutler7 avatar josh avatar justinkchen avatar k-s-a avatar kudohamu avatar kurotaky avatar lasvad avatar lgn21st avatar llmora avatar peter-chung-xfers avatar pienkowb avatar q9f avatar randoum avatar regul777 avatar se3000 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eth.rb's Issues

eth/contract: from_file defaults to ContextMixin

From Telegram:

I'm experiencing some strange behavior with from_file. It will default to ContentMixin for certain filenames (without changing the contract content), and have a nil abi, and then will get the proper contract contents for others

photo_2022-06-02_12-20-48

eth/contract hello_world "already known" bug?

Hi. Thank you for this gem. I have been debugging for hours adding print statements and trying to get past an issue with gem where every time I submit a simple hello world, or hi there contract, I get the same error:

{"jsonrpc"=>"2.0",
 "id"=>7,
 "error"=>{"code"=>-32000, "message"=>"already known"}}

This issue has been ongoing for a week now, so please forgive my annoying posts. I am simply trying to use the eth gem to deploy the most simple Hello World contract I can, and it will not work regardless of what I change, read, study or try.

I read that the way around this "already known" error was to cancel the transaction by sending zero ETH (test ETH) to and from the same address, which I did with MetaMask for a number of addresses, and all these "send zero to myself transactions" worked without flaw using MM.

However, then I try to deploy "Hello World" or "Hi There" again using the same address where I just send myself 0 ETH, on the same chain of course, I still get:

{"jsonrpc"=>"2.0",
 "id"=>7,
 "error"=>{"code"=>-32000, "message"=>"already known"}}

FWIW, one of the blockchain providers I use told me that they could deploy using curl and so they did not understand this issue; which seems to be related to eth.rb but despite adding countless print statements, reading the docs, and debugging the best I can consider I am not an expert with eth.rb nor the EVM.

If someone can post some simple ruby code using this gem to deploy "Hello World", that would be very helpful at this point in time. I can try that code and see if it works. I searched the net and read the eth docs many times, and I cannot find a working "Hello World" contract deployment example using this gem; but of course, I could have missed it after a week of hand banging :)

My apologies if posting these issues are annoying or if I am posting incorrectly. If I had the required experience with the EVM and this gem, I would gladly push some changes, believe me :)

Thank you.

Hello World

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract HelloWorld {
   function helloWorld() external pure returns (string memory) {
   return "Hello, World!";
   }
}

Note: I recall when I wrote my first client-server socket program over 30 years ago, even with no experience in C programming, I could send "Hello World" across the network using client and server sockets in just a few hours, just opening books on C programming. It seems to me that "Hello World" on the EVM using this Ruby gem should not take weeks, to be honest, but what do I know ;)

eth/abi: decode/encode array of tuples

I have encoded data like this:

0x0000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000018a475d6741215709ed6cc5f4d064732379b5a580000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002e516d57426953453942795236767278346876726a71533353473572367745345352713743503252567061665a5756000000000000000000000000000000000000

and i know that it's was encoded like ['uint256', '(address,uint256)[]', 'string'] with values [66, [(0x18a475d6741215709ed6cc5f4d064732379b5a58,1)], QmWBiSE9ByR6vrx4hvrjqS3SG5r6wE4SRq7CP2RVpafZWV]
and i know that everything is ok, because it's successfully decoded with ethers.js

is it possible to decode such data with this gem?

encode bytes error

When executing a method with arguments of data-type bytes4,
a method encode_bytes(value, subtype) in abi.rb:356 throws an Eth::Abi::ValueOutOfBounds

in the Abi I have:

 {
    "constant": true,
    "inputs": [
      { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }
    ],
    "name": "supportsInterface",
    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  }

When I call with name supportsInterface and value 0x80ac58cd it throws that error.
I can't pass an array of bytes or anything because anything other than a String throws an Eth::Abi::EncodingError on line 348.

Am I missing something or is it impossible to use bytes4 as an input type with the current implementation?

eth/abi: implement encode_packed/decode_packed

Given: I sign a message with a private key generated using eth.rb
Then: I get a signature
Expected: The signer of the message to be the signer verified on the smart contract

Ruby (Eth.rb) 
# Generate new key and wallet address
new_wallet = Eth::Key.new

# Array to be encoded 
token_ids = [1,2,3] 

 # Sign message
get_signature = Eth::Util.method(:prefix_hex) << new_wallet.method(:personal_sign) << Eth::Util
.method(:bin_to_prefixed_hex)  << Eth::Util.method(:keccak256)  << Eth::Abi.method(:encode)
 
get_signature.call(['uint256[]'], [token_ids])
# returns 0x656f70ae6747dc4de541fdaea84701a4b06feea22714cdae6b49ebd4e3c32d1b5b96dca9cf232add9ee0d0e35c951f3e940e93d26fcaf22a51413cd058402fb21c

Smart contract

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.9;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract VerifySignature {
    using ECDSA for bytes32;

    function getMessageHash(uint256[] memory  ids) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(ids));
    }


    function verify(bytes memory sig, uint256[] memory ids) external pure returns (bool) {
        bytes32 messagehash = keccak256(
            abi.encodePacked(ids)
        );
        return messagehash.toEthSignedMessageHash().recover(sig) == 0x2ceBaa61f571C38d41cFFc8605FB3C1ac4d7F6e7;
    }

    function getSigner(bytes memory sig, uint256[] memory ids) external pure returns (address) {
        bytes32 messagehash = getMessageHash(ids);
        return messagehash.toEthSignedMessageHash().recover(sig);
    }

}

getSigner
returns a different address from the new_wallet.address

@q9f @kurotaky
Is there a difference between how encodePacked in solidity works and encode in eth.rb?

Using v'0.5.3'

eth/contract greeter.sol - "Hash (NoMethodError) Did you mean? min" error

Hi All. Thank you very much for this eth gem.

Having trouble with my first ethgreeter test contract. Here is the module, ruby console and run-time error.

What am I missing? Must be something simple but I have read the docs over-and-over; banging head :)

module EthGem 
    require "eth"
   
    CHAINSTACK_ENDPOINT_ROPSTEN=ENV['CHAINSTACK_ENDPOINT_ROPSTEN'] unless const_defined?(:CHAINSTACK_ENDPOINT_ROPSTEN)
    METAMASK_ETH_ADDRESS=ENV['METAMASK_ETH_ADDRESS'] unless const_defined?(:METAMASK_ETH_ADDRESS)
    DEBUG = false unless const_defined?(:DEBUG)

    class Core
       def initialize
       end

       def self.greeter 
        chain = Eth::Client.create CHAINSTACK_ENDPOINT_ROPSTEN
        puts "METAMASK_ETH_ADDRESS: #{METAMASK_ETH_ADDRESS}"
        deposit_contract = Eth::Address.new METAMASK_ETH_ADDRESS
        balance = (chain.get_balance deposit_contract).to_f
        puts "Balance is #{balance.round(8)}"
        solc = Eth::Solidity.new
        contract = solc.compile "#{Rails.root}/greeter.sol"
        chain.deploy_and_wait(contract)
       end
    end
end

and the error:

irb(main):002:0> EthGem::Core.greeter
METAMASK_ETH_ADDRESS: "OMITTED: AVAILABLE ON REQUEST IF NEEDED TO DEBUG"
Balance is 4.0e+18              
/Users/tim/rails/eth/vendor/bundle/ruby/3.0.0/gems/eth-0.5.5/lib/eth/client.rb:182:in `deploy': undefined method `bin' for {"Greeter"=>{"abi"=>[{"inputs"=>[{"internalType"=>"string", "name"=>"_greeting", "type"=>"string"}], "stateMutability"=>"nonpayable", "type"=>"constructor"}, {"inputs"=>[], "name"=>"greet", "outputs"=>[{"internalType"=>"string", "name"=>"", "type"=>"string"}], "stateMutability"=>"view", "type"=>"function"}, {"inputs"=>[{"internalType"=>"string", "name"=>"_greeting", "type"=>"string"}], "name"=>"setGreeting", "outputs"=>[], "stateMutability"=>"nonpayable", "type"=>"function"}], "bin"=>"608060405234801561001057600080fd5b5060405161054c38038061054c83398101604081905261002f916100f8565b8051610042906000906020840190610049565b5050610201565b828054610055906101c7565b90600052602060002090601f01602090048101928261007757600085556100bd565b82601f1061009057805160ff19168380011785556100bd565b828001600101855582156100bd579182015b828111156100bd5782518255916020019190600101906100a2565b506100c99291506100cd565b5090565b5b808211156100c957600081556001016100ce565b634e487b7160e01b600052604160045260246000fd5b6000602080838503121561010b57600080fd5b82516001600160401b038082111561012257600080fd5b818501915085601f83011261013657600080fd5b815181811115610148576101486100e2565b604051601f8201601f19908116603f01168101908382118183101715610170576101706100e2565b81604052828152888684870101111561018857600080fd5b600093505b828410156101aa578484018601518185018701529285019261018d565b828411156101bb5760008684830101525b98975050505050505050565b600181811c908216806101db57607f821691505b6020821081036101fb57634e487b7160e01b600052602260045260246000fd5b50919050565b61033c806102106000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610050575b600080fd5b61004e6100493660046101c6565b61006e565b005b610058610085565b6040516100659190610277565b60405180910390f35b8051610081906000906020840190610117565b5050565b606060008054610094906102cc565b80601f01602080910402602001604051908101604052809291908181526020018280546100c0906102cc565b801561010d5780601f106100e25761010080835404028352916020019161010d565b820191906000526020600020905b8154815290600101906020018083116100f057829003601f168201915b5050505050905090565b828054610123906102cc565b90600052602060002090601f016020900481019282610145576000855561018b565b82601f1061015e57805160ff191683800117855561018b565b8280016001018555821561018b579182015b8281111561018b578251825591602001919060010190610170565b5061019792915061019b565b5090565b5b80821115610197576000815560010161019c565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156101d857600080fd5b813567ffffffffffffffff808211156101f057600080fd5b818401915084601f83011261020457600080fd5b813581811115610216576102166101b0565b604051601f8201601f19908116603f0116810190838211818310171561023e5761023e6101b0565b8160405282815287602084870101111561025757600080fd5b826020860160208301376000928101602001929092525095945050505050565b600060208083528351808285015260005b818110156102a457858101830151858201604001528201610288565b818111156102b6576000604083870101525b50601f01601f1916929092016040019392505050565b600181811c908216806102e057607f821691505b60208210810361030057634e487b7160e01b600052602260045260246000fd5b5091905056fea264697066735822122096013f8b33bbb3794d36acf00a07a399d713334cdb3716058e145765786b5c0664736f6c634300080e0033"}}:Hash (NoMethodError)
Did you mean?  min
irb(main):003:0> 

Thanks for helping me out.

empty access list bug

eth.sendRawTransaction("0x02f87282053980018504a817c800825208940c512b60531e2440edbee50f48bafdc6d291d8dd890246ddf9797668000080c080a075488ef7d2d6192758545f0020dd3e381a610068e6e7539860461ca25a952d38a0377b07f01be37bc1ebb48eb1ec9fa62c8e4ebf5c6b03d31a113b964b8c9e37e9")
Error: rlp: expected input list for types.txdata
	at web3.js:6347:37(47)
	at web3.js:5081:62(37)
	at <eval>:1:23(4)

https://ethereum.stackexchange.com/questions/119252/eip-2930-1559-is-an-empty-access-list-supposed-to-be-80-or-c0

gem install eth error

hello,
ruby version: 3.0.3
my error:

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/sai/.rvm/gems/ruby-3.0.3/gems/rbsecp256k1-5.1.0/ext/rbsecp256k1
/Users/sai/.rvm/rubies/ruby-3.0.3/bin/ruby -I /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0 -r ./siteconf20220707-73597-f0ln4m.rb extconf.rb
2 retrie(s) left for libsecp256k1.zip (Net::OpenTimeout)
1 retrie(s) left for libsecp256k1.zip (execution expired)
0 retrie(s) left for libsecp256k1.zip (execution expired)
execution expired
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
	--with-opt-dir
	--without-opt-dir
	--with-opt-include
	--without-opt-include=${opt-dir}/include
	--with-opt-lib
	--without-opt-lib=${opt-dir}/lib
	--with-make-prog
	--without-make-prog
	--srcdir=.
	--curdir
	--ruby=/Users/sai/.rvm/rubies/ruby-3.0.3/bin/$(RUBY_BASE_NAME)
	--with-system-library
	--without-system-library
/Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/digest.rb:50:in `initialize': No such file or directory @ rb_sysopen -
/Users/sai/.rvm/gems/ruby-3.0.3/gems/rbsecp256k1-5.1.0/ext/rbsecp256k1/ports/archives/libsecp256k1.zip (Errno::ENOENT)
	from /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/digest.rb:50:in `open'
	from /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/digest.rb:50:in `file'
	from /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/digest.rb:35:in `file'
	from /Users/sai/.rvm/gems/ruby-3.0.3/gems/mini_portile2-2.8.0/lib/mini_portile2/mini_portile.rb:335:in `verify_file'
	from extconf.rb:50:in `download'
	from /Users/sai/.rvm/gems/ruby-3.0.3/gems/mini_portile2-2.8.0/lib/mini_portile2/mini_portile.rb:182:in `cook'
	from extconf.rb:83:in `<main>'

extconf failed, exit code 1

Gem files will remain installed in /Users/sai/.rvm/gems/ruby-3.0.3/gems/rbsecp256k1-5.1.0 for inspection.
Results logged to /Users/sai/.rvm/gems/ruby-3.0.3/extensions/x86_64-darwin-18/3.0.0/rbsecp256k1-5.1.0/gem_make.out

  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/builder.rb:93:in `run'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/ext_conf_builder.rb:47:in `block in build'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/tempfile.rb:317:in `open'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/ext_conf_builder.rb:26:in `build'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/builder.rb:159:in `build_extension'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/builder.rb:193:in `block in build_extensions'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/builder.rb:190:in `each'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/ext/builder.rb:190:in `build_extensions'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/rubygems/installer.rb:845:in `build_extensions'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/rubygems_gem_installer.rb:71:in `build_extensions'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/rubygems_gem_installer.rb:28:in `install'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/source/rubygems.rb:200:in `install'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/installer/gem_installer.rb:54:in `install'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/installer/gem_installer.rb:16:in `install_from_spec'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/installer/parallel_installer.rb:186:in `do_install'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/installer/parallel_installer.rb:177:in `block in worker_pool'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/worker.rb:62:in `apply_func'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/worker.rb:57:in `block in process_queue'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/worker.rb:54:in `loop'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/worker.rb:54:in `process_queue'
  /Users/sai/.rvm/rubies/ruby-3.0.3/lib/ruby/3.0.0/bundler/worker.rb:91:in `block (2 levels) in create_threads'

An error occurred while installing rbsecp256k1 (5.1.0), and Bundler cannot continue.

In Gemfile:
  eth was resolved to 0.5.6, which depends on
    rbsecp256k1

rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.DynamicFeeTx).R

https://github.com/q9f/eth.rb/runs/4895235740?check_suite_focus=true

Failures:

  1) Eth::Client.transfer .transfer_and_wait funds a random account using legacy transactions
     Failure/Error: raise IOError, output["error"]["message"] unless output["error"].nil?

     IOError:
       rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.DynamicFeeTx).R
     # ./lib/eth/client.rb:207:in `send_command'
     # ./lib/eth/client.rb:191:in `block (2 levels) in <class:Client>'
     # ./lib/eth/client.rb:141:in `transfer'
     # ./lib/eth/client.rb:101:in `transfer_and_wait'
     # ./spec/eth/client_spec.rb:65:in `block (3 levels) in <top (required)>'

Not a bug, but a question seeking to reach the repository owner.

Hello, @q9f, I donโ€™t who to reach nor I found a way to contact you since the discord shutdown, but openethereum didnโ€™t reached the end of itโ€™s course, as there are many analysis tools relying on parity_liststoragekeys which is also required for building token richs lists on past blocks and Erigon is unwilling to break database compatibility for bringing the required equivalent fatdb feature which powered this.

I recognize this isnโ€™t the right place to talk about this, so would it be possible to have a chat about this or with the repository owner (taking over)?

OpenSSL support. Release 0.5 still supports openssl 3.0 but the 0.5.3 branch has be downgraded to OpenSSL 2.2

Hi there,

Is there any plan to bring this back up to openssl 3.0? I need to use the branch 0.5.3 because I need the ability to call a contract which doesn't seem to be supported yet in the main release branch.


Bundler could not find compatible versions for gem "openssl":
  In snapshot (Gemfile.lock):
    openssl (= 3.0.0)

  In Gemfile:
    openssl

    eth (= 0.5.3) was resolved to 0.5.3, which depends on
      openssl (~> 2.2)

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.

I've tried to downgrade the Openssl gem to version to 2.2 but having issues getting the install to work correctly on my machine.

My main question is, will the OpenSSL 3 dependency be coming back once this hits main branch?

Thanks

How to call contract Functions and send Tx?

Hi,

im new to this gem but i already have experience with other web3 SDK's. My Question is:

How can i call a contract function for example "SwapExactETHForToken" and put Token A and Token B into the payload? And how can i send the transaction afterwards? Sorry i dont understand the documentation.

I have use Eth::Contract.from_abi because i think that this is the wrapper for the contract itself. Then i want to use my client to build the transaction payload: client.transact(contract, "SwapExactETHForToken").

But it didnt work....

How can i put the TokenA and TokenB into the transaction?

For example in Javascript:

const tx_builder = await contract.methods.swapExactETHForTokens(
  0,
  [tokenSELL, tokenBUY],
  sender_address,
  expiryDate
);

let encoded_tx = tx_builder.encodeABI();

let transactionObject = {
  gas: 200000,
  gasPrice: web3.utils.toWei("10", "gwei"),
  data: encoded_tx,
  value: qty,
  from: sender_address,
  to: dexRouter,
  nonce: nonce,
};

after this i sign the transaction. Could you tell me if i can do the same with this Ruby Gem?
Or could you explain to me how i can call a contract function and als put data into the payload to send the transaction afterwards?

Thanks in advance

Sign array of arguments

I have the following code:

message = [
  157560238208251294832227588492080579756,
  210000000000000000000,
  1658508461,
  "0x0063046686E46Dc6F15918b61AE2B121458534a5",
  "borrower0272",
]

key = Eth::Key.new priv: 'MY_PRIV_HERE'
key.personal_sign(message, 1337)

The signature that generate its messed up. When i try to verify, it returns another private key.

I am verifying with:

    function tokenizeAsset(uint256 _uuid,
                           uint256 _amount,
                           uint256 _dueDate,
                           address _borrowerAddress,
                           string memory _documentNumber,
                           bytes memory signature)
      public
      onlyRole("MINTER")
      returns (uint256)
    {
        validateParametersAuthenticity(keccak256(abi.encodePacked(_uuid, _amount, _dueDate, _borrowerAddress, _documentNumber)),
                                       signature,
                                       owner());
       // more code here
       }
       
           function validateParametersAuthenticity(bytes32 message, bytes memory signature, address owner) internal {
        address signer = recoverSigner(addPrefixToMessage(message), signature);
        require(signer == owner,
                S.join("The message might be tampered. Expected signer ", S.fromAddress(owner),
                       ", but got ", S.fromAddress(signer)));
    }

    function recoverSigner(bytes32 message, bytes memory sig) internal pure returns(address) {
        uint8 v;
        bytes32 r;
        bytes32 s;
        (v, r, s) = splitSignature(sig);
        return ecrecover(message, v, r, s);
    }

    function addPrefixToMessage(bytes32 message) internal pure returns(bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", message));
    }

    function splitSignature(bytes memory sig)
        internal
        pure
        returns (uint8, bytes32, bytes32) {
            require(sig.length == 65);
            bytes32 r;
            bytes32 s;
            uint8 v;
            assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) }

            return (v, r, s);
    }

On python, i can sign with:

    def sign(self, types, message):
        msg = Web3.soliditySha3(types, message)
        full = encode_defunct(hexstr=msg.hex())
        result = w3.eth.account.sign_message(full, private_key=self.privateKey())
        return result.signature.hex()

Is it possible to sign in this way with this gem?

Reference in Python: https://github.com/ethereum/web3.py/blob/master/web3/main.py#L304

eth/contract hello_world.sol : Unsupported method: eth_coinbase. Alchemy does not support mining eth...

Hi Again, and thanks for this great gem and your fast support.

Made the change you suggested in (now closed) issue #113, but this leads to another error:

client.rb:453:in `send_command': etherbase must be explicitly specified (IOError)

Here is the module as tested:

module EthGem 
    require "eth"
    require "colorize"
   
    require_relative "#{Rails.root}/lib/assets/config.rb"

    class Core
       include Endpoint
       include Wallet
       def initialize
       
       end

       def self.contract(file)
        return nil if file.nil?
        chain = Eth::Client.create Endpoint::Provider.chainstack_ropsten
        puts "METAMASK_ETH_ADDRESS: #{Wallet::Address.metamask_eth}"
        deposit_contract = Eth::Address.new Wallet::Address.metamask_eth
        balance = (chain.get_balance deposit_contract).to_f
        puts "Balance is #{balance.round(8)}"
        
        contract_file = "#{Rails.root}/lib/assets/contracts/#{file}.sol"
        puts "FILE: #{contract_file}"
        contract = Eth::Contract.from_file(file: contract_file)   # changed based on issue #113
        
        #pp contract
        #pp chain
        chain.deploy_and_wait(contract)
       end

    end
end

Here is the calling (rails console) process:

MacStudio:eth tim$ rails c
Loading development environment (Rails 7.0.3)
irb(main):001:0> require "#{Rails.root}/lib/assets/eth.rb"
=> true
irb(main):002:0> EthGem::Core.contract("hello_world")
METAMASK_ETH_ADDRESS: 0xE179C056024150d56A4e94af9C5A36BCC0B4e502
Balance is 9.39999369999997e+19 
FILE: /Users/tim/rails/eth/lib/assets/contracts/hello_world.sol
/Users/tim/rails/eth/vendor/bundle/ruby/3.0.0/gems/eth-0.5.5/lib/eth/client.rb:453:in `send_command': etherbase must be explicitly specified (IOError)
irb(main):003:0> 

Looking at the line (453) where the error happens:

443     # Prepares parameters and sends the command to the client.
444     def send_command(command, args)
445       args << "latest" if ["eth_getBalance", "eth_call"].include? command
446       payload = {
447         jsonrpc: "2.0",
448         method: command,
449         params: marshal(args),
450         id: next_id,
451       }
452       output = JSON.parse(send(payload.to_json))
453       raise IOError, output["error"]["message"] unless output["error"].nil?
454       return output
455     end

hello_world.sol

MacStudio:eth tim$ cat /Users/tim/rails/eth/lib/assets/contracts/hello_world.sol
 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;           
contract HelloWorld {             
   function helloWorld() external pure returns (string memory) {
   return "Hello, World!";        
   }                              
}                                 

FWIW, get this same error with my earlier (issue #113) greeter.sol file as well:

irb(main):003:0> require "#{Rails.root}/lib/assets/eth.rb"
=> false
irb(main):004:0> EthGem::Core.contract("greeter")
METAMASK_ETH_ADDRESS: 0xE179C056024150d56A4e94af9C5A36BCC0B4e502
Balance is 9.39999369999997e+19                                                             
FILE: /Users/tim/rails/eth/lib/assets/contracts/greeter.sol                                 
/Users/tim/rails/eth/vendor/bundle/ruby/3.0.0/gems/eth-0.5.5/lib/eth/client.rb:183:in `deploy': Missing contract constructor params! (ArgumentError)
irb(main):005:0> 

I read the updated wiki (thanks for updating the docs, we all know that docs are our least favorite thing to do) and reviewed the steps again. Am I following your docs, to the letter, for endpoints such as CHAINSTACK or ALCHEMY, etc.? I think so, but maybe I'm missing something? Have reviewed the docs many times, but it seems I am following....

Where did I go wrong this time?

Thank you.

Tuple supoort

is it possible to encode/decode tuples?

pry(main)> Eth::Abi.encode(['(string,uint256)'],'("test",1025)')
Eth::Abi::Type::ParseError: Unknown base type
from /usr/local/bundle/gems/eth-0.5.1/lib/eth/abi/type.rb:173:in `validate_base_type'

Incorrect gas_limit in the constructor

Hi, thanks for this gem, I really like it.

That said, the gas_limit parameter doesn't seem to work.

Example:

> goerli
=> #<Eth::Client::Http:0x0000556304bc2b10
 @chain_id=5,
 @gas_limit=20000000,
 @host="nd-123-456-789.p2pify.com",
 @id=0,
 @max_fee_per_gas=31000000000.0,
 @max_priority_fee_per_gas=30000000000.0,
 @port=443,
 @ssl=true,

result. The gas limit is 57100.

> ropsten
=> #<Eth::Client::Http:0x000055630503da50
 @gas_limit=200000,
 @host="nd-123-456-789.p2pify.com",
 @id=0,
 @max_fee_per_gas=31000000000,
 @max_priority_fee_per_gas=30000000000,
 @port=443,
 @ssl=true,

result (different source contract). The gas limit is 56064.

#111 โ€” same issue or related?

Thanks!

Eth::Contract::FunctionInput drops array brackets from type

When initializing Eth::Contract::FunctionInput with an array type (e.g. uint256[]) it returns the a non-array type on it's type method.

This example should make it clear:

Eth::Contract::FunctionInput.new({ "name" => "array", "type" => "uint256[]" }).type
# "uint256"

honor intrinsic gas for eip-2718 transactions

The intrinsic cost of the new transaction is inherited from EIP-2930, specifically 21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count.

Unclear reason behind `Eth::Abi::DecodingError` after calling RPC function

Description

While trying to call the balanceOf RPC method on the CRYPTOPUNKS contract I encountered Eth::Abi::DecodingError.

I am instanting the contract using #from_abi with the ABI taken from the official source.

The code is currently working under the combination of eth.rb and ethereum.rb and my intention is to make use of the newly added smart contract interaction from eth.rb & drop usage of ethereum.rb altogether.

The official example does seem to work locally, but I don't understand much from this error & the algorithm behind the decoder seems rather complicated (or at least I can't seem to figure out what the issue might be by looking at the code).

[28] pry(#<Blockchain::Ethereum::SmartContracts::CryptoPunks::SmartContract>)> @client
=> #<Eth::Client::Http:0x0000ffff70f3b2c0
 @chain_id=5,
 @gas_limit=21000,
 @host="goerli.infura.io",
 @id=8,
 @max_fee_per_gas=0.2e11,
 @max_priority_fee_per_gas=0,
 @port=443,
 @ssl=true,
 @uri=#<URI::HTTPS https://goerli.infura.io/v3/obfuscated_project_id>>
[29] pry(#<Blockchain::Ethereum::SmartContracts::CryptoPunks::SmartContract>)> @contract
=> #<Eth::Contract::CRYPTOPUNKS:0x0000ffff70dd3810>
[30] pry(#<Blockchain::Ethereum::SmartContracts::CryptoPunks::SmartContract>)> @client.call(@contract, 'balanceOf', address)
Eth::Abi::DecodingError: Not enough data for head
from /usr/local/bundle/gems/eth-0.5.4/lib/eth/abi.rb:186:in `decode'

Expected behavior

Successfully call client.call(contract, 'balanceOf', address).


I realise there's all the chances I'm doing something wrong, so this might not necessarily be due to a bug.

Contract unable to verify signed message on Rinkeby (invalid V value)

Observation:

Typed data signature using eth.rb vs ethers.js differ in last bytes (both set to Chain ID 4). eg:

// ethers.js
0x3e2a2ced47f08d207d3fdf7804e20ef56cff751e3f0ce18e49bee84c9d914e06216e9623aa140643bbcac707498b63ec2ad26654d21ce0d83cab781d0cfb71021b

// eth.rb
0x3e2a2ced47f08d207d3fdf7804e20ef56cff751e3f0ce18e49bee84c9d914e06216e9623aa140643bbcac707498b63ec2ad26654d21ce0d83cab781d0cfb71022b

When sent to a contract on Rinkeby ethers.js signature can be verified, but eth.rb fails with message invalid 'v' value.
Interestingly ethers.js can verify both messages and return correct address.

In the code I've noticed following implementation:

# Converts a recovery ID into the expected `v` on a given chain.
#
# @param recovery_id [Integer] signature recovery id.
# @param chain_id [Integer] the chain id the signature was generated on.
# @return [Integer] the signature's `v` value.
def to_v(recovery_id, chain_id = ETHEREUM)
  v = 2 * chain_id + 35 + recovery_id
end

EIP-155 says:

If block.number >= FORK_BLKNUM and CHAIN_ID is available, then when computing the hash of a transaction for the purposes of signing, instead of hashing only six rlp encoded elements (nonce, gasprice, startgas, to, value, data), you SHOULD hash nine rlp encoded elements (nonce, gasprice, startgas, to, value, data, chainid, 0, 0). If you do, then the v of the signature MUST be set to {0,1} + CHAIN_ID * 2 + 35 where {0,1} is the parity of the y value of the curve point for which r is the x-value in the secp256k1 signing process. If you choose to only hash 6 values, then v continues to be set to {0,1} + 27 as previously.

Following change fixed the issue:

- v = 2 * chain_id + 35 + recovery_id
+ v = 27 + recovery_id

However I'm 100% uncertain if that's a fix or I'm simply missing something else?

Thanks for your work on this!

Using (Polygon) Mumbai Testnet Chain Id 80001 forces error 'Invalid signature v byte 27 for chain ID 80001!'

Hoping this is the correct manner in which to raise queries? I'm using ruby-eth (0.5.4 looking at my gemfile.lock) to verify metamask signatures (personal sign'ed). My implementation has been stable/fine with default Chain ID (1), but now I am looking to bind to Chain Id 8001. Testing this has driven errors 'Invalid signature v byte 27 for chain ID 80001!'.

The function I call is Eth::Signature::personal_recover - passing in Chain_id 80001

    def personal_recover(message, signature, chain_id = Chain::ETHEREUM)
      prefixed_message = prefix_message message
      hashed_message = Util.keccak256 prefixed_message
      recover hashed_message, signature, chain_id
    end

As this delegates to the Eth::Signature::recover function the dissect function is called and in tracing that I see the 'v' value is 27.

  def recover(blob, signature, chain_id = Chain::ETHEREUM)
      context = Secp256k1::Context.new
      r, s, v = dissect signature
      v = v.to_i(16)
      raise SignatureError, "Invalid signature v byte #{v} for chain ID #{chain_id}!" if v < chain_id
      recovery_id = Chain.to_recovery_id v, chain_id
      signature_rs = Util.hex_to_bin "#{r}#{s}"
      recoverable_signature = context.recoverable_signature_from_compact signature_rs, recovery_id
      public_key = recoverable_signature.recover_public_key blob
      Util.bin_to_hex public_key.uncompressed
    end

The error I am receiving seems to be a very simple logic trap, in which the SignatureError is raised if v (27) is less than the chain_id (80001).

If this is the case, how can i use a chain with an id more than 27? Apologies if i may be missing something contextual here (i.e. some implied blockchain convention), but looking purely at the execution path in my code, and into this gem, i can only see my issue being 27 < 80001.

Is this an intended constraint within the gem logic?

eth/api: update apis to latest

making a call to max_priority_fee_per_gas (the call in api.rb is wrong -- should be called eth_maxPriorityFeePerGas not eth_getMaxPriorityFeePerGas

eth/contract: "invalid argument 0: hex string has length 40, want 64 for common.Hash"

Working with engineers from Chainstack and Infura, was able to get gas to work finally. Now, this is the error when deploying contracts:

{"jsonrpc":"2.0","id":25,"error":{"code":-32602,"message":"invalid argument 0: hex string has length 40, want 64 for common.Hash"}}

I traced this to client.rb in the params in the payload in send_command. Printing each time this method is called, the params change from 64 (0x + 64) chars to 40 (0x + 40) chars before the failure.

...........end debug...........
ARGS...........
["0xBA8Ca77d45161FCbfb591F77cc1303d7151f2C4E"]
END ARGS...........
def marshal(params) ["0xBA8Ca77d45161FCbfb591F77cc1303d7151f2C4E"]
ARRAY
def marshal(params) 0xBA8Ca77d45161FCbfb591F77cc1303d7151f2C4E
Hex
......payload................
{:jsonrpc=>"2.0",
 :method=>"eth_getTransactionByHash",
 :params=>["0xBA8Ca77d45161FCbfb591F77cc1303d7151f2C4E"],
 :id=>25}
......output................
parse source {"jsonrpc":"2.0","id":25,"error":{"code":-32602,"message":"invalid argument 0: hex string has length 40, want 64 for common.Hash"}}
{"jsonrpc"=>"2.0",
 "id"=>25,
 "error"=>
  {"code"=>-32602,
   "message"=>
    "invalid argument 0: hex string has length 40, want 64 for common.Hash"}}
...........end debug...........

This param is causing the error:

 :params=>["0xBA8Ca77d45161FCbfb591F77cc1303d7151f2C4E"],

The length is wrong; but I am not sure why.

Normally, it is like this:

ARGS...........
["0x8d2103f1592b6e978230c51341e45c6c0e65f8460d65397793cbf963add0e33f"]
END ARGS...........
def marshal(params) ["0x8d2103f1592b6e978230c51341e45c6c0e65f8460d65397793cbf963add0e33f"]
ARRAY
def marshal(params) 0x8d2103f1592b6e978230c51341e45c6c0e65f8460d65397793cbf963add0e33f
Hex

Doesn't support structs in emitted events

Various errors when trying to decode logs from this contract:

// SDPX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../openzeppelin/[email protected]/contracts/token/ERC20/ERC20.sol";
import "../openzeppelin/[email protected]/contracts/security/Pausable.sol";
import "../openzeppelin/[email protected]/contracts/access/Ownable.sol";

struct Tip { 
    address sender;
    uint128 post;
    address author;
    uint amount;
}

contract FolkTipping is Pausable, Ownable {
    Tip[] public tips;
    mapping(uint128 => uint) public postValue;
   
    address internal tokenAddress;
    uint public minTip;
    uint public maxTip;

    event NewTip(Tip tip);
    event NewTipForAuthor(address author, Tip tip);
    event NewTipForPost(uint128 post, Tip tip);
   
    constructor (address _tokenAddress) {
        tokenAddress = _tokenAddress;
        minTip = 1;
        maxTip = type(uint).max;
    }
   
    function tip (uint128 post, address author, uint amount) public whenNotPaused {
        ERC20 token = ERC20(tokenAddress);

        require(author != msg.sender, 'Author cannot be sender');
        require(post > 0, 'Post id cannot be null');
        require(amount >= minTip, 'Amount less than minTip');
        require(amount < maxTip, 'Amount more than maxTip');
        require(token.transferFrom(msg.sender, author, amount));

        Tip memory t = Tip(msg.sender, post, author, amount);
        tips.push(t);
        postValue[post] += amount;

        emit NewTip(t);
        emit NewTipForAuthor(author, t);
        emit NewTipForPost(post, t);
    }

   function getTipCount() public view returns(uint result){
        result = tips.length;
   }

   function getTip(uint index) public view returns(Tip memory result){
        result = tips[index];
   }

   function getPostValue(uint128 post) public view returns(uint result){
        result = postValue[post];
   }

    function setMin (uint t) public onlyOwner {
        require(t < maxTip);
        minTip = t;
    }

    function setMax (uint t) public onlyOwner {
        require(t > minTip);
        maxTip = t;
    }

    function pause () public onlyOwner {
        _pause();
    }

    function unpause () public onlyOwner {
        _unpause();
    }
}

I'm working on adding support for structs / tuples, any pointers for me? Otherwise I'll submit a PR when it's ready.

eth/contract Testing ETH Contract Deployment using the `eth` Ruby gem against provider endpoints

Hope this helps.

I summarized some eth issues found when testing with four different blockchain endpoint providers here:

https://community.unix.com/t/testing-eth-contract-deployment-using-the-eth-ruby-gem-against-provider-endpoints/387004

Not sure the best was to continue reporting these issues, as I do not wish to be a pain and inundate anyone with these various issues encountered with testing various endpoints.

Kindly let me know how to proceed without being annoying. Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.