Aller au contenu principal

Work with Events

Events are immutable, verifiable objects emitted by the smart contracts, stored and served by the Alephium full node. They play a critical role in facilitating efficient and transparent communication between smart contracts and off-chain applications.

There are many use cases for events: DEX can use events to keep track of all the swaps happening in a token pair. NFT marketplace can use events to store all the NFT listings. Oracle can use events to signal requests of certain offchain info from smart contracts. Bridge can use events to represent certain actions that require consensus from all the bridge operators, etc.

Event Object

An event object in Alephium consists of the following attributes:

txId            : transaction id
blockHash : block hash
contractAddress : address of the contract where the event is emitted / special address for system events
eventIndex : index of the event emitted from the contract / special index for system events
fields : fields contained in the event

There are two types of events in Alephium: contract events and system events. The following sections discuss them respectively, with more details on the contractAddress, eventIndex and fields attributes.

Contract Events

As suggested by the name, contract events are custom events emitted by the contracts:

Contract Admin(mut admin: Address) {
event AdminUpdated(previous: Address, new: Address)

@using(updateFields = true)
pub fn updateAdmin(newAdmin: Address) -> () {
checkCaller!(callerAddress!() == admin, 0)

admin = newAdmin
emit AdminUpdated(admin, newAdmin)
}
}

In this example, an AdminUpdated event is emitted every time admin is updated. This information can be listened to by the off-chain applications to update their user interfaces or for auditing.

You can subscribe to all events emitted from a contract, or a specific event emitted from a contract using Alephium's Web3 SDK:

// `adminInstance` is a contract instance of the Admin contract
adminInstance.subscribeAdminUpdatedEvent({
pollingInterval: 500,
messageCallback: (event: AdminTypes.AdminUpdatedEvent): Promise<void> => {
console.log('got admin updated event:', event)
return Promise.resolve()
},
errorCallback: (error: any, subscription): Promise<void> => {
console.log(error)
subscription.unsubscribe()
return Promise.resolve()
}
})

In AdminUpdated's event object, contractAddress represents the contract address of adminInstance, eventIndex is 0 since AdminUpdated is the first event defined in the Admin contract (0-based index). fields contains two addresses: one for the previous admin, the other for the new admin.

System Events

Compared to the contract events, which are emitted explicitly from the contracts, system events are emitted automatically by the Alephium full node. Currently there are two system events:

ContractCreatedEvent

ContractCreatedEvent is emitted when a new contract is created:

TxScript Deploy(fooByteCode: ByteVec) {
createContract!{callerAddress!() -> ALPH: 1 ALPH}(fooByteCode, #00, #00)
}

In the example above, the Deploy transaction script creates a new Foo contract from its contract bytecode. Foo contract has no contract fields, which is why #00 is passed in as arguments to the createContract function. A ContractCreatedEvent system event is emitted after the Foo contract is created.

Alephium's Typescript SDK provides a helper function to subscribe to the ContractCreatedEvent event:

subscribeContractCreatedEvent({
pollingInterval: 500,
messageCallback: (event: ContractCreatedEvent): Promise<void> => {
console.log('got contract created event:', event)
return Promise.resolve()
},
errorCallback: (error: any, subscription): Promise<void> => {
console.log(error)
subscription.unsubscribe()
return Promise.resolve()
}
})

In ContractCreatedEvent's event object, eventIndex is set to -1, a value specifically set aside for the ContractCreatedEvent event. contractAddress is set to a special value calculated based on the eventIndex and contract group. fields contains the address of the newly created contract as well as its parent contract id if it exists.

ContractDestroyedEvent

ContractDestroyedEvent is emitted when a contract is destroyed:

Contract Foo() {
@using(assetsInContract = true)
pub fn destroy() -> () {
destroySelf!(callerAddress!())
}
}

In the example above, after the destroy function is called, Foo contract will be destroyed and a ContractDestroyedEvent system event will be emitted by the Alephium full node.

Alephium's Typescript SDK provides a helper function to subscribe to the ContractDestroyedEvent event:

subscribeContractDestroyedEvent({
pollingInterval: 500,
messageCallback: (event: ContractDestroyedEvent): Promise<void> => {
console.log('got contract destroyed event:', event)
return Promise.resolve()
},
errorCallback: (error: any, subscription): Promise<void> => {
console.log(error)
subscription.unsubscribe()
return Promise.resolve()
}
})

In ContractDestroyedEvent's event object, eventIndex is set to -2, a value specifically allocated for the ContractDestroyedEvent event. contractAddress is set to a special value calculated based on the eventIndex and contract group. fields contains the address of the destroyed contract.

Configuration

Alephium's full node allows flexible configuration of how events should be stored and indexed. By default it doesn't store events to keep the full node lightweight.

You can enable storing events using the following flag:

alephium.node.event-log.enabled=true

This allows us to query contract events and system events based on the contract address.

To enable querying events based on transaction id and block hash, you need to enable the following flags:

alephium.node.event-log.index-by-tx-id = true
alephium.node.event-log.index-by-block-hash = true

By default, events emitted from all contracts are stored. But if you know what contracts you are interested in getting events for, you can use the following configuration:

alephium.node.event-log.contract-addresses = [$CONTRACT_ADDR_1, $CONTRACT_ADDR_2]

This way, you will save disk space because your full node only stores the events emitted from the contracts specified here.

Further Reading

The event subscription functions in Alephium's Web3 SDK are built on top of Alephium's full node APIs. For more details about the APIs please refer to the OpenAPI Documentation.

Other than ContractEvent and SystemEvent, there is in fact another (special) system event called DebugEvent, please refer to Debugging for more information.