Giả sử ta có contract sau:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
contract MyContract {
    uint256 public myNumber;
 
    event NumberChanged(uint256 newValue, address indexed caller);
 
    constructor(uint256 _myNumber) {
        myNumber = _myNumber;
    }
 
    function setMyNumber(uint256 _myNumber) public {
        myNumber = _myNumber;
        emit NumberChanged(_myNumber, msg.sender);
    }
}

Để tạo ra contract, ta cần phải có địa chỉ và ABI của contract cũng như là provider mà ta đang sử dụng.

// You can also use an ENS name for the contract address
const contractAddress = "0x8cEc98c18722Cca11072eaeDf325a507784dd8fc";
 
// This is the Human-Readable ABI format
const contractAbi = [
    "function myNumber() view returns(uint256)",
 
    "function setMyNumber(uint256 _myNumber) public",
 
    "event NumberChanged(uint256 newValue, address indexed caller)",
];
 
// The Contract object
const contract = new ethers.Contract(contractAddress, contractAbi, provider);

Có thể thấy, đoạn code trên sử dụng một dạng ABI dễ đọc chỉ bao gồm signature của các hàm (hoặc event).

Read-Only Methods

Gọi hàm myNumber để truy vấn đến giá trị của biến myNumber ở trong contract như sau:

async function getMyNumber() {
    // Call the contract's `myNumber` function, which reads the current value of `myNumber`
    const myNumber: BigNumber = await contract.myNumber();
    console.log('My number: ', myNumber.toString());
    // My number:  0
}
getMyNumber();

State Changing Methods

Nếu muốn gọi các hàm có làm thay đổi state thì ta cần kết nối contract đến signer để có thể thực hiện ký giao dịch.

Ví dụ:

async function setMyNumber() {
    // Connect to the contract with a signer, so we can submit transactions
    const contractWithSigner = contract.connect(signer);
 
    // Send a transaction to the contract to set the value of `myNumber`
    const tx = await contractWithSigner.setMyNumber(11);
    console.log('Transaction hash: ', tx.hash);
    // Transaction hash:  0x4490e8dbb7ef14b9cebad8b182ad59458094d33bf9e989757d64bc8046bc18f5
}
setMyNumber();

Listening to Events

Để lắng nghe các event, ta sử dụng phương thức on của contract với hai đối số:

  • Đối số đầu tiên là tên của event được định nghĩa ở trong ABI.
  • Đối số thứ hai là một event listener: bản chất là một hàm mà có các tham số giống với event đang được lắng nghe. Ví dụ với event sau:
event NumberChanged(uint256 newValue, address indexed caller);

Thì event listener sẽ có dạng như sau:

// Receive an event when ANY transfer occurs
contract.on("NumberChanged", (newValue, caller, event) => {
    console.log(`${caller} triggered NumberChanged event with the new value: ${newValue}`);
    
    // Optionally, stop listening
    event.removeListener();
});

Có thể thấy, ngoài những tham số giống với event thì nó còn có thêm một tham số nữa đại diện cho event (event).

Để dừng lắng nghe, ta có thể chạy phương thức removeListener của đối tượng event.

Do có sử dụng indexed, chúng ta có thể lọc ra các event mà được trigger bởi một địa chỉ nào đó bằng cách sử dụng filter như sau:

// A filter for when a specific address changes the value of `myNumber`
const myAddress = "0xF54aFBE31a6e78ED2Cd73ca1C78B12DD3473E44d";
const filter = contract.filters.NumberChanged(null, myAddress)
 
contract.on(filter, (newValue, caller, event) => {
    console.log(`${caller} triggered NumberChanged event with the new value: ${newValue}`);
    
    // Optionally, stop listening
    event.removeListener();
});

Query Historic Events

Để truy vấn đến lịch sử của các event, ta cần tạo ra một filter như sau:

const filterFrom = contract.filters.NumberChanged(null, myAddress)

Sau đó truyền filter vào phương thức queryFilter như sau:

await contract.queryFilter(filterFrom, 0, 54)

Với tham số thứ hai và thứ ba là block range cần truy vấn.

Chúng ta có thể truy vấn 10 block mới nhất như sau:

await contract.queryFilter(filterFrom, -10)