Solidity Events
An event is a public log entry that a contract emits when an action happens. Apps outside the chain read these entries to follow the contract's activity.
Declare an event and emit it on deposit
The event is declared like a function header, without a body. The emit line fires it each time a deposit lands.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract SavingsBox {
event Deposited(address indexed who, uint256 amount);
mapping(address => uint256) public balanceOf;
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
}
Emit a second event on withdraw
Withdraw fires its own event after the payout succeeds. Each action leaves a log entry with its own signature.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract SavingsBox {
event Deposited(address indexed who, uint256 amount);
event Withdrawn(address indexed who, uint256 amount);
mapping(address => uint256) public balanceOf;
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdraw(uint256 amount) public {
require(balanceOf[msg.sender] >= amount, "not enough saved");
balanceOf[msg.sender] -= amount;
(bool ok, ) = payable(msg.sender).call{value: amount}("");
require(ok, "payment failed");
emit Withdrawn(msg.sender, amount);
}
}
Each event leaves a different signature topic
The first topic of every log entry is the hash of the event's signature. A reader can tell a deposit from a withdrawal by that topic alone.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract SavingsBox {
event Deposited(address indexed who, uint256 amount);
event Withdrawn(address indexed who, uint256 amount);
mapping(address => uint256) public balanceOf;
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdraw(uint256 amount) public {
require(balanceOf[msg.sender] >= amount, "not enough saved");
balanceOf[msg.sender] -= amount;
(bool ok, ) = payable(msg.sender).call{value: amount}("");
require(ok, "payment failed");
emit Withdrawn(msg.sender, amount);
}
}
Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract SavingsBox {
event Deposited(address indexed who, uint256 amount);
event Withdrawn(address indexed who, uint256 amount);
mapping(address => uint256) public balanceOf;
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdraw(uint256 amount) public {
require(balanceOf[msg.sender] >= amount, "not enough saved");
balanceOf[msg.sender] -= amount;
(bool ok, ) = payable(msg.sender).call{value: amount}("");
require(ok, "payment failed");
emit Withdrawn(msg.sender, amount);
}
}
Key points
- Event: a named entry a contract writes into the public log of the blockchain, the shared record the network keeps.
- Declare: an event is declared with the event keyword, its name, and the values it carries.
- Emit: the emit keyword fires an event at the moment the action happens.
- Indexed: marking an argument indexed makes that value searchable in the log.
- Topics: a log entry stores its event signature and its indexed values as topics, with the remaining values as data.
- Off-chain readers: apps outside the chain read events to show deposits, withdrawals, and other activity.
In this example, the SavingsBox contract declares two events, Deposited and Withdrawn, each with an indexed who argument. The deposit function credits the sender and emits Deposited with the amount sent. The withdraw function checks the saved balance, pays the sender back, and then emits Withdrawn. Every action leaves exactly one log entry that names the contract, the saver, and the amount.
Note
a contract cannot read its own events back, so keep any value the contract itself needs in a state variable too.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Remix IDE
Exercise
Where does the signature of an emitted event sit in its log entry?