-
Notifications
You must be signed in to change notification settings - Fork 44
Add AccessControl, Pausable to GemGame contract #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,66 @@ | ||
| // Copyright (c) Immutable Pty Ltd 2018 - 2024 | ||
| // SPDX-License-Identifier: Apache 2 | ||
| // solhint-disable not-rely-on-time | ||
|
|
||
| pragma solidity ^0.8.19; | ||
|
|
||
| import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; | ||
| import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; | ||
|
|
||
| error Unauthorized(); | ||
| error ContractPaused(); | ||
|
|
||
| /** | ||
| * @title GemGame - A simple contract that emits an event for the purpose of indexing off-chain | ||
| * @author Immutable | ||
| * @dev The GemGame contract is not designed to be upgradeable or extended | ||
| */ | ||
| contract GemGame { | ||
| contract GemGame is AccessControl, Pausable { | ||
| /// @notice Indicates that an account has earned a gem | ||
| event GemEarned(address indexed account, uint256 timestamp); | ||
|
|
||
| /// @notice Mapping of the last time an account earned a gem | ||
| mapping(address account => uint256 lastEarned) public accountLastEarned; | ||
|
|
||
| /// @notice Role to allow pausing the contract | ||
| bytes32 private constant _PAUSE = keccak256("PAUSE"); | ||
|
|
||
| /// @notice Role to allow unpausing the contract | ||
| bytes32 private constant _UNPAUSE = keccak256("UNPAUSE"); | ||
|
|
||
| /** | ||
| * @notice Sets the DEFAULT_ADMIN, PAUSE and UNPAUSE roles | ||
| * @param _admin The address for the admin role | ||
| * @param _pauser The address for the pauser role | ||
| * @param _unpauser The address for the unpauser role | ||
| */ | ||
| constructor(address _admin, address _pauser, address _unpauser) { | ||
| _grantRole(DEFAULT_ADMIN_ROLE, _admin); | ||
| _grantRole(_PAUSE, _pauser); | ||
| _grantRole(_UNPAUSE, _unpauser); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Pauses the contract | ||
| */ | ||
| function pause() external { | ||
| if (!hasRole(_PAUSE, msg.sender)) revert Unauthorized(); | ||
| _pause(); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Unpauses the contract | ||
| */ | ||
| function unpause() external { | ||
| if (!hasRole(_UNPAUSE, msg.sender)) revert Unauthorized(); | ||
| _unpause(); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Function that emits a `GemEarned` event | ||
| */ | ||
| function earnGem() external { | ||
| // solhint-disable-next-line not-rely-on-time | ||
| if (paused()) revert ContractPaused(); | ||
| emit GemEarned(msg.sender, block.timestamp); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,68 @@ | ||
| // Copyright Immutable Pty Ltd 2018 - 2024 | ||
| // SPDX-License-Identifier: Apache 2.0 | ||
| pragma solidity ^0.8.20; | ||
| // solhint-disable not-rely-on-time | ||
|
|
||
| pragma solidity ^0.8.19; | ||
|
|
||
| import "forge-std/Test.sol"; | ||
| import {GemGame} from "../../../contracts/games/gems/GemGame.sol"; | ||
| import {GemGame, Unauthorized, ContractPaused} from "../../../contracts/games/gems/GemGame.sol"; | ||
|
|
||
| contract GemGameTest is Test { | ||
| event GemEarned(address indexed account, uint256 timestamp); | ||
|
|
||
| GemGame gemGame; | ||
| GemGame private _gemGame; | ||
|
|
||
| function setUp() public { | ||
| gemGame = new GemGame(); | ||
| _gemGame = new GemGame(address(this), address(this), address(this)); | ||
| } | ||
|
|
||
| function testEarnGem_EmitsGemEarnedEvent() public { | ||
| function testEarnGemEmitsGemEarnedEvent() public { | ||
| vm.expectEmit(true, true, false, false); | ||
| emit GemEarned(address(this), block.timestamp); | ||
| gemGame.earnGem(); | ||
| _gemGame.earnGem(); | ||
| } | ||
|
|
||
| function testEarnGemContractPausedReverts() public { | ||
| // pause the contract | ||
| _gemGame.pause(); | ||
|
|
||
| // attempt to earn a gem | ||
| vm.expectRevert(ContractPaused.selector); | ||
| _gemGame.earnGem(); | ||
| } | ||
|
|
||
| function testPausePausesContract() public { | ||
| // pause the contract | ||
| _gemGame.pause(); | ||
|
|
||
| assertEq(_gemGame.paused(), true, "GemGame should be paused"); | ||
| } | ||
|
|
||
| function testPauseWithoutPauseRoleReverts() public { | ||
| // revoke the pause role | ||
| _gemGame.revokeRole(keccak256("PAUSE"), address(this)); | ||
|
|
||
| // attempt to pause | ||
| vm.expectRevert(Unauthorized.selector); | ||
| _gemGame.pause(); | ||
| } | ||
|
|
||
| function testUnpauseUnpausesContract() public { | ||
| // pause the contract | ||
| _gemGame.pause(); | ||
|
|
||
| // unpause the contract | ||
| _gemGame.unpause(); | ||
|
|
||
| assertEq(_gemGame.paused(), false, "GemGame should be unpaused"); | ||
| } | ||
|
|
||
| function testUnpauseWithoutPauseRoleReverts() public { | ||
| // revoke the unpause role | ||
| _gemGame.revokeRole(keccak256("UNPAUSE"), address(this)); | ||
|
|
||
| // attempt to unpause | ||
| vm.expectRevert(Unauthorized.selector); | ||
| _gemGame.unpause(); | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't need this anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we do, specifically for this line https://github.com/immutable/contracts/pull/200/files#diff-fe9f97ca149602fd40e7caaba1458383ddde181249e482aaa94bea8bbc412803R61
It's for any code using
block.timestampThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah!