How to emit and capture events in Phat Contract
2024-01-22
Events and logs are important to the Web3 developers because they facilitate communication between smart contracts and their user interfaces. Like other smart contracts, Phat Contract can emit events to communicate that something has happened. Unlike other smart contracts, Phat Contract events are only available inside the cluster. You need to use our JS-SDK to capture these events and take actions when they happen.
Here is an example of an ERC-20 like implementation from ink-examples, and it can deployed on Phala Network. You can test it on the PoC6 Testnet.
The complete code example can be found here: https://github.com/Leechael/phat-contract-events-example/
How to Emit Events from a Phat Contract
Firstly, you need to declare an event definition
#[ink(event)]
pub struct Transfer {
#[ink(topic)]
from: Option<AccountId>,
#[ink(topic)]
to: Option<AccountId>,
value: Balance,
}
To define an Ink! event, you need to add the #[ink(event)]
macro to the struct
definition. Also, apply the #[ink(topic)]
attribute tag to each item in your event that you want to mark as 'should be indexed'. Note that you can index a maximum of four attributes.
Emitting Events in a Constructor
You can use Self::env().emit_event()
to emit events in a constructor function:
#[ink(constructor)]
pub fn new(initial_supply: Balance) -> Self {
let caller = Self::env().caller();
let mut balances = Mapping::default();
balances.insert(caller, &initial_supply);
Self::env().emit_event(Transfer {
from: None,
to: Some(caller),
value: initial_supply,
});
Self {
total_supply: initial_supply,
balances,
allowances: Mapping::default(),
}
}
Emitting Events in a Message
You can use self.env().emit_event()
to emit events in a message function with &mut
mark:
self.env().emit_event(Transfer {
from: Some(*from),
to: Some(*to),
value,
});
&mut self
mark MUST be used.Capture Events with JS-SDK
Once we have the Phat Contract instance that emit events, we can capture events in a web3 application with our JS-SDK.
You need to load the ABI JSON first:
const fs = require('fs')
const abi = fs.readFileSync('./target/ink/erc20.json', 'utf8')
Since the events only occur within the cluster and are not broadcasted on-chain, you can use the logger to capture them:
const ws = argv['--ws'] || process.env.WS || 'wss://poc6.phala.network/ws'
const logger = await getLogger({ transport: ws })
// Or you already use `getClient` before:
const client = await getClient({ transport: ws })
const
The logger
object allows you to access the log stream from a specified PRuntime Node. Now you can call logger.tail(1000)
to fetch the last 1000 log entries. For further usage, you can learn more about that via the JS-SDK documentation and the tail.js
example.
Currently, if you want capture the event after submitting the transaction, you need a bit more boilerplate codes:
const { blockNumber } = await client.phactory.getInfo({})
const result = await contract.send.transfer(
{ cert, address: cert.address, pair },
toAddress,
value
)
await result.waitFinalized()
const { records } = await logger.tail(10000, {
contract: contract.address.toHex(),
abi,
type: ['Event'] as LogTypeLiteral[],
})
const matched = records.filter(i => (i as SerMessageEventWithDecoded).blockNumber >= blockNumber)
console.assert(matched.length === 1, 'It should only one matched event.')
for (const record of matched) {
let rec = record as SerMessageEventWithDecoded
prettyPrint(rec)
}
The SerMessageEventWithDecoded
struct represents an event that includes decoded data. It has the following structure:
interface SerMessageEventWithDecoded {
type: 'Event'
sequence: number
blockNumber: number
contract: string
topics: string[]
payload: string
decoded?: {
args: Codec[];
event: {
args: {
name: string;
type: TypeDef;
}[];
docs: string[];
fromU8a: (data: Uint8Array) => DecodedEvent;
identifier: string;
index: number;
}
}
}
Conclusion
Events are a crucial aspect of Phat Contract development as they allow you to determine the success or failure of your transaction and provide valuable insights into cluster activity. It is important to familiarize yourself with the pattern of events in order to effectively utilize them.