Subscribing to Events
WebSocketProvider
Để lắng nghe các sự kiện, trước tiên, ta cần chuyển provider sang dạng web socket như sau:
const provider = new Web3.providers.WebsocketProvider(`ws://127.0.0.1:7545`);
const web3 = new Web3(provider);
Đối với Infura thì có đôi chút khác biệt:
const provider = new Web3.providers.WebsocketProvider(`wss://sepolia.infura.io/ws/v3/${INFURA_API_KEY}`)
Khi chuyển sang dạng này, kết nối đến server sẽ không được tự động ngắt sau khi thực thi xong các hàm như khi sử dụng HttpProvider
. Để ngắt, ta cần sử dụng câu lệnh sau1:
provider.disconnect(1012);
Và có thể lắng nghe sự kiện ngắt kết nối như sau:
provider.on('close', function () {
console.log('The connection has been closed!');
});
Attention
Cần xử lý bất đồng bộ một cách hợp lý khi sử dụng câu lệnh ngắt kết nối.
Subscribing
Giả sử ta có event sau cho biết giá trị của số đã bị thay đổi:
event NumberChanged(uint256 newValue);
Và ta emit event này ở trong hàm setMyNumber
:
function setMyNumber(uint256 _myNumber) public {
myNumber = _myNumber;
emit NumberChanged(_myNumber);
}
Ta có thể subscribe event NumberChanged
thông qua events
2 ở phía front-end như sau:
MyContract.events.NumberChanged()
.on('data', function (event) {
console.log("Number was changed to ", event.returnValues.newValue);
})
.on("error", console.error);
Chú ý rằng đoạn code trên sẽ được trigger bất cứ khi nào hàm setMyNumber
được gọi thực thi, có thể là bởi một user khác. Mà ta chỉ muốn nhận trigger khi hàm được gọi bởi user hiện tại.
Filtering
Để giải quyết vấn đề trên, ta có thể khai báo thêm địa chỉ của caller với từ khóa indexed
trong danh sách tham số của event3:
event NumberChanged(uint256 newValue, address indexed caller);
Truyền vào địa chỉ của caller lúc emit event như sau:
emit NumberChanged(_myNumber, msg.sender);
Bằng cách này, ta có thể lọc ra các event được trigger bởi user hiện tại thông qua thuộc tính filter
:
MyContract.events.NumberChanged({filter: { caller: "0xF54aFBE31a6e78ED2Cd73ca1C78B12DD3473E44d" }})
Querying Past Events
Truy vấn đến các event đã xảy ra trước đó thông qua phương thức getPastEvents
:
const events = await MyContract.getPastEvents("NumberChanged", { fromBlock: 0, toBlock: "latest" });
Bởi vì ta có thể truy cập đến lịch sử các event nên ta có thể tận dụng tính năng này để lưu dữ liệu thay vì lưu ở trong contract. Nói cách khác, ta có thể emit các event và truyền dữ liệu vào đó nhằm tạo nên một danh sách các event có chứa dữ liệu mà có thể được truy cập ở phía front-end.
Việc lưu dữ liệu theo cách này có một trade-off là ta không thể truy xuất được dữ liệu ở bên trong contract, nhưng nó lại giúp ta giảm thiểu được các thao tác ghi dữ liệu vào trong blockchain, vốn là rất tốn kém
Footnotes
-
Tham khảo: Legacy Event
close
has been deprecated ↩ -
Tham khảo thêm web3.eth.Contract — web3.js 1.0.0 documentation (web3js.readthedocs.io) ↩
-
Xem thêm Indexed Parameters. ↩