[Ethereum] How to view event logs for an ethereum contract

historylogssolidityweb3js

After reading the this post, it seems like I should be able to access the logs of events that have been called on an Ethereum contract. The section I am interested in is Option 3 which is using event logs as a cheaper form of storage.

This Stack Exchange question highlights a great way to do this.

The important part:

var filter = web3.eth.filter({
    'fromBlock': 0,
    'toBlock': 'latest',
    'address': contractAddress,
    'topics':[
        web3.sha3('newtest(string,uint256,string,string,uint256)')
    ]
});

filter.watch(function(error, result) {
   ...
})

However, the data I am getting back is not an array of logs in the structure of my event which I was expecting.

Blow is a basic contract where I am taking a product and moving it between addresses. When I do so, I create an event to highlight who sent it and who took ownership as well as the quantity. This should give me a rich history of every address that owned the product and the quantities at each transaction.

Contract:

contract Product{
  address public owner;
  string public title;
  mapping (address => uint) quantity;

  event Transferred(address from, address to, uint quantity);

  // Constructor
  function Product(string _title, uint _quantity){
    owner = msg.sender;
    title = _title;
    quantity[msg.sender] = _quantity;
  }

  function getQuantity(address _user) constant returns (uint _quantity){
    return quantity[_user];
  }

  function changeQuantity(uint _quantity) returns (bool success){
    quantity[msg.sender] = _quantity;
    return true;
  }

  function transfer(address _to, uint _quantity) returns (bool success){
    if (quantity[msg.sender] < _quantity){
      return false;
    }

    owner = _to;
    quantity[msg.sender] -= _quantity;
    quantity[_to] += _quantity;
    Transferred(msg.sender, _to, _quantity);
    return true;
  }
}

Let's start interacting with this bad boy in the Truffle console. First let's make our account objects.

var account1 = web3.eth.accounts[0]
var account2 = web3.eth.accounts[1]

Now, lets access the deployed contract.

var product;
Product.deployed().then(function(i){ product = i });

Now lets make sure account1 has 0 of whatever product this is

product.getQuantity(account1).then(function(ci){console.log(ci)})
// >>> { [String: '0'] s: 1, e: 0, c: [ 0 ] }

Sweet. Lets give it 10 as a quantity.

product.changeQuantity(10).then(function(ci){console.log(ci)})
product.getQuantity(account1).then(function(ci){console.log(ci)})

// >>> { [String: '10'] s: 1, e: 1, c: [ 10 ] }

Now we are cooking! Lets give one widget to account2. This will trigger the Transferred event and hopefully create a log that I can see forever and ever.

product.transfer(account2, 1).then(function(ci){console.log(ci)})
product.getQuantity(account2).then(function(ci){console.log(ci)})
// >>> { [String: '1'] s: 1, e: 0, c: [ 1 ] }

Awesome! Now lets check those logs! Replace the_contract_address with the contract address String.

var filter = web3.eth.filter({
    fromBlock:0,
    toBlock: 'latest',
    address: ’the_contract_address',
    'topics':[
        web3.sha3('Transferred(address,address,uint)')
    ]
});

Printing the filter object will give you:

Filter {
  requestManager:
   RequestManager {
     provider: HttpProvider { host: 'http://localhost:8545', timeout: 0 },
     polls: {},
     timeout: null },
  options:
   { topics: [ '0x6f8b0853f4c56c6a9faec2151763bdc803a695c1ce09b45d3036186c5ae14c47' ],
     from: undefined,
     to: undefined,
     address: '0x570a704dded5379eb21b064cc7750741348e8860',
     fromBlock: '0x0',
     toBlock: 'latest' },
  implementation:
   { newFilter: { [Function: send] request: [Function: bound ], call: [Function: newFilterCall] },
     uninstallFilter: { [Function: send] request: [Function: bound ], call: 'eth_uninstallFilter' },
     getLogs: { [Function: send] request: [Function: bound ], call: 'eth_getFilterLogs' },
     poll: { [Function: send] request: [Function: bound ], call: 'eth_getFilterChanges' } },
  filterId: '0x0b',
  callbacks: [],
  getLogsCallbacks: [],
  pollFilters: [],
  formatter: [Function: outputLogFormatter]
}

From here, I have no idea how to get the log data I am looking for. Even calling the temptingly named function eth_getFilterLogs returns []. I need a way to see a beautiful list of logs that grows over time with each transaction.

Any help would be greatly appreciated!

Edit:

var myEvent = product.Transferred({fromBlock: 0, toBlock: 'latest'});
myEvent.watch(function(error, result){console.log(result)});

The above successfully gives me the last event log it seems. However, it only seems to return one. For instance, executing:

product.transfer(account2, 3).then(function(ci){console.log(ci)})
product.transfer(account2, 2).then(function(ci){console.log(ci)})

yields only the second transaction results.

{ logIndex: 0,
  transactionIndex: 0,
  transactionHash: '0xe2e44b73d67147fb9988dce58d7616da759660a7fde99b3068c6b92fdb9ad8d3',
  blockHash: '0x3a28c6909d44f380a2c64601ecbc3523551ed3840f3afd2549d72d0b710fbb2d',
  blockNumber: 38,
  address: '0x97c553ef6d28b88e19f47735c35369d71f891d76',
  type: 'mined',
  event: 'Transferred',
  args:
   { from: '0x00bd67f06685ad070579b95c2e19ec0022fc916e',
     to: '0x90144809cf261f271e9b7fd3d6d1d0b51d8426c2',
     quantity: {
       [String: '2'] s: 1, e: 0, c: [Object]
     }
   }
}

How can I get both (all) of them?

Best Answer

To get event logs of the past, you can instantiate the event with a block range, and use the myEvent.get function to retrieve events.

In your example, we could do something minimal like this:

let transferEvent = product.Transferred({}, {fromBlock: 0, toBlock: 'latest'})
transferEvent.get((error, logs) => {
  // we have the logs, now print them
  logs.forEach(log => console.log(log.args))
})

Note: The first argument {} can be used like a filter, to only catch certain events.

Above code logs this to console:

{ from: '0xc7d748654199d3594239a244d806e51331ca14b5',                    
  to: '0x3c11b23b4d5adb2d534d262cf83cee773c9b1c0a',                                            
  quantity: { [String: '1'] s: 1, e: 0, c: [ 1 ] } }                                           
{ from: '0xc7d748654199d3594239a244d806e51331ca14b5',                                          
  to: '0x3c11b23b4d5adb2d534d262cf83cee773c9b1c0a',                                            
  quantity: { [String: '3'] s: 1, e: 0, c: [ 3 ] } }                                           

What about for all events of a contract?

Here the event instantiation would just look like this (notice we only use one parameter here):

let events = product.allEvents({fromBlock: 0, toBlock: 'latest'})

And then continue as before, with events.get().

event.get() vs event.watch()

event.watch() is used for dealing with new events that fit a filter, as they stream in ("watching" the blockchain).

event.get() is for grabbing existing events that fit some filter.

Sources: