generated from Hats-Protocol/hats-module-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from Hats-Protocol/chaining-compatibility
HatControlledModule
- Loading branch information
Showing
9 changed files
with
460 additions
and
25 deletions.
There are no files selected for viewing
This file contains 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
Submodule forge-std
updated
40 files
Submodule hats-module
updated
13 files
+3 −3 | .gitmodules | |
+1 −1 | lib/solady | |
+1 −1 | script/HatsEligibilitiesChain.s.sol | |
+1 −1 | script/HatsModuleFactory.s.sol | |
+1 −1 | script/HatsTogglesChain.s.sol | |
+11 −11 | src/HatsEligibilitiesChain.sol | |
+35 −21 | src/HatsModuleFactory.sol | |
+11 −11 | src/HatsTogglesChain.sol | |
+4 −3 | src/utils/DeployFunctions.sol | |
+36 −32 | test/HatsEligibilitiesChain.t.sol | |
+11 −6 | test/HatsModule.t.sol | |
+118 −34 | test/HatsModuleFactory.t.sol | |
+34 −28 | test/HatsTogglesChain.t.sol |
This file contains 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 |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.19; | ||
|
||
import { Script, console2 } from "forge-std/Script.sol"; | ||
import { HatControlledModule } from "../src/HatControlledModule.sol"; | ||
|
||
contract Deploy is Script { | ||
HatControlledModule public implementation; | ||
bytes32 public constant SALT = bytes32(abi.encode(0x4a75)); // ~ H(4) A(a) T(7) S(5) | ||
|
||
// default values | ||
bool internal _verbose = true; | ||
string internal _version = "0.1.0"; // initial version for HatControlledModule | ||
|
||
/// @dev Override default values, if desired | ||
function prepare(bool verbose, string memory version) public { | ||
_verbose = verbose; | ||
_version = version; | ||
} | ||
|
||
/// @dev Set up the deployer via their private key from the environment | ||
function deployer() public returns (address) { | ||
uint256 privKey = vm.envUint("PRIVATE_KEY"); | ||
return vm.rememberKey(privKey); | ||
} | ||
|
||
function _log(string memory prefix) internal view { | ||
if (_verbose) { | ||
console2.log(string.concat(prefix, "Module:"), address(implementation)); | ||
} | ||
} | ||
|
||
/// @dev Deploy the contract to a deterministic address via forge's create2 deployer factory. | ||
function run() public virtual { | ||
vm.startBroadcast(deployer()); | ||
|
||
implementation = new HatControlledModule{ salt: SALT }(_version); | ||
|
||
vm.stopBroadcast(); | ||
|
||
_log(""); | ||
} | ||
} | ||
|
||
/* FORGE CLI COMMANDS | ||
## A. Simulate the deployment locally | ||
forge script script/DeployHatControlledModule.s.sol -f mainnet | ||
## B. Deploy to real network and verify on etherscan | ||
forge script script/DeployHatControlledModule.s.sol -f mainnet --broadcast --verify | ||
## C. Fix verification issues (replace values in curly braces with the actual values) | ||
forge verify-contract --chain-id 1 --num-of-optimizations 1000000 --watch --constructor-args $(cast abi-encode \ | ||
"constructor(string)" "_version") \ | ||
--compiler-version v0.8.19 {deploymentAddress} \ | ||
src/HatControlledModule.sol:HatControlledModule --etherscan-api-key $ETHERSCAN_KEY | ||
## D. To verify ir-optimized contracts on etherscan... | ||
1. Run (C) with the following additional flag: `--show-standard-json-input > etherscan.json` | ||
2. Patch `etherscan.json`: `"optimizer":{"enabled":true,"runs":100}` => | ||
`"optimizer":{"enabled":true,"runs":100},"viaIR":true` | ||
3. Upload the patched `etherscan.json` to etherscan manually | ||
See this github issue for more: https://github.com/foundry-rs/foundry/issues/3507#issuecomment-1465382107 | ||
*/ |
This file contains 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 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 |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.19; | ||
|
||
// import { console2 } from "forge-std/Test.sol"; // remove before deploy | ||
import { HatsEligibilityModule, HatsModule } from "hats-module/HatsEligibilityModule.sol"; | ||
import { HatsToggleModule } from "hats-module/HatsToggleModule.sol"; | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
CUSTOM ERRORS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Thrown when the caller is not wearing the {hatId} hat | ||
error NotAuthorized(); | ||
|
||
/** | ||
* @title HatControlledModule | ||
* @author spengrah | ||
* @author Haberdasher Labs | ||
* @notice This module allows the wearer(s) of a given "controller" hat to serve as the eligibilty and/or toggle module | ||
* for a different hat. It is compatible with module chaining. | ||
* @dev This contract inherits from HatsModule, and is intended to be deployed as minimal proxy clone(s) via | ||
* HatsModuleFactory. For this contract to be used, it must be set as either the eligibility or toggle module for | ||
* another hat. | ||
*/ | ||
contract HatControlledModule is HatsEligibilityModule, HatsToggleModule { | ||
/*////////////////////////////////////////////////////////////// | ||
EVENTS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Emitted when the wearer status is set | ||
event WearerStatusSet(address wearer, uint256 hatId, bool eligible, bool standing); | ||
|
||
/// @notice Emitted when the hat status is set | ||
event HatStatusSet(uint256 hatId, bool active); | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
DATA MODELS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* @notice Ineligibility and standing data for an account, defaulting to positives. | ||
* @param ineligible Whether the account is ineligible to wear the hat. Defaults to eligible. | ||
* @param badStanding Whether the account is in bad standing for the hat. Defaults to good standing. | ||
*/ | ||
struct IneligibilityData { | ||
bool ineligible; | ||
bool badStanding; | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
CONSTANTS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* This contract is a clone with immutable args, which means that it is deployed with a set of | ||
* immutable storage variables (ie constants). Accessing these constants is cheaper than accessing | ||
* regular storage variables (such as those set on initialization of a typical EIP-1167 clone), | ||
* but requires a slightly different approach since they are read from calldata instead of storage. | ||
* | ||
* Below is a table of constants and their location. | ||
* | ||
* For more, see here: https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args | ||
* | ||
* ----------------------------------------------------------------------+ | ||
* CLONE IMMUTABLE "STORAGE" | | ||
* ----------------------------------------------------------------------| | ||
* Offset | Constant | Type | Length | Source | | ||
* ----------------------------------------------------------------------| | ||
* 0 | IMPLEMENTATION | address | 20 | HatsModule | | ||
* 20 | HATS | address | 20 | HatsModule | | ||
* 40 | hatId | uint256 | 32 | HatsModule | | ||
* 72 | CONTROLLER_HAT | uint256 | 32 | HatControlledModule | | ||
* ----------------------------------------------------------------------+ | ||
*/ | ||
|
||
/// @notice The hat that controls this module instance and can set wearer and hat statuses | ||
function CONTROLLER_HAT() public pure returns (uint256) { | ||
return _getArgUint256(72); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
MUTABLE STORAGE | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Ineligibility and standing data for a given hat and wearer, defaulting to eligible and good standing | ||
mapping(uint256 hatId => mapping(address wearer => IneligibilityData ineligibility)) internal wearerIneligibility; | ||
|
||
/// @notice Status of a given hat | ||
mapping(uint256 hatId => bool inactive) internal hatInactivity; | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
CONSTRUCTOR | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Deploy the implementation contract and set its version | ||
/// @dev This is only used to deploy the implementation contract, and should not be used to deploy clones | ||
constructor(string memory _version) HatsModule(_version) { } | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
INITIALIZER | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @inheritdoc HatsModule | ||
function _setUp(bytes calldata) internal override { | ||
// no initial values to set | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
ELIGIBILITY FUNCTIONS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* @notice Set the eligibility status of a `_hatId` for a `_wearer`, in this contract. When this contract is set as | ||
* the eligibility module for that `hatId`, including as part of a module chain, Hats Protocol will pull this data | ||
* when checking the wearer's eligibility. | ||
* @dev Only callable by the wearer(s) of the {hatId} hat. | ||
* @param _wearer The address to set the eligibility status for | ||
* @param _hatId The hat to set the eligibility status for | ||
* @param _eligible The new _wearer's eligibility, where TRUE = eligible | ||
* @param _standing The new _wearer's standing, where TRUE = in good standing | ||
*/ | ||
function setWearerStatus(address _wearer, uint256 _hatId, bool _eligible, bool _standing) public onlyController { | ||
wearerIneligibility[_hatId][_wearer] = IneligibilityData(!_eligible, !_standing); | ||
emit WearerStatusSet(_wearer, _hatId, _eligible, _standing); | ||
} | ||
|
||
/// @inheritdoc HatsEligibilityModule | ||
function getWearerStatus(address _wearer, uint256 _hatId) public view override returns (bool eligible, bool standing) { | ||
IneligibilityData memory data = wearerIneligibility[_hatId][_wearer]; | ||
// bad standing means not eligible, as well | ||
if (data.badStanding) return (false, false); | ||
// good standing but ineligible | ||
if (data.ineligible) return (false, true); | ||
// eligible and in good standing | ||
return (true, true); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
TOGGLE FUNCTIONS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* @notice Toggle the status of `_hatId` in this contract. When this contract is set as the toggle module for that | ||
* `hatId`, including as part of a module chain, Hats Protocol will pull this data when checking the status of the | ||
* hat. | ||
* @dev Only callable by the wearer(s) of the {hatId} hat. | ||
* @param _hatId The hat to set the status for | ||
* @param _newStatus The new status, where TRUE = active | ||
*/ | ||
function setHatStatus(uint256 _hatId, bool _newStatus) public onlyController { | ||
hatInactivity[_hatId] = !_newStatus; | ||
emit HatStatusSet(_hatId, _newStatus); | ||
} | ||
|
||
/// @inheritdoc HatsToggleModule | ||
function getHatStatus(uint256 _hatId) public view override returns (bool active) { | ||
return !hatInactivity[_hatId]; | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
MODIFIERS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Reverts if the caller is not wearing the {hatId} hat | ||
modifier onlyController() { | ||
if (!HATS().isWearerOfHat(msg.sender, CONTROLLER_HAT())) revert NotAuthorized(); | ||
_; | ||
} | ||
} |
This file contains 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
Oops, something went wrong.