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:
Note: The first argument
{}
can be used like a filter, to only catch certain events.Above code logs this to console:
What about for all events of a contract?
Here the event instantiation would just look like this (notice we only use one parameter here):
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: