diff --git a/circom b/circom new file mode 160000 index 00000000..ccc8cd74 --- /dev/null +++ b/circom @@ -0,0 +1 @@ +Subproject commit ccc8cd743c38ff4d24f6c8b68a8ad7b3093705db diff --git a/frontend/src/components/molecules/EventGroupTab.tsx b/frontend/src/components/molecules/EventGroupTab.tsx index d4ccaeae..c363995d 100644 --- a/frontend/src/components/molecules/EventGroupTab.tsx +++ b/frontend/src/components/molecules/EventGroupTab.tsx @@ -27,6 +27,8 @@ const EventGroupTab: FC = ({ group }) => { return 1; case router.asPath.includes("role"): return 2; + case router.asPath.includes("transfer"): + return 3; default: break; } @@ -81,6 +83,21 @@ const EventGroupTab: FC = ({ group }) => { {t.RBAC_EDIT_COLLABORATORS} )} + {(group.ownerAddress === address) && ( + + router.push(`/event-groups/${router.query.eventgroupid}/transfer`) + } + > + {t.EVENT_GROUP_TAB_TRANSFER} + + )} ); diff --git a/frontend/src/locales/en.ts b/frontend/src/locales/en.ts index 927fc271..0a025962 100644 --- a/frontend/src/locales/en.ts +++ b/frontend/src/locales/en.ts @@ -43,6 +43,7 @@ export default { // Event group tab EVENT_GROUP_TAB_EVENTS: "Event List", EVENT_GROUP_TAB_LEADERS: "Leader Board", + EVENT_GROUP_TAB_TRANSFER: "Transfer Owner", // Event group leaders EVENT_GROUP_LEADERS_RANK: "RANK", EVENT_GROUP_LEADERS_ADDRESS: "ADDRESS", diff --git a/frontend/src/locales/ja.ts b/frontend/src/locales/ja.ts index f39d40d9..6c6942dc 100644 --- a/frontend/src/locales/ja.ts +++ b/frontend/src/locales/ja.ts @@ -43,6 +43,7 @@ export default { // Event group tab EVENT_GROUP_TAB_EVENTS: "イベント一覧", EVENT_GROUP_TAB_LEADERS: "リーダーボード", + EVENT_GROUP_TAB_TRANSFER: "権限譲与", // Event group leaders EVENT_GROUP_LEADERS_RANK: "ランク", EVENT_GROUP_LEADERS_ADDRESS: "アドレス", diff --git a/hardhat/contracts/Event.sol b/hardhat/contracts/Event.sol index bf7dc1f2..e0bb9179 100644 --- a/hardhat/contracts/Event.sol +++ b/hardhat/contracts/Event.sol @@ -194,6 +194,44 @@ contract EventManager is OwnableUpgradeable { return _groups; } + function transferGroupOwner( + uint256 _groupId, + address _newOwnerAddress + ) external whenNotPaused { + bool isOwner = false; + require( + _newOwnerAddress != address(0), + "New owner address is blank" + ); + + for (uint256 i = 0; i < ownGroupIds[msg.sender].length; i++) { + if (ownGroupIds[msg.sender][i] == _groupId) { + isOwner = true; + break; + } + } + require(isOwner, "Caller is not the owner of the group"); + + for (uint256 i = 0; i < groups.length; i++) { + if (groups[i].groupId == _groupId) { + groups[i].ownerAddress = _newOwnerAddress; + break; + } + } + + // Update the mapping of ownGroupIds for the new owner + ownGroupIds[_newOwnerAddress].push(_groupId); + + // Remove the groupId from the list for the current owner + for (uint256 i = 0; i < ownGroupIds[msg.sender].length; i++) { + if (ownGroupIds[msg.sender][i] == _groupId) { + ownGroupIds[msg.sender][i] = ownGroupIds[msg.sender][ownGroupIds[msg.sender].length - 1]; + ownGroupIds[msg.sender].pop(); + break; + } + } + } + function createEventRecord( uint256 _groupId, string memory _name, diff --git a/hardhat/test/EventManager.ts b/hardhat/test/EventManager.ts index 1066b3bd..690b7751 100644 --- a/hardhat/test/EventManager.ts +++ b/hardhat/test/EventManager.ts @@ -39,9 +39,10 @@ describe("EventManager", function () { let participant1: SignerWithAddress; let participant2: SignerWithAddress; let relayer: SignerWithAddress; + let newOwner: SignerWithAddress; before(async () => { - [organizer, participant1, participant2, relayer] = + [organizer, participant1, participant2, relayer, newOwner] = await ethers.getSigners(); const SecretPhraseVerifierFactory = await ethers.getContractFactory( "SecretPhraseVerifier" @@ -698,6 +699,51 @@ describe("EventManager", function () { }); }); + describe("GetOwnTransfer", function () { + let eventManager: EventManager; + before(async () => { + const eventManagerContractFactory = await ethers.getContractFactory("EventManager"); + const deployedEventManagerContract = await upgrades.deployProxy( + eventManagerContractFactory, + [ + organizer.address, + relayer.address, + 250000, + 1000000, + operationController.address + ], + { initializer: "initialize" } + ); + eventManager = deployedEventManagerContract as EventManager; + await eventManager.deployed(); + await eventManager.setMintNFTAddr(mintNFT.address); + await mintNFT.setEventManagerAddr(eventManager.address); + + const createGroupTx = await eventManager.connect(organizer).createGroup("transferGroup"); + await createGroupTx.wait(); + + const createdGroups = await eventManager.getGroups(); + expect(createdGroups.some(group => group.name === "transferGroup")).to.be.true; + }); + + it("Should transfer group ownership", async function () { + const groups = await eventManager.getGroups(); + const group = groups.find(group => group.name === "transferGroup"); + expect(group, "Group not found").to.exist; + + const groupId = group!.groupId; + + const transferTx = await eventManager.connect(organizer).transferGroupOwner(groupId, newOwner.address); + await transferTx.wait(); + + const updatedGroups = await eventManager.getGroups(); + const updatedGroup = updatedGroups.find(group => group.groupId.eq(groupId)); + + expect(updatedGroup, "Updated group not found").to.exist; + expect(updatedGroup!.ownerAddress).to.equal(newOwner.address); + }); + }); + describe("Role", async () => { let eventManager: EventManager;