Skip to content

feat(contract): Feature/issue 374 role hat integration via Hat protocol instead of authority in module contract #378

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 15 commits into from
Jul 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions .github/workflows/test-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,9 @@ jobs:

- name: Install dependencies
run: pnpm install
working-directory: ./pkgs/contract

- name: Lint Check
run: pnpm lint
working-directory: ./pkgs/contract

- name: Run Hardhat tests
run: |
npx hardhat test > ./test-results.txt
echo "\`\`\`\n$(cat ./test-results.txt)" > ./comments
working-directory: ./pkgs/contract
run: pnpm contract test
env:
PRIVATE_KEY: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

Expand Down
4 changes: 2 additions & 2 deletions docs/puml/class-v2.puml
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ BigBang --|> OwnableUpgradeable
BigBang --|> UUPSUpgradeable

HatsTimeFrameModule --|> HatsModule
HatsTimeFrameModule --|> OwnableUpgradeable
HatsTimeFrameModule --|> Upgradeable

HatsHatCreatorModule --|> HatsModule
HatsHatCreatorModule --|> OwnableUpgradeable
HatsHatCreatorModule --|> Upgradeable

FractionToken --|> ERC1155Upgradeable
FractionToken --|> ERC1155SupplyUpgradeable
Expand Down
2 changes: 1 addition & 1 deletion lefthook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ pre-commit:
type-check:
glob: "*.{ts,tsx}"
run: |
npx pnpm frontend typecheck
pnpm frontend typecheck
51 changes: 44 additions & 7 deletions pkgs/contract/contracts/bigbang/BigBang.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable {

ISplitsCreatorFactory public SplitsCreatorFactory;

uint32 private maxTobanSupply = 10;

address public HatsTimeFrameModule_IMPL;

address public HatsHatCreatorModule_IMPL;
Expand All @@ -29,6 +31,9 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable {
address indexed owner,
uint256 indexed topHatId,
uint256 hatterHatId,
uint256 operatorHatId,
uint256 creatorHatId,
uint256 minterHatId,
address hatsTimeFrameModule,
address hatsHatCreatorModule,
address splitCreator
Expand Down Expand Up @@ -100,25 +105,54 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable {
_hatterHatImageURI
);

// 3. HatsHatCreatorModuleのデプロイ
// 3. Create Fixed Roles under TopHat
uint256 operatorHatId = Hats.createHat(
topHatId,
_hatterHatDetails,
5,
0x0000000000000000000000000000000000004A75,
0x0000000000000000000000000000000000004A75,
true,
_hatterHatImageURI
);
uint256 creatorHatId = Hats.createHat(
operatorHatId,
_hatterHatDetails,
5,
0x0000000000000000000000000000000000004A75,
0x0000000000000000000000000000000000004A75,
true,
_hatterHatImageURI
);
uint256 minterHatId = Hats.createHat(
operatorHatId,
_hatterHatDetails,
5,
0x0000000000000000000000000000000000004A75,
0x0000000000000000000000000000000000004A75,
true,
_hatterHatImageURI
);

// 4. HatsHatCreatorModuleのデプロイ
address hatsHatCreatorModule = HatsModuleFactory.createHatsModule(
HatsHatCreatorModule_IMPL,
topHatId,
"",
abi.encode(_owner), // ownerを初期化データとして渡す
abi.encode(creatorHatId), // ownerを初期化データとして渡す
0
);

// 4. HatsTimeFrameModuleのデプロイ
// 5. HatsTimeFrameModuleのデプロイ
address hatsTimeFrameModule = HatsModuleFactory.createHatsModule(
HatsTimeFrameModule_IMPL,
topHatId,
"",
abi.encode(_owner), // ownerを初期化データとして渡す
abi.encode(minterHatId), // ownerを初期化データとして渡す
0
);

// 5. HatterHatにHatModuleをMint
// 6. HatterHatにHatModuleをMint
uint256[] memory hatIds = new uint256[](2);
hatIds[0] = hatterHatId;
hatIds[1] = hatterHatId;
Expand All @@ -129,10 +163,10 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable {

Hats.batchMintHats(hatIds, modules);

// 6. TopHatIdの権限を_ownerに譲渡
// 7. TopHatIdの権限を_ownerに譲渡
Hats.transferHat(topHatId, address(this), _owner);

// 7. SplitCreatorをFactoryからデプロイ
// 8. SplitCreatorをFactoryからデプロイ
address splitCreator = SplitsCreatorFactory
.createSplitCreatorDeterministic(
topHatId,
Expand All @@ -148,6 +182,9 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable {
_owner,
topHatId,
hatterHatId,
operatorHatId,
creatorHatId,
minterHatId,
hatsTimeFrameModule,
hatsHatCreatorModule,
splitCreator
Expand Down
51 changes: 44 additions & 7 deletions pkgs/contract/contracts/bigbang/mock/BigBang_Mock_v2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ contract BigBang_Mock_v2 is OwnableUpgradeable, UUPSUpgradeable {

ISplitsCreatorFactory public SplitsCreatorFactory;

uint32 private maxTobanSupply = 10;

address public HatsTimeFrameModule_IMPL;

address public HatsHatCreatorModule_IMPL;
Expand All @@ -29,6 +31,9 @@ contract BigBang_Mock_v2 is OwnableUpgradeable, UUPSUpgradeable {
address indexed owner,
uint256 indexed topHatId,
uint256 hatterHatId,
uint256 operatorHatId,
uint256 creatorHatId,
uint256 minterHatId,
address hatsTimeFrameModule,
address hatsHatCreatorModule,
address splitCreator
Expand Down Expand Up @@ -100,25 +105,54 @@ contract BigBang_Mock_v2 is OwnableUpgradeable, UUPSUpgradeable {
_hatterHatImageURI
);

// 3. HatsHatCreatorModuleのデプロイ
// 3. Create Fixed Roles under TopHat
uint256 operatorHatId = Hats.createHat(
topHatId,
_hatterHatDetails,
5,
0x0000000000000000000000000000000000004A75,
0x0000000000000000000000000000000000004A75,
true,
_hatterHatImageURI
);
uint256 creatorHatId = Hats.createHat(
operatorHatId,
_hatterHatDetails,
5,
0x0000000000000000000000000000000000004A75,
0x0000000000000000000000000000000000004A75,
true,
_hatterHatImageURI
);
uint256 minterHatId = Hats.createHat(
operatorHatId,
_hatterHatDetails,
5,
0x0000000000000000000000000000000000004A75,
0x0000000000000000000000000000000000004A75,
true,
_hatterHatImageURI
);

// 4. HatsHatCreatorModuleのデプロイ
address hatsHatCreatorModule = HatsModuleFactory.createHatsModule(
HatsHatCreatorModule_IMPL,
topHatId,
"",
abi.encode(_owner), // ownerを初期化データとして渡す
abi.encode(creatorHatId), // ownerを初期化データとして渡す
0
);

// 4. HatsTimeFrameModuleのデプロイ
// 5. HatsTimeFrameModuleのデプロイ
address hatsTimeFrameModule = HatsModuleFactory.createHatsModule(
HatsTimeFrameModule_IMPL,
topHatId,
"",
abi.encode(_owner),
abi.encode(minterHatId), // ownerを初期化データとして渡す
0
);

// 5. HatterHatにHatModuleをMint
// 6. HatterHatにHatModuleをMint
uint256[] memory hatIds = new uint256[](2);
hatIds[0] = hatterHatId;
hatIds[1] = hatterHatId;
Expand All @@ -129,10 +163,10 @@ contract BigBang_Mock_v2 is OwnableUpgradeable, UUPSUpgradeable {

Hats.batchMintHats(hatIds, modules);

// 6. TopHatIdの権限を_ownerに譲渡
// 7. TopHatIdの権限を_ownerに譲渡
Hats.transferHat(topHatId, address(this), _owner);

// 7. SplitCreatorをFactoryからデプロイ
// 8. SplitCreatorをFactoryからデプロイ
address splitCreator = SplitsCreatorFactory
.createSplitCreatorDeterministic(
topHatId,
Expand All @@ -148,6 +182,9 @@ contract BigBang_Mock_v2 is OwnableUpgradeable, UUPSUpgradeable {
_owner,
topHatId,
hatterHatId,
operatorHatId,
creatorHatId,
minterHatId,
hatsTimeFrameModule,
hatsHatCreatorModule,
splitCreator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,43 @@ pragma solidity ^0.8.24;

import {IHatsHatCreatorModule} from "./IHatsHatCreatorModule.sol";
import {HatsModule} from "../../hats/module/HatsModule.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract HatsHatCreatorModule is HatsModule, Ownable, IHatsHatCreatorModule {
/// @dev Mapping to track addresses with hat creation authority
mapping(address => bool) public createHatAuthorities;
contract HatsHatCreatorModule is HatsModule, IHatsHatCreatorModule {
uint256 private creatorHatId;

/**
* @dev Constructor to initialize the contract
* @param _version The version of the contract
* @param _tmpOwner The owner of the contract
*/
constructor(
string memory _version,
address _tmpOwner
) HatsModule(_version) Ownable(_tmpOwner) {}
constructor(string memory _version) HatsModule(_version) {}

/**
* @dev Initializes the contract, setting up the owner
* @param _initData The initialization data (encoded owner address)
* @dev Initializes the contract, hold creator toban ID
* @param _initData The initialization data (encoded creator toban ID)
*/
function _setUp(bytes calldata _initData) internal override {
address _owner = abi.decode(_initData, (address));
_grantCreateHatAuthority(_owner);
_transferOwnership(_owner);
uint256 _creatorHatId = abi.decode(_initData, (uint256));
creatorHatId = _creatorHatId;
}

/**
* @notice Checks if an address has hat creation authority
* @notice Checks if an address is authorized to create hats
* @param authority The address to check
* @return bool Whether the address has authority
*/
function hasCreateHatAuthority(
address authority
) public view returns (bool) {
return createHatAuthorities[authority];
}

/**
* @notice Grants hat creation authority to an address
* @param authority The address to grant authority to
* @return bool Whether the address is authorized
*/
function grantCreateHatAuthority(address authority) external onlyOwner {
_grantCreateHatAuthority(authority);
function _authorized(address authority) internal view returns (bool) {
return
HATS().isAdminOfHat(authority, creatorHatId) ||
HATS().isWearerOfHat(authority, creatorHatId);
}

/**
* @notice Revokes hat creation authority from an address
* @param authority The address to revoke authority from
* @notice Checks if an address has hat creation authority
* @param authority The address to check
* @return bool Whether the address has authority
*/
function revokeCreateHatAuthority(address authority) external onlyOwner {
_revokeCreateHatAuthority(authority);
function hasAuthority(address authority) public view returns (bool) {
return _authorized(authority);
}

/**
Expand All @@ -76,7 +62,7 @@ contract HatsHatCreatorModule is HatsModule, Ownable, IHatsHatCreatorModule {
bool _mutable,
string calldata _imageURI
) external returns (uint256) {
require(hasCreateHatAuthority(msg.sender), "Not authorized");
require(hasAuthority(msg.sender), "Not authorized");

return
HATS().createHat(
Expand All @@ -100,7 +86,7 @@ contract HatsHatCreatorModule is HatsModule, Ownable, IHatsHatCreatorModule {
uint256 hatId,
string calldata newDetails
) external override {
require(hasCreateHatAuthority(msg.sender), "Not authorized");
require(hasAuthority(msg.sender), "Not authorized");
HATS().changeHatDetails(hatId, newDetails);
emit HatDetailsChanged(hatId, newDetails);
}
Expand All @@ -115,7 +101,7 @@ contract HatsHatCreatorModule is HatsModule, Ownable, IHatsHatCreatorModule {
uint256 hatId,
string calldata newImageURI
) external override {
require(hasCreateHatAuthority(msg.sender), "Not authorized");
require(hasAuthority(msg.sender), "Not authorized");
HATS().changeHatImageURI(hatId, newImageURI);
emit HatImageURIChanged(hatId, newImageURI);
}
Expand All @@ -130,33 +116,8 @@ contract HatsHatCreatorModule is HatsModule, Ownable, IHatsHatCreatorModule {
uint256 hatId,
uint32 newMaxSupply
) external override {
require(hasCreateHatAuthority(msg.sender), "Not authorized");
require(hasAuthority(msg.sender), "Not authorized");
HATS().changeHatMaxSupply(hatId, newMaxSupply);
emit HatMaxSupplyChanged(hatId, newMaxSupply);
}

// Internal Functions

/**
* @dev Grants hat creation authority to an address
* @param authority The address to grant authority to
*/
function _grantCreateHatAuthority(address authority) internal {
require(authority != address(0), "Invalid address");
require(!hasCreateHatAuthority(authority), "Already granted");

createHatAuthorities[authority] = true;
emit CreateHatAuthorityGranted(authority);
}

/**
* @dev Revokes hat creation authority from an address
* @param authority The address to revoke authority from
*/
function _revokeCreateHatAuthority(address authority) internal {
require(hasCreateHatAuthority(authority), "Not granted");

createHatAuthorities[authority] = false;
emit CreateHatAuthorityRevoked(authority);
}
}
Loading
Loading