Là một cơ chế giúp ghi lại và thông báo về các sự kiện quan trọng xảy ra trong smart contract. Event cho phép các ứng dụng bên ngoài theo dõi và phản ứng khi các sự kiện này xảy ra trên blockchain.

Event được lưu ở trong log entry của blockchain và không thể được sử dụng bởi các contract khác.

Giả sử ta có event sau:

// declare the event
event IntegersAdded(uint x, uint y, uint result);
 
function add(uint _x, uint _y) public returns (uint) {
  uint result = _x + _y;
  // fire an event to let the app know the function was called:
  emit IntegersAdded(_x, _y, result);
  return result;
}

Phía front-end (Web3JS) có thể theo dõi sự kiện này như sau1:

YourContract.events.IntegersAdded()
	.on('data', function (event) {
		const { x, y, result } = event.returnValues;
		console.log(`The result of ${x} + ${y} is ${result}`);
	})
	.on("error", console.error);

Indexed Parameters

Để có thể lọc ra các event từ phía front-end dựa trên một giá trị nào đó của tham số, chẳng hạn như tìm các event mà có giá trị của result bằng 100, ta có thể dùng từ khóa indexed cho tham số đó:

event IntegersAdded(uint x, uint y, uint indexed result);

Và lọc các event bằng Web3JS như sau:

YourContract.events.IntegersAdded({filter: { result: 100 }})
	.on('data', function (event) {
		const { x, y, result } = event.returnValues;
		console.log(`The result of ${x} + ${y} is ${result}`);
	})
	.on("error", console.error);

Topics

Là một cấu trúc dữ liệu ở bên trong log entry của một event lưu signature của event và các indexed parameter.

Trong Solidity có hai loại event:

  • Non-anonymous: loại này chỉ có thể có tối đa 3 topic (tức là chỉ có tối đa 3 index parameter) vì topic đầu tiên được dùng để lưu event signature. Đây là kiểu event mặc định.
  • Anonymous: mỗi event có tối đa 4 topic và do đó mà ta không thể lọc event dựa trên event signature. Chúng ta có thể khai báo một event là anonymous với từ khóa anonymous.

Xét event sau:

event NumberChanged(uint256 indexed newValue, address indexed caller);

Chúng ta emit nó ở trong hàm setMyNumber như sau:

function setMyNumber(uint256 _myNumber) public {
	myNumber = _myNumber;
	emit NumberChanged(_myNumber, msg.sender);
}

Và nếu ta gọi thực thi hàm setMyNumber ở phía front-end thông qua Web3JS với đối số của _myNumber12:

const receipt = await myContract.methods.setMyNumber(12).send({
	from: "0xF54aFBE31a6e78ED2Cd73ca1C78B12DD3473E44d",
	gas: 1000000,
	gasPrice: 10000000000,
});

Thì log entry sẽ có các topic như sau:

{
	...
	"topics": [
	    "0x43c975a6a5fd998ee5157e3a71e16357b061f8d023b698b80b51710ab31b470e",
	    "0x000000000000000000000000000000000000000000000000000000000000000c",
	    "0x000000000000000000000000f54afbe31a6e78ed2cd73ca1c78b12dd3473e44d"
	],
	...
}

Phân tích cụ thể:

  • Như đã nói, topic đầu tiên sẽ được dùng để lưu event signature. Giá trị 0x43c975a6a5fd998ee5157e3a71e16357b061f8d023b698b80b51710ab31b470e chính là giá trị hash của chuỗi NumberChanged(uint256,address).
  • Topic thứ hai chính là giá trị 12 ở dạng thập lục phân và có padding.
  • Tương tự, topic thứ ba chính là địa chỉ của caller ở dạng thập lục phân và có padding.

Important

Đối với topic của các giá trị mà có kiểu là tham chiếu (struct, array và mapping), giá trị của nó sẽ được hash.

Tất cả các giá trị trên đều là được hash bởi hàm băm Keccak256.

Footnotes

  1. Xem thêm Filtering.