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

How to parse contract logs

Open BlinkyStitt opened this issue 6 years ago • 5 comments

I just wrote some code to subscribe to the etherdelta contract logs. It is working as expected, but the Log struct itself isn't parsed at all.

Log { address: 0x8d12a197cb00d4747a1fe03395095ce2a5cc6819, topics: [0x6effdda786735d5033bfad5f53e5131abcced9e52be6c507b62d639685fbed6d], data: Bytes([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, 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, 15, 24, 14, 193, 89, 82, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 117, 13, 37, 84, 22, 72, 59, 236, 26, 49, 202, 112, 80, 198, 218, 196, 38, 59, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 90, 177, 69, 220, 240, 84, 236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 131, 239, 77, 161, 236, 59, 185, 96, 32, 209, 8, 13, 0, 180, 54, 172, 228, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 205, 1, 35, 205, 118, 244, 38, 95, 93, 126, 76, 243, 4, 210, 140, 57, 94, 205, 22, 27]), block_hash: Some(0xf35d9e95325c8735d5928f894f5cf58a19ddf5b970d7fa6c0a29427dcd91906e), block_number: Some(7458860), transaction_hash: Some(0x95f32ebacd1747a5179c469638f4777d50f81a3c2ae3af72d06a4114094ef279), transaction_index: Some(7), log_index: Some(4), transaction_log_index: None, log_type: None, removed: Some(false) }

Does a Contract have any helpers for parsing log.data? I'm not seeing anything like this in web3's code, but I do see some log parsing code in the ethabi crate. I don't see how to access the abi object on my contract object though since it is not marked public.

BlinkyStitt avatar Mar 28 '19 19:03 BlinkyStitt

I think this is a little different than #26 since that looks like it is about querying the logs and I've already subscribed to them and just need to parse them, but maybe they can be solved the same way.

BlinkyStitt avatar Mar 28 '19 19:03 BlinkyStitt

Currently not. There is a rudimentary support for that on this branch though: https://github.com/tomusdrw/rust-web3/tree/td-fixes

tomusdrw avatar Mar 28 '19 21:03 tomusdrw

The entire contract support should be re-worked for strong typing, see #120 and #17

tomusdrw avatar Mar 28 '19 21:03 tomusdrw

I’m new to rust, so there's probably a much more elegant way to do this, but I think the following illustrates the core concepts

use ethabi::{Event, EventParam, ParamType, Log, RawLog};
[...]

    let params = vec![EventParam {
        name: "thing".to_string(),
        kind: ParamType::Address,
        indexed: false
    }];

    let event = Event {
        name: "Thing".to_string(),
        inputs: params,
        anonymous: false
    }

    let ev_hash = event.signature();
    let receipt = web3.eth()
            .transaction_receipt(hash)
            .wait()
            .unwrap().unwrap();

    let log = receipt.logs.iter().find(|log| {
        log.topics.iter().find(|topic| topic == &&ev_hash).is_some()
    });

    match log {
        Some(l) => {
            Some(event.parse_log(RawLog {
                topics: vec![ ev_hash ],
                data: l.data.clone().0
            }).unwrap())
        },
        None => None
    }

    // extract log outputs into your type

justinbretting avatar Apr 30 '19 14:04 justinbretting

Thanks justinbretting,For example:

contract EventTest { event get_log(address a,address b,uint256 c);

function getName(uint256 c) public  returns (bool) {
    emit get_log(msg.sender,msg.sender,c);
    return true;
}

}

after call getName(1) get log:

let hash=log.unwrap().transaction_hash.unwrap(); info!("the hash is {:?}",hash);

use ethabi::{Event, EventParam, ParamType, Log, RawLog};


    let params = vec![EventParam {
        name: "a".to_string(),
        kind: ParamType::Address,
        indexed: false
    },EventParam {
        name: "b".to_string(),
        kind: ParamType::Address,
        indexed: false
    },EventParam {
        name: "c".to_string(),
        kind: ParamType::Uint(256),
        indexed: false
    }];

    let event = Event {
        name: "get_log".to_string(),
        inputs: params,
        anonymous: false
    };

    let ev_hash = event.signature();

    info!("the ev_hash is {:?}",ev_hash);

    let web3_format_hash=web3::types::H256::from_slice(&ev_hash.0);
    info!("the web3_format_hash is {:?}",web3_format_hash);

    let receipt = web3.eth()
            .transaction_receipt(hash)
            .await?.unwrap();

            info!("the receipt is {:?}",receipt);

    let log = receipt.logs.iter().find(|log| {
        log.topics.iter().find(|topic| topic == &&web3_format_hash).is_some()
    });

    let res=match log {
        Some(l) => {
            Some(event.parse_log(RawLog {
                topics: vec![ ev_hash ],
                data: l.data.clone().0
            })?)
        },
        None => None
    };
    
    info!("the res is {:?}",res);

After successful, You should get Res is :

the res is Some(Log { params: [LogParam { name: "a", value: Address(0x1a1afbd92af983276b5e494465b33ddd84c5c1f6) }, LogParam { name: "b", value: Address(0x1a1afbd92af983276b5e494465b33ddd84c5c1f6) }, LogParam { name: "c", value: Uint(1) }] })

Ok,Now,You can get Event!

ryanjackgit avatar Apr 03 '22 08:04 ryanjackgit