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

Function::decode_input need help

Open Antake2333 opened this issue 3 years ago • 13 comments

input data=Bytes([24, 203, 175, 229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 196, 154, 165, 147, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 198, 5, 152, 144, 190, 233, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 100, 61, 138, 212, 127, 55, 193, 43, 206, 248, 33, 146, 238, 213, 82, 224, 138, 193, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 97, 163, 109, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 240, 6, 238, 103, 47, 143, 57, 198, 230, 60, 167, 91 , 28, 161, 64, 103, 179, 195, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 76, 219, 156, 189, 54, 176, 27, 209, 203, 174, 191, 45, 224, 141, 145, 115, 188, 9, 92]);

if transaction != None && transaction.as_ref().unwrap().to == Some(self.router_address) { println!("got hash_id: {:?}", hash_id); let input_data = &transaction.as_ref().unwrap().input; let data = self.router_contract .abi() .function("swapExactETHForTokens") .unwrap().decode_output((&hex::encode(&input_data.0)).as_bytes()).unwrap(); println!("{:?}", data); }

when i decode this input_data,error:InvalidDatam,How can I strive to parse the byte array into correct data?

Antake2333 avatar Nov 28 '21 14:11 Antake2333

+1

gogopro-dev avatar Dec 06 '21 20:12 gogopro-dev

Same here, given a transaction t and the router abi I have:

    for f in context.router.abi().functions() {
        match f.decode_input(&t.input.0) {
            Ok(tokens) => {
                println!(
                    "Transaction {} called function {} with args {:?}",
                    t.hash, f.name, tokens
                );
            }
            _ => (),
        }
    }

and for some reason it matches multiple functions with multiple parameters. However taking a random swap transaction you can see it identifies the swapExactTokensForETHSupportingFeeOnTransferTokens function.

I've also tried to match the function by the first 4 bytes which match the first 4 bytes of the input data:

        if f.short_signature() == fn_sign_bytes {
            let input = f.decode_input(&t.input.0).unwrap();
            println!("input {:?}", input);
        }

however I get invalid data

Any idea? It would be also nice to be able to figure out the function by itself, like web3.py

alex88 avatar Dec 10 '21 07:12 alex88

Found the problem, decode_input doesn't consider that the first 4 bytes are the function signature. You can use f.decode_input(&t.input.0[4..]).unwrap(); which should work

alex88 avatar Dec 10 '21 07:12 alex88

This way you can do something like:

    let fn_sign_bytes = &t.input.0[0..4];
    for f in router.abi().functions() {
        if f.short_signature() == *fn_sign_bytes {
            let input = f.decode_input(&t.input.0[4..]).unwrap();
            println!(
                "Transaction {} called function {}:{} with args {:?}",
                t.hash,
                f.name,
                hex::encode(f.short_signature()),
                input
            );
        }
    }

alex88 avatar Dec 10 '21 07:12 alex88

This way you can do something like:

    let fn_sign_bytes = &t.input.0[0..4];
    for f in router.abi().functions() {
        if f.short_signature() == *fn_sign_bytes {
            let input = f.decode_input(&t.input.0[4..]).unwrap();
            println!(
                "Transaction {} called function {}:{} with args {:?}",
                t.hash,
                f.name,
                hex::encode(f.short_signature()),
                input
            );
        }
    }

Thanks bro, I'll try it later. Thanks again.

Antake2333 avatar Dec 10 '21 08:12 Antake2333

This way you can do something like:

    let fn_sign_bytes = &t.input.0[0..4];
    for f in router.abi().functions() {
        if f.short_signature() == *fn_sign_bytes {
            let input = f.decode_input(&t.input.0[4..]).unwrap();
            println!(
                "Transaction {} called function {}:{} with args {:?}",
                t.hash,
                f.name,
                hex::encode(f.short_signature()),
                input
            );
        }
    }

there is no function like f.short_signature(),what's your version?Could u pls give me a complete demo,thanks

Antake2333 avatar Dec 10 '21 12:12 Antake2333

you have to use latest master in your cargo.toml file

web3 = { git = "https://github.com/tomusdrw/rust-web3.git" }

alex88 avatar Dec 10 '21 15:12 alex88

you have to use latest master in your cargo.toml file

web3 = { git = "https://github.com/tomusdrw/rust-web3.git" }

Really thank you very much

Antake2333 avatar Dec 11 '21 03:12 Antake2333

@alex88 @Antake2333 do you have a full code working example? I'm running into the same issues as described above.

Basically I have the ABI and want to decode the transaction input into a serde value, but I keep getting InvalidData

Is it possible to get a working code example on how to decode data?

Corfucinas avatar May 02 '22 18:05 Corfucinas

@Corfucinas unfortunately that's all I have, I didn't work on that project anymore

alex88 avatar May 03 '22 00:05 alex88

@Antake2333 is there's a working example to decode function parameters given that you have

  1. smart contract address, 2) smart contract ABI

I'm referring to the following example taken from web3py

>>> transaction = w3.eth.get_transaction('0x5798fbc45e3b63832abc4984b0f3574a13545f415dd672cd8540cd71f735db56')
>>> transaction.input
'0x612e45a3000000000000000000000000b656b2a9c3b2416437a811e07466ca712f5a5b5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000116c6f6e656c792c20736f206c6f6e656c7900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
>>> contract.decode_function_input(transaction.input)
(<Function newProposal(address,uint256,string,bytes,uint256,bool)>,
 {'_recipient': '0xB656b2a9c3b2416437A811e07466cA712F5a5b5a',
  '_amount': 0,
  '_description': b'lonely, so lonely',
  '_transactionData': b'',
  '_debatingPeriod': 604800,
  '_newCurator': True})

I keep running into errors in rust-web3

Corfucinas avatar May 03 '22 06:05 Corfucinas

Found a workaround, but limited. Basically I know thetx_data so I don't have to call to get the Transaction

// Need to take out the "Ox" prefix
let tx_data = "f7a1696300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d53d5ea1c50cae08bf29566bb9060b88146e7c400000000000000000000000000000000000000000000000000000000001e18558830977d8ab1b655dda456cd9a447d8f5e5f5625677dbebe34caa587876f6426a0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41000000000000000000000000d53d5ea1c50cae08bf29566bb9060b88146e7c40000000000000000000000000000000000000000000000000000000000000000764616f6261627900000000000000000000000000000000000000000000000000";

// This works
let correct_format: [u8;260] = hex_literal::hex!("f7a1696300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d53d5ea1c50cae08bf29566bb9060b88146e7c400000000000000000000000000000000000000000000000000000000001e18558830977d8ab1b655dda456cd9a447d8f5e5f5625677dbebe34caa587876f6426a0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41000000000000000000000000d53d5ea1c50cae08bf29566bb9060b88146e7c40000000000000000000000000000000000000000000000000000000000000000764616f6261627900000000000000000000000000000000000000000000000000");
 let decoded_data = contract
        .abi()
        .function("Foo")
        .unwrap()
        .decode_input(&correct_format[4..])
        .unwrap();

The problem is that hex_literal::hex! only works with literals (only hard coded). How to deserialize a rax_data into (what it seems) works correctly which is [u8;260]

Corfucinas avatar May 03 '22 09:05 Corfucinas

Found the solution

// Need to take out the "Ox" prefix
let tx_data = "f7a1696300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d53d5ea1c50cae08bf29566bb9060b88146e7c400000000000000000000000000000000000000000000000000000000001e18558830977d8ab1b655dda456cd9a447d8f5e5f5625677dbebe34caa587876f6426a0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41000000000000000000000000d53d5ea1c50cae08bf29566bb9060b88146e7c40000000000000000000000000000000000000000000000000000000000000000764616f6261627900000000000000000000000000000000000000000000000000";
let mut byte_array = [0u8; 260];
hex::decode_to_slice(tx_data, &mut byte_array).unwrap();
 let decoded_data = contract
        .abi()
        .function("Foo")
        .unwrap()
        .decode_input(&byte_array[4..])
        .unwrap();

it works!

Corfucinas avatar May 03 '22 09:05 Corfucinas