web3-eth icon indicating copy to clipboard operation
web3-eth copied to clipboard

Sending a transaction

Open woniesong92 opened this issue 7 years ago • 25 comments

Is there a way to call a function (i.e. send a transaction) with this library? If I wanted to programmatically call a function that updates blockchain state, what's the best way to do it?

woniesong92 avatar Feb 15 '18 04:02 woniesong92

Signing transactions is not implemented in this GEM.

astudnev avatar Feb 18 '18 09:02 astudnev

@astudnev Do you have a plan to implement it?

drselump14 avatar Feb 22 '18 03:02 drselump14

No direct plan, but would be very nice

astudnev avatar Feb 22 '18 05:02 astudnev

I am thinking to implement it.... Any ideas? Someone wants to collaborate?

lucasperez avatar Mar 12 '18 22:03 lucasperez

Don't mean to steal anyone's thunder. But I have basic functionality for sendTransaction (not ready yet for arrays) in my library here https://github.com/hswick/w3 and I am looking for collaborators as I can only work on it part time.

There is also https://github.com/EthWorks/ethereum.rb which is fairly developed

hswick avatar Mar 12 '18 23:03 hswick

Hey people, why reinvent the wheel, for signing transactions you can use https://github.com/se3000/ruby-eth - would be more useful to try and fix some of the quirks of that library (one of them I will fix as soon as time permits...)

Then you can simply do:

#================= web3 = Web3::Eth::Rpc.new host: 'ropsten.infura.io', port: 443,
connect_options: { open_timeout: 20, read_timeout: 140, use_ssl: true, rpc_path: '/keyxxxxxx' }

res = web3.eth.getTransactionCount ['.....acct.......', 'pending'] tc = res.to_i(16)

tx = Eth::Tx.new({ data: "0x075624e1", # this is incrementTwice() - to do: hex data from any function input gas_limit: 210_000, gas_price: 20_000_000, nonce: tc, to: "0xc015edDe79C543A4a7aa7e54EC590cB9cCC5E6ae", value: 0, })

key = Eth::Key.new priv: '....acct_private_key.....' tx.sign key puts tx.hash res = web3.eth.sendRawTransaction([tx.hex]) #=================

The more tricky part (from my point of view) is to properly encode the data - this seems to be possible with web3-eth but need to test it.

alex-kampa avatar Mar 13 '18 18:03 alex-kampa

@hswick - apart from the signing, all the necessary functions to send transactions seem to be implemented in a pretty comprehensive way in the Ethereum.rb gem (https://github.com/EthWorks/ethereum.rb) which does have some minor quirks but generally works (only with parity at the moment). However, that gem does not expose most of its useful functions, such as input data encoding, so instead of trying to redo, we could work on doing that. What do you think?

alex-kampa avatar Mar 13 '18 19:03 alex-kampa

@alex-kampa I like the simplicity of ruby-eth for signing transactions. Anyone looking on this thread would probably benefit from the use of it.

In regards to ethereum.rb I address this in my README. I'm a fairly opinionated person (as I think conviction makes good software), and I have actually been stealing some of the internals for w3 from ethereum.rb (which I reference). It isn't a matter of functionality, it is about design. My design isn't based on just becoming feature parity with something like web3.js, it is about surpassing it.

hswick avatar Mar 13 '18 19:03 hswick

@hswick I just went through your w3 library, it looks promising. I'll check it out.

ToJen avatar Apr 03 '18 13:04 ToJen

I'm also trying to send transaction as @woniesong92 my code here

        key = Eth::Key.new priv: privateKey
        gas_limit = 200000
        gas_price = $web3.eth.gasPrice().to_i(16)
        nonce = $web3.eth.getTransactionCount([key.address, 'pending']).to_i(16)
        tx = Eth::Tx.new({
                gas_limit: 210000,
                gas_price: gas_price,
                nonce: nonce,
                from: key.address,
                to: address_to,
                value: amount,
                data: ""
            })

        tx.sign(key)
        
        result = $web3.eth.sendRawTransaction(tx.hex)

then error respose

{
    "status": 500,
    "error": "Internal Server Error",
    "exception": "#<RuntimeError: Error code 400 on request https://rinkeby.infura.io/kEICAYHkrSFjduGKSOR5 {\"jsonrpc\":\"2.0\",\"method\":\"eth_sendRawTransaction\",\"params\":\"0xf86480843b9aca0083033450945dc73424b8bb75d22cd728dc8dbb2aad77f5fd4680801ba096c8949ec52baa4ef55fdedbc138ed330717a7495ecc70cffd7d4ce05d4cb389a06f3c88b4323cd4f6f0d8bfdb51e788faff621733d48f77622cd598341edb6b15\",\"id\":6401146}>",
    "traces": {
        "Application Trace": [
            {
                "id": 5,
                "trace": "app/walletapi/eth_client.rb:45:in `drawal'"
            },
            {
                "id": 6,
                "trace": "app/controllers/api/v1/wallets_controller.rb:137:in `withdrawal'"
            }
        ],
        "Framework Trace": [
            {
                "id": 0,
                "trace": "web3-eth (0.2.16) lib/web3/eth/rpc.rb:44:in `block in request'"

please help me.

marknguyen85 avatar Jun 06 '18 18:06 marknguyen85

Any luck with that problem @marknguyen85?

jlstr avatar Jun 13 '18 17:06 jlstr

Hello @alex-kampa,

In your example above:

tx = Eth::Tx.new({
  data: "0x075624e1", # this is incrementTwice() - to do: hex data from any function input
  gas_limit: 210_000,
  gas_price: 20_000_000,
  nonce: tc,
  to: "0xc015edDe79C543A4a7aa7e54EC590cB9cCC5E6ae",
  value: 0,
})

Could you please be more clear as to what goes in the data field? it seems this is what @marknguyen85 Could be missing as well?

Thank you very much.

jlstr avatar Jun 13 '18 17:06 jlstr

i've resolved with code below

result = $web3.eth.sendRawTransaction([tx.hex])

@jlstr i think data is not require

marknguyen85 avatar Jun 14 '18 01:06 marknguyen85

Awesome thanks!

@marknguyen85 May I ask one follow up Question, How do you get a contract object?

I'm trying to load it from a Ruby Hash:

contract = web3.eth.contract(Abi)
contract_instance = contract.at('0x0.....')

Where Abi is the standard Contract, ie. a Ruby Array behind the scenes But it does not work!

The above does not allow me to do: contract_instance.balanceOf('0x....') Throws NoMethodError and I wonder If the ABI has to be in a specific format? any ideas,

Please Help.

jlstr avatar Jun 14 '18 02:06 jlstr

@jlstr I'm also problems as you with methods of constract instance

marknguyen85 avatar Jun 18 '18 04:06 marknguyen85

@jlstr I found the cause of the error, Abi must be formatted string instead of array ex Abi = []

format to => Abi = '[]'

marknguyen85 avatar Jun 18 '18 07:06 marknguyen85

Awesome! Thank you @marknguyen85, And How are you calling contract methods?

jlstr avatar Jun 18 '18 13:06 jlstr

@jlstr as you, i don't know how to calling contract methods. When call contract method alway get error Function transfer is not constant: transfer, requires to sign transaction

i found in this gem code

def call_contract contract_address, method_name, args
        function = functions[method_name]
        raise "No method found in ABI: #{method_name}" unless function
        raise "Function #{method_name} is not constant: #{method_name}, requires to sign transaction" unless function.constant
        function.do_call web3_rpc, contract_address, args
      end

=> raise "Function #{method_name} is not constant: #{method_name}, requires to sign transaction"

have you any idea for this processing?

marknguyen85 avatar Jun 19 '18 10:06 marknguyen85

Hello @marknguyen85, I have figured it out, and let me tell you, you're in the right track, but the key is that you have to serialize the entire contract method you'll call according to the ABI Spec. Once serialized, you will get a long HEX string that looks like: 0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001

That hex string goes in the data field of the TX:

tx = Eth::Tx.new({ data: hex_data, gas_limit: 210_000, gas_price: gas_price, nonce: nonce, to: contract_address, from: address_that_is_signing_this_transaction, value: 0 })

The rest is exactly as you have in your example, but remember that the to will always be the contract's address in Ethereum.

Let me know if require any further help. -Jose

jlstr avatar Jun 19 '18 14:06 jlstr

@jlstr thanks you. I have read the link that you submitted (ABI Spec) but don't know how to create data_hash. Can you just me how to generate param hex?

00000000000000000000000000000000000000000000000000000000000000045 or 0000000000000000000000000000000000000000000000000000000000000001

marknguyen85 avatar Jun 20 '18 04:06 marknguyen85

The answer to your Question is very broad, in the hex strings you have pasted above, what are you trying to represent? a number (uint256)?

I will teach you how I personally generate a uint256 as an example so that you have an idea of what to do next, let say you want to pass the decimal value 10 to a smart contract's method:

value = 10

First you have to convert it into WEI, easy enough, just multiply by a 10^18 constant

value = value * 1_000_000_000_000_000_000

Now, you have to convert it to hexadecimal:

hex_value = "%x" % (value)

Finally, pad the hexadecimal value with 0s to complete a 64 bit long hex string that's required by Ethereum:

hex_value = hex_value.rjust(64, "0")

This example will translate a decimal number like 10 into a uint256 type, I have examples for other types, but I hope this will give you an idea of how to do it.

Kindly, Jose

jlstr avatar Jun 20 '18 14:06 jlstr

@jlstr thanks

marknguyen85 avatar Jun 21 '18 02:06 marknguyen85

@jlstr - some of the encoding was also done by @hswick at https://github.com/hswick/w3/blob/master/lib/w3/encoder.rb

It does not seem to handle all cases though - for example arrays as inputs are probably a bit tricky, and this includes strings which are handled like arrays as far as i know. If anyone here manages to do the encoding in the most generic way, please let us know.

alex-kampa avatar Jun 22 '18 14:06 alex-kampa

BTW, the specs for the encoding are here: http://solidity.readthedocs.io/en/develop/abi-spec.html

alex-kampa avatar Jun 22 '18 14:06 alex-kampa

Thank you @alex-kampa I did use that very same spec in order to do the parsing, too bad it's so coarse.

jlstr avatar Jun 22 '18 15:06 jlstr