Solidity Access Control
Access control is deciding which accounts may run which functions in a contract. An account is an address that can call a contract, such as a person or another contract.
The owner is recorded at creation
The constructor runs once when the contract is deployed. It stores the deploying account as the owner.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract NoticeBoard {
address public owner;
string public notice = "Welcome";
constructor() {
owner = msg.sender;
}
function readNotice() public view returns (string memory) {
return notice;
}
}
An onlyOwner modifier guards a function
The modifier checks the caller first. A stranger's call reverts, and the notice stays unchanged.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract NoticeBoard {
address public owner;
string public notice = "Welcome";
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function setNotice(string memory newNotice) public onlyOwner {
notice = newNotice;
}
function readNotice() public view returns (string memory) {
return notice;
}
}
A plain require guards a function
The same check can sit inside the function body. Here require compares the caller with the owner before any change.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract NoticeBoard {
address public owner;
string public notice = "Welcome";
constructor() {
owner = msg.sender;
}
function clearNotice() public {
require(msg.sender == owner, "Not owner");
notice = "";
}
function readNotice() public view returns (string memory) {
return notice;
}
}
Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract NoticeBoard {
address public owner;
string public notice = "Welcome";
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function setNotice(string memory newNotice) public onlyOwner {
notice = newNotice;
}
function clearNotice() public {
require(msg.sender == owner, "Not owner");
notice = "";
}
function readNotice() public view returns (string memory) {
return notice;
}
}
Key points
- Owner: the account recorded as being in charge of the contract.
- Recorded at creation: the constructor stores msg.sender as the owner when the contract is deployed.
- Guarded function: a function that checks the caller before it runs.
- Modifier guard: an onlyOwner modifier checks the caller before the function body runs.
- Require guard: a require line inside the function body can make the same check.
- Public read: a function with no guard can be called by any account.
In this example, the constructor records the deployer as the owner. setNotice is guarded by the onlyOwner modifier, and clearNotice is guarded by a require line in its body. readNotice has no guard, so the owner and a stranger can both call it.
Note
a failed check reverts the call, so the guarded change does not happen.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Remix IDE
Exercise
A stranger calls a function guarded by onlyOwner. What happens?