From 61781424f83d70b1ff516ab26e118188986ddd29 Mon Sep 17 00:00:00 2001 From: bearni95 Date: Fri, 11 Oct 2024 01:06:43 +0200 Subject: [PATCH 01/12] tbz updated blacklist --- .../deployments/blacklist/hekla.json | 3 + .../deployments/blacklist/mainnet.json | 3 + packages/supplementary-contracts/package.json | 4 +- .../script/blacklist/Deploy.data.json | 155 +++++++++++++++++- .../script/blacklist/Deploy.sol | 72 ++++++-- 5 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 packages/supplementary-contracts/deployments/blacklist/hekla.json create mode 100644 packages/supplementary-contracts/deployments/blacklist/mainnet.json diff --git a/packages/supplementary-contracts/deployments/blacklist/hekla.json b/packages/supplementary-contracts/deployments/blacklist/hekla.json new file mode 100644 index 0000000000..84a53c5c7c --- /dev/null +++ b/packages/supplementary-contracts/deployments/blacklist/hekla.json @@ -0,0 +1,3 @@ +{ + "Blacklist": "0xD10a2c799F61A89B8f6D8C695c48A4F38684ab30" +} diff --git a/packages/supplementary-contracts/deployments/blacklist/mainnet.json b/packages/supplementary-contracts/deployments/blacklist/mainnet.json new file mode 100644 index 0000000000..bcadc7ecfe --- /dev/null +++ b/packages/supplementary-contracts/deployments/blacklist/mainnet.json @@ -0,0 +1,3 @@ +{ + "Blacklist": "0x5e3052424E51a62c4FDFf4F1dD42300F59c9C57A" +} diff --git a/packages/supplementary-contracts/package.json b/packages/supplementary-contracts/package.json index db4335fa5e..ba6c6f93d6 100644 --- a/packages/supplementary-contracts/package.json +++ b/packages/supplementary-contracts/package.json @@ -9,7 +9,9 @@ "layout": "./deployments/gen-layouts.sh", "fmt:sol": "forge fmt", "lint:sol": "forge fmt && pnpm solhint 'contracts/**/*.sol'", - "test": "forge test -vvv --match-path test/*.t.sol" + "test": "forge test -vvv --match-path test/*.t.sol", + "deploy:blacklist:hekla": "forge script script/blacklist/Deploy.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200", + "deploy:blacklist:mainnet": "forge script script/blacklist/Deploy.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100" }, "devDependencies": { "@types/node": "^20.11.30", diff --git a/packages/supplementary-contracts/script/blacklist/Deploy.data.json b/packages/supplementary-contracts/script/blacklist/Deploy.data.json index 415c666f0a..f0f0137813 100644 --- a/packages/supplementary-contracts/script/blacklist/Deploy.data.json +++ b/packages/supplementary-contracts/script/blacklist/Deploy.data.json @@ -1,6 +1,6 @@ { - "admin": "0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be", - "updater": "0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be", + "admin": "0x7d70236E2517f5B95247AF1d806A9E3C328a7860", + "updater": "0x7d70236E2517f5B95247AF1d806A9E3C328a7860", "blacklist": [ "0x098B716B8Aaf21512996dC57EB0615e2383E2f96", "0xa0e1c89Ef1a489c9C7dE96311eD5Ce5D32c20E4B", @@ -155,6 +155,155 @@ "0x175d44451403Edf28469dF03A9280c1197ADb92c", "0x21B8d56BDA776bbE68655A16895afd96F5534feD", "0xE950DC316b836e4EeFb8308bf32Bf7C72a1358FF", - "0x19F8f2B0915Daa12a3f5C9CF01dF9E24D53794F7" + "0x19F8f2B0915Daa12a3f5C9CF01dF9E24D53794F7", + "0xB9C144ee9Db3850BB5c1598f9b1B03CD68812e30", + "0x683279542eD04d7C60DC56E4EA230fe621eDD4Ca", + "0x29E866A8471DA4f89b4AFF3FD41c801d8027058b", + "0xa2C13a6913D1DF18ff4f5de2bc342023Cd683C38", + "0x5c913285DD9421EDc9d8f1000A35D9bbDF0DCb62", + "0x4ee49B318B785BC358f947BfdCec46dFA12b1Ac1", + "0x6d9999D1aC0027F1E3Dc1e3F154e3366726743b7", + "0x4342F35851fa88873496A49F55ECF79671042bF9", + "0xb226eFF9Be0F679CfB703626C911113513B63bd4", + "0x93ff385916cfcDDA8EB0858276B0074e061E98F4", + "0x29aC5D7972C8B672bF2Ae60eFbc1bEFf48F1F038", + "0xA241038Cb362BDAbd55fDa08233de6f75c764C63", + "0x3d96638B127463e136dAaD12d9bfEAeaFA5437De", + "0x60E9c48A50828c4DC4e36D28fc7EF1b25124e002", + "0xB4c9105B0bF3785C30676D13D210D698Dde84F11", + "0xa4426fA0F0e53Cd06dc41d4fEcF4Bf8783dC5a27", + "0x509BBc835912dF27Fb19837b604b92664A5B8FF1", + "0x08103076D7b1219c7135D89fC0a1c90D26FB307F", + "0x2Cfd96Fb65a990F1b5193992a47e0b6D918Df0Dc", + "0xB33e272E9B62409a53dA0171Eaf88eFef8eE4692", + "0x64FF010064656dF2FC55a3b7D99420Bc4A07c56A", + "0xcDc7306b3c9E0F2947a7B62A137eF90806901f37", + "0xAF5c672f943cd547B7938158A46D08b584c492E8", + "0xAd5e1B0Ed51b625519f81D43117b479DE7e9AbDC", + "0xF8f5811d342DdC2a6320C205856977CfE3289f29", + "0xdd8BD6CEDE3D9eD2D453216df394C5bC8b05046a", + "0x72a2B878e6A909192b0aD91AF976494Ba5143855", + "0xBa5613D19214eD93844AabD24F1102040aceae4f", + "0xaf00a8b4e83A005921D0f878d20006DE0cDA044C", + "0x1a903fe2f732F96bAa7292A05f4121766dD7e07a", + "0xB84F419FD6DC9C30cCAA6ecbF5d194Af065A33ff", + "0xb787c56Bd3c21FB6C18Ceb9a3E49637ab7cD4Dd9", + "0xE2189aa87c1Ad7654bE7Ee258f348d6484F68165", + "0xbdaD5D2d23c0d5D6B17E402a79658f787328ee08", + "0xA189b646c8983253De9eE06C815c51171F265f81", + "0x660888f021BB7da1004869C6a51059Ed97d9e7c0", + "0x1dCFB0dB0D1A774Da525F1200E2c549Bf8A9DC70", + "0x4BA67EC73A7aBF9E015eeFC1979053CE0E56B369", + "0xF46dC57a0d8B614a0e8571217316d24B840ED9BC", + "0x3099cE94b9dDB6715B1169F61Aa0e16ad818Ce89", + "0x24317cAB3C632E0B1eD0d21De1E0a009359B1296", + "0xb692c229A8622Ad1dB19b18065E69B2b6116b9ef", + "0xC478e784b91b19665470CeAaCfa39Ba17D0f6183", + "0xEF8F0d7684D4405B5c51AEF023675946D5992b29", + "0xE9F41a0090fcc7eaf626037003AAD44B17098E7C", + "0x57B7F15C80461E83Ee56daE6a813A57f41E9b61d", + "0x05431cD62F011Cb44Ec803Fe4AC96FAC651CA494", + "0xcaFd650401708ca4f65a22340D07f78E18f8C332", + "0xbB7Cc747f5279Fde28b0878C928D92Eb6c90deBd", + "0x0f883a80a11Ba4057c265ad3a4C0c4e3869ac4c3", + "0xab2ECcf82a5b66396419da7a6e9fbF6630Bb7521", + "0xEc1C61c1c5cfEaA49141C24fB88815052D2fa6B1", + "0x7282aB49b58C7c493F49F2d5D1a723BdEd47C420", + "0x688c0a468ad7BE8Dc2e1cB92cc1b9b6fdca84d5A", + "0x5065d72003878656670f8C67498924704108C00C", + "0x8B59C939bE050814e56D69A68B7c8E485f79f9c3", + "0x38c104275141eaaE188dE0017eFf125FEb87F218", + "0xEe74356635A216A1fC89D4921495124c1533b0C8", + "0x394702830B44476C919389Cb7bA3726a1A885F3A", + "0xBA7770994a63d874127bC6eF8a0c78CCd5174E13", + "0x4edb34De5A54B42cDb88D7E535EC767AcdCa9Ce1", + "0x9c6F685403491272E6CF6258c32ccaA8E9A1ed21", + "0xdd7B2F2b821Ff20ee52A8787b3D9614b91A2b8B1", + "0xE86d78381c4cd7B4a67278e1194cA7bABc363Af9", + "0x873415F6633A0C42b8717bC898354638F52b13f3", + "0xB439b0844D0D9aF237FF37dc2379f07B6CD06171", + "0x796e3F689D8d475Dc82ce801440B184A76e68175", + "0xCe5CDb2Da5A7F30163126388Dd22a1e8084a9ad1", + "0x85204a5E932b69455822033F33E378DFF4Bb8960", + "0x588137e7c1Fce64a78e092611609B259bb524DaE", + "0x6De556d0d262BCC52fc5594abCcBc3b78524Ae17", + "0x52539Ea869f7162BC18f9c3C2dB595f2Cd69f63b", + "0xA706009f64b8eeDE2005546d18d2cbB87DDd95dF", + "0xD1799f7B0Bb7F48b0e997358eEEac349bA776c49", + "0x8fe67f559f6D6dED602E954d45897ebEE5F7FFec", + "0x464E1e588Bb3f49D039Ab8C4D5E33bB47fF8915C", + "0x9905d5753a005cd3e13D71bD030f0B6654703B81", + "0x4d7bF7e16fA37867b4Efcf7c05BD3d889ACB6ac9", + "0x026649F79905E4c5Fa26DafFbD905B3522d37e10", + "0x160EE9Bc0a0Fa051C6fEE189e9107669ed07DB0a", + "0x84a6b0C091c57313E3350B1C9d59472517f60D41", + "0x6d9807d18374F33213DE86917937544A7095e39e", + "0x9c9a2d9826b7c7e95D686e5714Fb0eF948EbC6B8", + "0x6aca843DD19AE4bfA43b6C53fC2e0c3a0bF6a002", + "0x381a0241a2A6e0217376A44C113707D5F9993e1b", + "0xC12B1050553c11993ed3A6aaB6818de7EC4DE4E2", + "0xc3F6b750Ab8b42D0455Cc90cac0E7B1F6bb6953a", + "0x07C35EF7A4AC953F5B8bBC79E6D497d114c38e6E", + "0x0395c16F061E8D2d79DFadF957fFcb97F2D765D3", + "0x6FFE37F348CCB189aC63BfB69ba6B4Aa0beDC3FB", + "0x7C22a14E676657Ebd573B7BE96AcFb21f09171dc", + "0xEC5568Bb595a311D2060506437D0f16Da8B8562D", + "0xea3D4D7f3484a683c39494D0b2A62C83cD52E8d4", + "0xDbd300Eba0F755Bb06DA769A4E1A84E4330068AF", + "0xE7eeb97861761e0AEE35F3e5dE42e1501C283495", + "0x0DC74caBcfB00ab5Fdeef60088685A71fef97003", + "0x45d993393f7B3Fe781935e1155118C7f830d4415", + "0x96BCa503a3ae8bEb794DaD2602202BB5ECD4AAE6", + "0x3Ad16031c0b29A2a6B7A3FBB5996649e39f4127b", + "0x8Cc38ceC2535DE89f2D573F6a50E74a717B45829", + "0x9fE7EDeDcd7DCF206cBe7F00bD23A6e70b398d74", + "0xa58b94936C196d5263b54232EB7D6C2B99A745b4", + "0xd8C860D789Ec1c6aE9631B680E6555F25Bc28331", + "0x6AEF558e7ebE574D692c2acd4C47232fd54e62b6", + "0xea99b1e741779a99646B7F9D57aF0F8264d94020", + "0x924eC99107E0F6fF59Ab33460ae6be9E8A6f5017", + "0x9a80ba749a649B2604d796e01D8A64Cc7B58ff78", + "0x904F40730023CC632c9dF65606dA0809D00D3B9a", + "0x9ECEc759D9F899Db97C6decDe38731f0A9DB449d", + "0xD6AFE7BA2E3c42C6A2729b861F33B54F8b2E6f2F", + "0xDE367Ba53E9EDC9381Ce00cDBba3DB01497E7974", + "0x37C4334EB5d24DCcCCfEBCC54680C433313EB040", + "0x87cCe15D359633EB901889E1B7CBa95c21053C19", + "0xE0B61B1EE0aa4Cb23d8667fda8F720F9f707c4B1", + "0x697F9Ab686b2834ACd86750B8A9Fd9f26b1f640e", + "0x6873a48E3A67E240D62E6Ec510900C0b9480cF8C", + "0x855980B2B60d8E3b60CFCf92413e3E3aF04AA101", + "0x435f6Fef6f7c1CadcD77CCB4ef1AD66C27FcFe79", + "0x735026cA2D9a0Cd24Da088eD3E1853EDe083Fc97", + "0x41ad8DD494057E87cE16dAeb20A06649E8ca2B50", + "0x87dFedBFD4FBBFDb8153fec55c82A983c299e5ad", + "0xEaA132575a13F88B715738562C7c1A545EC24300", + "0x3D3Aa68b54df9666CB6670A93aBE7A80CA01fbc3", + "0x7135603DAF6E50a76c3603582bdC2f33Fc558494", + "0x321186Ed48B0112Cc7Ed232e621d932c6EB84c61", + "0xc1B906579E383bE6Cb1B1Cc737DBA2e63CCEFa04", + "0x9471e8B1903Ef7f65f4002cFCE147b2646b6BB0f", + "0xc3fd682bdfb5ABab39Eb2c92E3411978e0b57E3f", + "0x477992B48247c889d50c9B0A87B2b2E34cbCA623", + "0x049dD5F736B851419cde65BB94DcE74d48324269", + "0xD9E6718b1C294CCe6EBC67eb6eBdbc02c343E489", + "0x830a02416Fc210C6F50A5afe65384F6c88132BE7", + "0x1b39f5ee1Fb4958F2E4049f69B53F78b75Bf1888", + "0x1742289017b61C48A5B4340666711AEd2Db72B4E", + "0x523c2937b6A1A0df143032033d6824dce499ddBF", + "0xB0a5eA798249fb30E9E1c81d475B051279962BB2", + "0xd2c7EeDBE8D68b045388A79b65915253dB582385", + "0xc1b44b014cD87d88BB04e176ba9324e086e9cd73", + "0x9D9854f6C9588bF5A56158a47090E0DFAC89d596", + "0x40eF5243c64BFa49301adF18b09A79A190CFa266", + "0xCF3c579061fB243aeFab480055B49e9038e6E488", + "0x0117Ce17Bc661d9bc331A328DC57C05d99B087D3", + "0xB2de33b99A59877d0780D4315690a600f47B5996", + "0xd50c9d01223F0EC1184A82eBEA3730Bdbc82238e", + "0x1F4a6756Da7592fc0f9a031D127d016ACc0BfcB2", + "0x63c9E4f81716dEBc57dB9Fbc60600106a44B9964", + "0xB7Cd2CEF367bf5536d7fdF6c491CE158D2651d97", + "0x1aa4a66Ef0cfA99cA9D39FdAD2B05489744C972a", + "0x3150BdCdA1b41E0F2A550955839F6b2C723aD0B7" ] } diff --git a/packages/supplementary-contracts/script/blacklist/Deploy.sol b/packages/supplementary-contracts/script/blacklist/Deploy.sol index f3ce0ced2c..67ee1c65a2 100644 --- a/packages/supplementary-contracts/script/blacklist/Deploy.sol +++ b/packages/supplementary-contracts/script/blacklist/Deploy.sol @@ -9,25 +9,75 @@ import "../../contracts/blacklist/Blacklist.sol"; contract DeployBlacklist is Script { using stdJson for string; - function setUp() public { } + uint256 public chainId; + + string public lowercaseNetworkKey; + string public uppercaseNetworkKey; + string public jsonLocation; + + uint256 public deployerPrivateKey; + address public deployerAddress; + + function getPrivateKey() public view returns (uint256) { + string memory lookupKey = string.concat(uppercaseNetworkKey, "_PRIVATE_KEY"); + return vm.envUint(lookupKey); + } + + function getContractJsonLocation() public view returns (string memory) { + string memory root = vm.projectRoot(); + return string.concat(root, "/deployments/blacklist/", lowercaseNetworkKey, ".json"); + } + + function setUp() public { + // load all network configs + chainId = block.chainid; + + if (chainId == 31_337) { + lowercaseNetworkKey = "localhost"; + uppercaseNetworkKey = "LOCALHOST"; + } else if (chainId == 17_000) { + lowercaseNetworkKey = "holesky"; + uppercaseNetworkKey = "HOLESKY"; + } else if (chainId == 167_001) { + lowercaseNetworkKey = "devnet"; + uppercaseNetworkKey = "DEVNET"; + } else if (chainId == 11_155_111) { + lowercaseNetworkKey = "sepolia"; + uppercaseNetworkKey = "SEPOLIA"; + } else if (chainId == 167_008) { + lowercaseNetworkKey = "katla"; + uppercaseNetworkKey = "KATLA"; + } else if (chainId == 167_000) { + lowercaseNetworkKey = "mainnet"; + uppercaseNetworkKey = "MAINNET"; + } else if (chainId == 167_009) { + lowercaseNetworkKey = "hekla"; + uppercaseNetworkKey = "HEKLA"; + } else { + revert("Unsupported chainId"); + } + + deployerPrivateKey = getPrivateKey(); + deployerAddress = vm.addr(deployerPrivateKey); + jsonLocation = getContractJsonLocation(); + } function run() external { string memory path = "/script/blacklist/Deploy.data.json"; string memory json = vm.readFile(string.concat(vm.projectRoot(), path)); - // get admin address - bytes memory rawPortion = json.parseRaw(".admin"); - address admin = abi.decode(rawPortion, (address)); - // get updater address - rawPortion = json.parseRaw(".updater"); - address updater = abi.decode(rawPortion, (address)); // get initial blacklist - rawPortion = json.parseRaw(".blacklist"); + bytes memory rawPortion = json.parseRaw(".blacklist"); address[] memory blacklist = abi.decode(rawPortion, (address[])); - vm.startBroadcast(); + vm.startBroadcast(deployerPrivateKey); + + Blacklist target = new Blacklist(deployerAddress, deployerAddress, blacklist); + console2.log("Blacklist deployed to ", address(target)); + + string memory finalJson = vm.serializeAddress("", "Blacklist", address(target)); + vm.writeJson(finalJson, jsonLocation); - Blacklist target = new Blacklist(admin, updater, blacklist); - console2.log("Deployed!\n", address(target)); + vm.stopBroadcast(); } } From 1d85a0cb7f9f2ed4e4cf5bf7b3b9b6f3c28319f7 Mon Sep 17 00:00:00 2001 From: Korbinian Date: Thu, 5 Dec 2024 13:47:42 +0100 Subject: [PATCH 02/12] upgradable owner --- .../contracts/eventRegister/EventRegister.sol | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/nfts/contracts/eventRegister/EventRegister.sol b/packages/nfts/contracts/eventRegister/EventRegister.sol index 72b01d77e8..779bb94b54 100644 --- a/packages/nfts/contracts/eventRegister/EventRegister.sol +++ b/packages/nfts/contracts/eventRegister/EventRegister.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24.0; -import "@openzeppelin/contracts/access/AccessControl.sol"; - +import { Ownable2StepUpgradeable } from + "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import { AccessControlUpgradeable } from + "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; /** * @title EventRegister * @notice A contract that allows authorized managers to create events, manage user registrations, @@ -10,7 +12,8 @@ import "@openzeppelin/contracts/access/AccessControl.sol"; * @dev Utilizes OpenZeppelin's AccessControl for role management. The contract does not hold any * Ether. */ -contract EventRegister is AccessControl { + +contract EventRegister is Ownable2StepUpgradeable, AccessControlUpgradeable { /** * @dev The role identifier for event managers. This role allows accounts to create events * and manage registrations. @@ -40,8 +43,7 @@ contract EventRegister is AccessControl { * @dev Mapping from event ID to a mapping of user addresses to their registration status. * Indicates whether a user has registered for a specific event. */ - mapping(uint256 eventId => mapping(address registrant => bool isRegistered)) public - registrations; + mapping(uint256 eventId => mapping(address registrant => bool isRegistered)) public isRegistered; /** * @dev Emitted when a new event is created. @@ -81,6 +83,18 @@ contract EventRegister is AccessControl { */ uint256 private nextEventId; + /** + * @notice Contract initializer + * @dev Initializes the contract by granting the deployer the default admin role. + * The deployer is also granted the EVENT_MANAGER_ROLE. + * The deployer is set as the owner of the contract. + */ + function initialize() external initializer { + __Context_init(); + _grantRole(EVENT_MANAGER_ROLE, _msgSender()); + _transferOwnership(_msgSender()); + } + /** * @notice Initializes the contract by granting the deployer the default admin role. * @dev The deployer of the contract is granted the DEFAULT_ADMIN_ROLE, allowing them to manage @@ -88,7 +102,6 @@ contract EventRegister is AccessControl { */ constructor() { _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); - _grantRole(EVENT_MANAGER_ROLE, _msgSender()); } /** @@ -190,9 +203,9 @@ contract EventRegister is AccessControl { Event memory currentEvent = events[_eventId]; require(currentEvent.exists, "Event not found"); require(currentEvent.registrationOpen, "Registrations closed"); - require(!registrations[_eventId][msg.sender], "Already registered"); + require(!isRegistered[_eventId][msg.sender], "Already registered"); - registrations[_eventId][msg.sender] = true; + isRegistered[_eventId][msg.sender] = true; emit Registered(msg.sender, _eventId); } @@ -212,9 +225,9 @@ contract EventRegister is AccessControl { Event memory currentEvent = events[_eventId]; require(currentEvent.exists, "Event not found"); require(currentEvent.registrationOpen, "Registrations closed"); - require(registrations[_eventId][_user], "Not registered"); + require(isRegistered[_eventId][_user], "Not registered"); - registrations[_eventId][_user] = false; + isRegistered[_eventId][_user] = false; emit Unregistered(_user, _eventId); } @@ -230,7 +243,7 @@ contract EventRegister is AccessControl { uint256 count = 0; for (uint256 i = 0; i < nextEventId; i++) { - if (registrations[i][_user]) { + if (isRegistered[i][_user]) { temp[count] = i; count++; } From 15343f975e55975b77435d24eb4f592b51555526 Mon Sep 17 00:00:00 2001 From: Korbinian Date: Mon, 9 Dec 2024 11:41:23 +0100 Subject: [PATCH 03/12] adjust test --- .../test/trailblazer/eventRegister/EventRegister.t.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol b/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol index 702165d3b2..2176e69830 100644 --- a/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol +++ b/packages/nfts/test/trailblazer/eventRegister/EventRegister.t.sol @@ -97,7 +97,7 @@ contract EventRegisterTest is Test { eventRegister.register(0); vm.stopPrank(); - assertTrue(eventRegister.registrations(0, user1), "User1 should be registered"); + assertTrue(eventRegister.isRegistered(0, user1), "User1 should be registered"); } function testOnlyEventManagersCanOpenCloseRegistrations() public { @@ -125,7 +125,7 @@ contract EventRegisterTest is Test { eventRegister.register(0); vm.stopPrank(); - assertTrue(eventRegister.registrations(0, user1), "User1 should be registered for event 0"); + assertTrue(eventRegister.isRegistered(0, user1), "User1 should be registered for event 0"); } function testUserCannotRegisterTwice() public { @@ -235,8 +235,8 @@ contract EventRegisterTest is Test { eventRegister.register(0); vm.stopPrank(); - assertTrue(eventRegister.registrations(0, user1), "User1 should be registered"); - assertTrue(eventRegister.registrations(0, user2), "User2 should be registered"); + assertTrue(eventRegister.isRegistered(0, user1), "User1 should be registered"); + assertTrue(eventRegister.isRegistered(0, user2), "User2 should be registered"); } function testGetRegisteredEvents() public { From 4fecb208efadaf762048b0bc219ec3a2bb77099a Mon Sep 17 00:00:00 2001 From: Korbinian Date: Mon, 9 Dec 2024 12:05:58 +0100 Subject: [PATCH 04/12] add initialize to deploy script --- packages/nfts/script/trailblazer/eventRegister/Deploy.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nfts/script/trailblazer/eventRegister/Deploy.sol b/packages/nfts/script/trailblazer/eventRegister/Deploy.sol index 647511ea4d..4a8d576803 100644 --- a/packages/nfts/script/trailblazer/eventRegister/Deploy.sol +++ b/packages/nfts/script/trailblazer/eventRegister/Deploy.sol @@ -15,6 +15,11 @@ contract DeployEventRegisterScript is Script { console.log("Deployed EventRegister to:", address(eventRegister), "from", deployerAddress); + // Initialize the contract + eventRegister.initialize(); + + console.log("Initialized EventRegister contract."); + vm.stopBroadcast(); } } From b60a2b57a1c0a8b6ffd2b4088f50e1741096ed4a Mon Sep 17 00:00:00 2001 From: Korbinian Date: Mon, 9 Dec 2024 12:42:50 +0100 Subject: [PATCH 05/12] deployment --- packages/nfts/deployments/eventRegister/hekla.json | 3 +++ packages/nfts/deployments/eventRegister/mainnet.json | 3 +++ .../trailblazer/eventRegister/{Deploy.sol => Deploy.s.sol} | 0 3 files changed, 6 insertions(+) create mode 100644 packages/nfts/deployments/eventRegister/hekla.json create mode 100644 packages/nfts/deployments/eventRegister/mainnet.json rename packages/nfts/script/trailblazer/eventRegister/{Deploy.sol => Deploy.s.sol} (100%) diff --git a/packages/nfts/deployments/eventRegister/hekla.json b/packages/nfts/deployments/eventRegister/hekla.json new file mode 100644 index 0000000000..b40d00f89c --- /dev/null +++ b/packages/nfts/deployments/eventRegister/hekla.json @@ -0,0 +1,3 @@ +{ + "EventRegister": "0x6439Bb31dE508Dc36B8D3A39C389EB8E6C214ebE" +} diff --git a/packages/nfts/deployments/eventRegister/mainnet.json b/packages/nfts/deployments/eventRegister/mainnet.json new file mode 100644 index 0000000000..32df83a78e --- /dev/null +++ b/packages/nfts/deployments/eventRegister/mainnet.json @@ -0,0 +1,3 @@ +{ + "EventRegister": "0x7cbec64D099D49eA91062746B38182455c94862C" +} diff --git a/packages/nfts/script/trailblazer/eventRegister/Deploy.sol b/packages/nfts/script/trailblazer/eventRegister/Deploy.s.sol similarity index 100% rename from packages/nfts/script/trailblazer/eventRegister/Deploy.sol rename to packages/nfts/script/trailblazer/eventRegister/Deploy.s.sol From b9e2675f3503679f4c81378af4fa4ef3696d2da5 Mon Sep 17 00:00:00 2001 From: bearni95 Date: Wed, 11 Dec 2024 13:58:51 +0100 Subject: [PATCH 06/12] the commit --- .../BadgeRecruitmentV2.sol | 95 ++++++ .../TrailblazersS1BadgesV4.sol | 2 +- .../TrailblazersS1BadgesV5.sol | 71 ++++ .../BadgeRecruitment.t.sol | 2 +- .../BadgeRecruitmentV2.t.sol | 306 ++++++++++++++++++ 5 files changed, 474 insertions(+), 2 deletions(-) create mode 100644 packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol create mode 100644 packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol create mode 100644 packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol diff --git a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol new file mode 100644 index 0000000000..eb9d3fc1c5 --- /dev/null +++ b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "./BadgeRecruitment.sol"; + +contract BadgeRecruitmentV2 is BadgeRecruitment { + /// @notice Events + event RecruitmentReset(address indexed user, uint256 indexed s1TokenId, uint256 s1BadgeId); + + /// @notice Errors + error RECRUITMENT_ALREADY_COMPLETED(); + + /// @notice Updated version function + function version() external pure virtual returns (string memory) { + return "V2"; + } + + /// @notice Start a recruitment for a badge + /// @param _s1BadgeId The badge ID (s1) + /// @dev Not all badges are eligible for recruitment at the same time + /// @dev Defines a cooldown for the recruitment to be complete + /// @dev the cooldown is lesser the higher the Pass Tier + /// @dev Must be called from the s1 badges contract + function startRecruitment( + address _user, + uint256 _s1BadgeId, + uint256 _s1TokenId + ) + external + virtual + onlyRole(S1_BADGES_ROLE) + recruitmentOpen(_s1BadgeId) + isNotMigrating(_user) + hasntMigratedInCycle(_s1BadgeId, _user, RecruitmentType.Migration) + { + if (s1Badges.ownerOf(_s1TokenId) != _user) { + revert TOKEN_NOT_OWNED(); + } + _startRecruitment(_user, _s1BadgeId, _s1TokenId, RecruitmentType.Migration); + } + + /// @notice Disable all current recruitments + /// @dev Bypasses the default date checks + function forceDisableAllRecruitments() external virtual onlyRole(DEFAULT_ADMIN_ROLE) { + forceDisableRecruitments(); + } + + /// @notice Get the active recruitment for a user + /// @param _user The user address + /// @return The active recruitment + function getActiveRecruitmentsFor(address _user) public view returns (Recruitment[] memory) { + if (recruitments[_user].length == 0) { + revert RECRUITMENT_NOT_STARTED(); + } + return recruitments[_user]; + } + + /// @notice Reset a recruitment that hasn't been completed + /// @param _user The user address + /// @param _recruitmentIdx The recruitment index + /// @param _s1TokenId The s1 token ID + /// @dev Must be called from the s1 badges contract + function resetRecruitment( + address _user, + uint256 _recruitmentIdx, + uint256 _s1TokenId + ) + public + virtual + onlyRole(S1_BADGES_ROLE) + { + Recruitment memory recruitment_ = recruitments[_user][_recruitmentIdx]; + if (recruitment_.s1TokenId != _s1TokenId) { + revert RECRUITMENT_NOT_STARTED(); + } + + if (recruitment_.s2TokenId != 0) { + revert RECRUITMENT_ALREADY_COMPLETED(); + } + + // reset + uint256 s1BadgeId_ = recruitment_.s1BadgeId; + + //delete + delete recruitments[_user][_recruitmentIdx]; + recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Undefined] + = false; + recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Claim] = + false; + recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Migration] + = false; + + emit RecruitmentReset(_user, _s1TokenId, s1BadgeId_); + } +} diff --git a/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV4.sol b/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV4.sol index c272a55266..f3313f0335 100644 --- a/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV4.sol +++ b/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV4.sol @@ -59,7 +59,7 @@ contract TrailblazersBadgesV4 is TrailblazersBadgesV3 { /// @notice Start recruitment for a badge /// @param _badgeId Badge id - function startRecruitment(uint256 _badgeId) public { + function startRecruitment(uint256 _badgeId) public virtual { if (recruitmentLockDuration == 0) { revert RECRUITMENT_LOCK_DURATION_NOT_SET(); } diff --git a/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol b/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol new file mode 100644 index 0000000000..a3b1ad0558 --- /dev/null +++ b/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "./TrailblazersS1BadgesV4.sol"; +import "./BadgeRecruitment.sol"; +import "./BadgeRecruitmentV2.sol"; + +contract TrailblazersBadgesV5 is TrailblazersBadgesV4 { + /// @notice Errors + error RECRUITMENT_ALREADY_COMPLETED(); + error NOT_OWNER(); + error NOT_IMPLEMENTED(); + /// @notice Updated version function + /// @return Version string + + function version() external pure virtual override returns (string memory) { + return "V5"; + } + + BadgeRecruitmentV2 public recruitmentContractV2; + + function setRecruitmentContractV2(address _recruitmentContractV2) public onlyOwner { + recruitmentContractV2 = BadgeRecruitmentV2(_recruitmentContractV2); + } + + /// @notice Start recruitment for a badge + /// @param _tokenId Token ID + function startRecruitment(uint256 _badgeId, uint256 _tokenId) public { + if (recruitmentLockDuration == 0) { + revert RECRUITMENT_LOCK_DURATION_NOT_SET(); + } + if (ownerOf(_tokenId) != _msgSender()) { + revert NOT_OWNER(); + } + + if (unlockTimestamps[_tokenId] > block.timestamp) { + revert BADGE_LOCKED(); + } + + unlockTimestamps[_tokenId] = block.timestamp + recruitmentLockDuration; + recruitmentContractV2.startRecruitment(_msgSender(), _badgeId, _tokenId); + } + + /// @notice Deprecated of legacy function + function startRecruitment(uint256 /*_badgeId*/ ) public virtual override { + revert NOT_IMPLEMENTED(); + } + + /// @notice Reset an ongoing migration + /// @param _tokenId Token ID + function resetMigration(uint256 _tokenId) public virtual { + if (ownerOf(_tokenId) != _msgSender()) { + revert NOT_OWNER(); + } + + BadgeRecruitment.Recruitment[] memory recruitments_ = + recruitmentContractV2.getActiveRecruitmentsFor(_msgSender()); + + for (uint256 i = 0; i < recruitments_.length; i++) { + // check if the recruitment is ongoing + // AND if it isn't finished (s2TokenId == 0) + if (recruitments_[i].s1TokenId == _tokenId && recruitments_[i].s2TokenId == 0) { + unlockTimestamps[_tokenId] = 0; + recruitmentContractV2.resetRecruitment(_msgSender(), i, _tokenId); + return; + } + } + + revert RECRUITMENT_ALREADY_COMPLETED(); + } +} diff --git a/packages/nfts/test/trailblazers-season-2/BadgeRecruitment.t.sol b/packages/nfts/test/trailblazers-season-2/BadgeRecruitment.t.sol index 45ab1952fc..0d1fac24ef 100644 --- a/packages/nfts/test/trailblazers-season-2/BadgeRecruitment.t.sol +++ b/packages/nfts/test/trailblazers-season-2/BadgeRecruitment.t.sol @@ -16,7 +16,7 @@ import { TrailblazersBadgesV4 } from "../../contracts/trailblazers-season-2/TrailblazersS1BadgesV4.sol"; import { BadgeRecruitment } from "../../contracts/trailblazers-season-2/BadgeRecruitment.sol"; -contract TrailblazersBadgesS2Test is Test { +contract BadgeRecruitmentTest is Test { UtilsScript public utils; TrailblazersBadgesV4 public s1BadgesV4; diff --git a/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol b/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol new file mode 100644 index 0000000000..17fde02d9a --- /dev/null +++ b/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import { Test } from "forge-std/src/Test.sol"; + +import { TrailblazersBadges } from "../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { Merkle } from "murky/Merkle.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { UtilsScript } from "../../script/taikoon/sol/Utils.s.sol"; +import { MockBlacklist } from "../util/Blacklist.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { TrailblazersBadgesS2 } from + "../../contracts/trailblazers-season-2/TrailblazersBadgesS2.sol"; +import { TrailblazerBadgesS1MintTo } from "../util/TrailblazerBadgesS1MintTo.sol"; +import { TrailblazersBadgesV4 } from + "../../contracts/trailblazers-season-2/TrailblazersS1BadgesV4.sol"; +import { BadgeRecruitment } from "../../contracts/trailblazers-season-2/BadgeRecruitment.sol"; +import { BadgeRecruitmentV2 } from "../../contracts/trailblazers-season-2/BadgeRecruitmentV2.sol"; +import "../../contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol"; + +contract BadgeRecruitmentV2Test is Test { + UtilsScript public utils; + + TrailblazersBadgesV5 public s1BadgesV5; + TrailblazersBadgesV4 public s1BadgesV4; + TrailblazersBadgesS2 public s2Badges; + + address public owner = vm.addr(0x5); + + address[3] public minters = [vm.addr(0x1), vm.addr(0x2), vm.addr(0x3)]; + + uint256 public BADGE_ID; + + MockBlacklist public blacklist; + + address mintSigner; + uint256 mintSignerPk; + + uint256 public MAX_INFLUENCES = 3; + uint256 public COOLDOWN_RECRUITMENT = 1 hours; + uint256 public COOLDOWN_INFLUENCE = 5 minutes; + uint256 public INFLUENCE_WEIGHT_PERCENT = 5; + uint256 public MAX_INFLUENCES_DIVIDER = 100; + uint256 public DEFAULT_CYCLE_DURATION = 7 days; + + BadgeRecruitment public recruitmentV1; + BadgeRecruitmentV2 public recruitment; + + function setUp() public { + utils = new UtilsScript(); + utils.setUp(); + blacklist = new MockBlacklist(); + // create whitelist merkle tree + vm.startPrank(owner); + + (mintSigner, mintSignerPk) = makeAddrAndKey("mintSigner"); + + // deploy token with empty root + address impl = address(new TrailblazersBadges()); + address proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall( + TrailblazersBadges.initialize, (owner, "ipfs://", mintSigner, blacklist) + ) + ) + ); + + TrailblazersBadges s1BadgesV2 = TrailblazersBadges(proxy); + + // upgrade s1 badges contract to use the mock version + + s1BadgesV2.upgradeToAndCall( + address(new TrailblazerBadgesS1MintTo()), + abi.encodeCall(TrailblazerBadgesS1MintTo.call, ()) + ); + + BADGE_ID = s1BadgesV2.BADGE_RAVERS(); + + // upgrade s1 contract to v4 + s1BadgesV2.upgradeToAndCall( + address(new TrailblazersBadgesV4()), abi.encodeCall(TrailblazersBadgesV4.version, ()) + ); + + s1BadgesV4 = TrailblazersBadgesV4(address(s1BadgesV2)); + + // upgrade to v5 + s1BadgesV4.upgradeToAndCall( + address(new TrailblazersBadgesV5()), abi.encodeCall(TrailblazersBadgesV5.version, ()) + ); + + s1BadgesV5 = TrailblazersBadgesV5(address(s1BadgesV4)); + + // set cooldown recruitment + s1BadgesV5.setRecruitmentLockDuration(365 days); + + // deploy the s2 erc1155 token contract + + impl = address(new TrailblazersBadgesS2()); + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall(TrailblazersBadgesS2.initialize, (address(recruitmentV1), "ipfs://")) + ) + ); + s2Badges = TrailblazersBadgesS2(proxy); + + // deploy the recruitment contract + BadgeRecruitment.Config memory config = BadgeRecruitment.Config( + COOLDOWN_RECRUITMENT, + COOLDOWN_INFLUENCE, + INFLUENCE_WEIGHT_PERCENT, + MAX_INFLUENCES, + MAX_INFLUENCES_DIVIDER, + DEFAULT_CYCLE_DURATION + ); + + impl = address(new BadgeRecruitment()); + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall( + BadgeRecruitment.initialize, + (address(s1BadgesV2), address(s2Badges), mintSigner, config) + ) + ) + ); + recruitmentV1 = BadgeRecruitment(proxy); + + s1BadgesV5.setRecruitmentContract(address(recruitmentV1)); + s2Badges.setMinter(address(recruitmentV1)); + // enable recruitment for BADGE_ID + uint256[] memory enabledBadgeIds = new uint256[](1); + enabledBadgeIds[0] = BADGE_ID; + recruitmentV1.enableRecruitments(enabledBadgeIds); + + vm.stopPrank(); + } + + function wait(uint256 time) public { + vm.warp(block.timestamp + time); + } + + function test_upgradeV2() public { + vm.startPrank(owner); + recruitmentV1.upgradeToAndCall( + address(new BadgeRecruitmentV2()), abi.encodeCall(BadgeRecruitmentV2.version, ()) + ); + + recruitment = BadgeRecruitmentV2(address(recruitmentV1)); + + assertEq(recruitment.version(), "V2"); + + s1BadgesV5.setRecruitmentContractV2(address(recruitment)); + vm.stopPrank(); + } + + function test_legacy_startRecruitment_throws() public { + test_upgradeV2(); + vm.expectRevert(TrailblazersBadgesV5.NOT_IMPLEMENTED.selector); + s1BadgesV5.startRecruitment(BADGE_ID); + } + + function test_full_recruitment() public { + test_upgradeV2(); + + address minter = minters[0]; + vm.startPrank(minter); + + // mint the badge + bytes32 _hash = s1BadgesV5.getHash(minter, BADGE_ID); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mintSignerPk, _hash); + bool canMint = s1BadgesV5.canMint(abi.encodePacked(r, s, v), minter, BADGE_ID); + assertTrue(canMint); + + s1BadgesV5.mint(abi.encodePacked(r, s, v), BADGE_ID); + uint256 tokenId = s1BadgesV5.tokenOfOwnerByIndex(minter, 0); + + vm.stopPrank(); + + // mint and transfer to minter a secondary badge with id 0 + + vm.startPrank(minters[1]); + _hash = s1BadgesV5.getHash(minters[1], BADGE_ID); + (v, r, s) = vm.sign(mintSignerPk, _hash); + canMint = s1BadgesV5.canMint(abi.encodePacked(r, s, v), minters[1], BADGE_ID); + assertTrue(canMint); + + s1BadgesV5.mint(abi.encodePacked(r, s, v), BADGE_ID); + uint256 secondTokenId = s1BadgesV5.tokenOfOwnerByIndex(minters[1], 0); + + s1BadgesV5.transferFrom(minters[1], minter, secondTokenId); + + // ensure balances + assertEq(s1BadgesV5.balanceOf(minter), 2); + assertEq(s1BadgesV5.balanceOf(minters[1]), 0); + vm.stopPrank(); + + // start migration with first badge, using v1 methods + vm.startPrank(minter); + wait(100); + s1BadgesV5.startRecruitment(BADGE_ID, tokenId); + assertEq(recruitment.isRecruitmentActive(minter), true); + assertEq(s1BadgesV5.balanceOf(minter), 2); + assertEq(s1BadgesV5.unlockTimestamps(tokenId), block.timestamp + 365 days); + + // and end it + wait(COOLDOWN_INFLUENCE); + wait(COOLDOWN_RECRUITMENT); + + // generate the claim hash for the current recruitment + bytes32 claimHash = recruitment.generateClaimHash( + BadgeRecruitment.HashType.End, + minter, + 0 // experience points + ); + + // simulate the backend signing the hash + (v, r, s) = vm.sign(mintSignerPk, claimHash); + + // exercise the randomFromSignature function + recruitment.endRecruitment(claimHash, v, r, s, 0); + + // check for s2 state reset + assertEq(recruitment.isRecruitmentActive(minter), false); + assertEq(recruitment.isInfluenceActive(minter), false); + + // check for s2 mint + assertEq(s2Badges.balanceOf(minter, 1), 1); + + // open a second migration cycle + vm.stopPrank(); + vm.startPrank(owner); + + // enable recruitment for BADGE_ID + uint256[] memory enabledBadgeIds = new uint256[](1); + enabledBadgeIds[0] = BADGE_ID; + recruitment.forceDisableAllRecruitments(); + recruitment.enableRecruitments(enabledBadgeIds); + vm.stopPrank(); + + // expect legacy method to fail + vm.startPrank(minter); + wait(100); + vm.expectRevert(TrailblazersBadgesV4.BADGE_LOCKED.selector); + s1BadgesV5.startRecruitment(BADGE_ID, tokenId); + // time to start the second migration + wait(100); + + s1BadgesV5.startRecruitment(BADGE_ID, secondTokenId); + assertEq(recruitment.isRecruitmentActive(minter), true); + assertEq(s1BadgesV5.balanceOf(minter), 2); + assertEq(s1BadgesV5.unlockTimestamps(secondTokenId), block.timestamp + 365 days); + + vm.stopPrank(); + } + + function test_resetRecruitment() public { + test_full_recruitment(); + address minter = minters[0]; + uint256 ongoingTokenId = s1BadgesV5.tokenOfOwnerByIndex(minter, 1); + uint256 completedTokenId = s1BadgesV5.tokenOfOwnerByIndex(minter, 0); + + assertEq(s2Badges.balanceOf(minter, 1), 1); + + vm.startPrank(minter); + + // expect revert from the completed migration + vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_ALREADY_COMPLETED.selector); + s1BadgesV5.resetMigration(completedTokenId); + + // reset the ongoing recruitment + s1BadgesV5.resetMigration(ongoingTokenId); + + // start over the ongoing migration + wait(100); + s1BadgesV5.startRecruitment(BADGE_ID, ongoingTokenId); + assertEq(recruitment.isRecruitmentActive(minter), true); + assertEq(s1BadgesV5.balanceOf(minter), 2); + assertEq(s1BadgesV5.unlockTimestamps(ongoingTokenId), block.timestamp + 365 days); + + // and end it + wait(COOLDOWN_INFLUENCE); + wait(COOLDOWN_RECRUITMENT); + + bytes32 claimHash = recruitment.generateClaimHash( + BadgeRecruitment.HashType.End, + minter, + 0 // experience points + ); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mintSignerPk, claimHash); + recruitment.endRecruitment(claimHash, v, r, s, 0); + + // check for s2 state reset + assertEq(recruitment.isRecruitmentActive(minter), false); + assertEq(recruitment.isInfluenceActive(minter), false); + + // check for s2 mint + assertEq(s2Badges.balanceOf(minter, 2), 1); + + // fail to reset the ongoing migration + vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_ALREADY_COMPLETED.selector); + s1BadgesV5.resetMigration(ongoingTokenId); + } +} From c430ab392e36f1e310daff917bf31a927c8d7b9b Mon Sep 17 00:00:00 2001 From: bearni95 Date: Wed, 11 Dec 2024 14:19:41 +0100 Subject: [PATCH 07/12] recruitment script --- packages/nfts/package.json | 3 +- .../RecruitmentUpgradeV2.s.sol | 154 ++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol diff --git a/packages/nfts/package.json b/packages/nfts/package.json index 41926afa56..3c3238129d 100644 --- a/packages/nfts/package.json +++ b/packages/nfts/package.json @@ -41,7 +41,8 @@ "tbz:upgradeV3:hekla": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/UpgradeV3.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200", "tbz:upgradeV3:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/UpgradeV3.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", "tbz:upgradeV4:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-badges/UpgradeV4.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", - "tbz-s2:upgradeV2:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-season-2/UpgradeV2.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100" + "tbz-s2:upgradeV2:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-season-2/UpgradeV2.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", + "tbz-s2:upgradeRecruitmentV2:hekla": "forge clean && pnpm compile && forge script script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200" }, "devDependencies": { "@types/node": "^20.11.30", diff --git a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol new file mode 100644 index 0000000000..1c5ef35328 --- /dev/null +++ b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import { UtilsScript, MockBlacklist } from "./Utils.s.sol"; +import { Script, console } from "forge-std/src/Script.sol"; +import { Merkle } from "murky/Merkle.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { TrailblazersBadges } from "../../contracts/trailblazers-badges/TrailblazersBadges.sol"; +import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; + +import "../../contracts/trailblazers-season-2/TrailblazersBadgesS2.sol"; + + +import "../../contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol"; + +contract UpgradeV2 is Script { + // setup + UtilsScript public utils; + string public jsonLocation; + uint256 public deployerPrivateKey; + address public deployerAddress; + + // deployment vars + TrailblazersBadgesV5 public tokenV5; + BadgeRecruitmentV2 public badgeRecruitmentV2; + + // mainnet config + address public s1TokenAddress = 0xa20a8856e00F5ad024a55A663F06DCc419FFc4d5; + address public badgeRecruitmentAddress = 0xa9Ceb04F3aF71fF123409d426A92BABb5124970C; + +// hekla config + string baseURI = + "https://taikonfts.4everland.link/ipfs/bafybeiatuzeeeznd3hi5qiulslxcjd22ebu45t4fra2jvi3smhocr2c66a"; + + IMinimalBlacklist blacklist = IMinimalBlacklist(0xe61E9034b5633977eC98E302b33e321e8140F105); + address claimMintSigner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; + address recruitmentSigner = 0x3cda4F2EaC3fc2FdE78B3DFFe1A1A1Eff88c68c5; + + uint256 public MAX_INFLUENCES = 5; + uint256 public COOLDOWN_RECRUITMENT = 10 minutes; + uint256 public COOLDOWN_INFLUENCE = 5 minutes; + uint256 public INFLUENCE_WEIGHT_PERCENT = 9; + uint256 public MAX_INFLUENCES_DIVIDER = 100; + uint256 public DEFAULT_CYCLE_DURATION = 7 days; + uint256 public s1EndDate = 1_734_350_400; // Dec 16th 2024, noon UTC + uint256 public S1_LOCK_DURATION = (s1EndDate - block.timestamp); + + + function setUp() public { + utils = new UtilsScript(); + utils.setUp(); + + jsonLocation = utils.getContractJsonLocation(); + deployerPrivateKey = utils.getPrivateKey(); + deployerAddress = utils.getAddress(); + } + + function run() public { + vm.startBroadcast(deployerPrivateKey); + + address impl; + address proxy; + TrailblazersBadgesV4 s1Token; + TrailblazersBadgesS2 s2Token; + BadgeRecruitment badgeRecruitment; + + if (block.chainid == 167_000) { + // mainnet, use existing contract + s1Token = TrailblazersBadgesV4(s1TokenAddress); + badgeRecruitment = BadgeRecruitment(badgeRecruitmentAddress); + } else { + // non-mainnet, deploy contract chain + impl = address(new TrailblazersBadges()); + blacklist = new MockBlacklist(); + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall( + TrailblazersBadges.initialize, (deployerAddress, baseURI, claimMintSigner, blacklist) + ) + ) + ); + + TrailblazersBadges s1TokenV2 = TrailblazersBadges(proxy); + + // upgrade s1 contract to v4 + s1TokenV2.upgradeToAndCall( + address(new TrailblazersBadgesV4()), + abi.encodeCall(TrailblazersBadgesV4.version, ()) + ); + + s1Token = TrailblazersBadgesV4(address(s1TokenV2)); + + // deploy recruitment contract + BadgeRecruitment.Config memory config = BadgeRecruitment.Config( + COOLDOWN_RECRUITMENT, + COOLDOWN_INFLUENCE, + INFLUENCE_WEIGHT_PERCENT, + MAX_INFLUENCES, + MAX_INFLUENCES_DIVIDER, + DEFAULT_CYCLE_DURATION + ); + impl = address(new BadgeRecruitment()); + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall( + BadgeRecruitment.initialize, + (address(s1Token), address(s2Token), recruitmentSigner, config) + ) + ) + ); + + badgeRecruitment = BadgeRecruitment(proxy); + } + + // upgrade token contract + s1Token.upgradeToAndCall( + address(new TrailblazersBadgesV5()), + abi.encodeCall(TrailblazersBadgesV5.version, ()) + ); + + tokenV5 = TrailblazersBadgesV5(address(s1Token)); + console.log("Upgraded TrailblazersBadgesV4 to:", address(tokenV5)); + + // upgrade recruitment contract + badgeRecruitment.upgradeToAndCall( + address(new BadgeRecruitmentV2()), + abi.encodeCall(BadgeRecruitmentV2.version, ()) + ); + + badgeRecruitmentV2 = BadgeRecruitmentV2(address(badgeRecruitment)); + console.log("Upgraded BadgeRecruitment to:", address(badgeRecruitmentV2)); + + // set upgraded recruitment contract + tokenV5.setRecruitmentContractV2(address(badgeRecruitmentV2)); + console.log("Set recruitment contract to:", address(badgeRecruitmentV2)); + +/* + token.upgradeToAndCall( + address(new TrailblazersBadgesS2()), abi.encodeCall(TrailblazersBadgesS2.version, ()) + ); + + token = TrailblazersBadgesS2(address(token)); + + console.log("Upgraded TrailblazersBadgesV3 to:", address(token)); + + // update uri + token.setUri( + "https://taikonfts.4everland.link/ipfs/bafybeief7o4u6f676e6uz4yt4cv34ai4mesd7motoq6y4xxaoyjfbna5de" + ); + console.log("Updated token URI");*/ + } +} From b08d0f93d05c3bb0cc9c4b322b93cbbbd94845a9 Mon Sep 17 00:00:00 2001 From: bearni95 Date: Wed, 11 Dec 2024 14:19:59 +0100 Subject: [PATCH 08/12] fmt, lint --- .../RecruitmentUpgradeV2.s.sol | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol index 1c5ef35328..ef1e4e9f76 100644 --- a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol +++ b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol @@ -10,7 +10,6 @@ import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol"; import "../../contracts/trailblazers-season-2/TrailblazersBadgesS2.sol"; - import "../../contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol"; contract UpgradeV2 is Script { @@ -28,15 +27,15 @@ contract UpgradeV2 is Script { address public s1TokenAddress = 0xa20a8856e00F5ad024a55A663F06DCc419FFc4d5; address public badgeRecruitmentAddress = 0xa9Ceb04F3aF71fF123409d426A92BABb5124970C; -// hekla config - string baseURI = - "https://taikonfts.4everland.link/ipfs/bafybeiatuzeeeznd3hi5qiulslxcjd22ebu45t4fra2jvi3smhocr2c66a"; + // hekla config + string baseURI = + "https://taikonfts.4everland.link/ipfs/bafybeiatuzeeeznd3hi5qiulslxcjd22ebu45t4fra2jvi3smhocr2c66a"; IMinimalBlacklist blacklist = IMinimalBlacklist(0xe61E9034b5633977eC98E302b33e321e8140F105); address claimMintSigner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; address recruitmentSigner = 0x3cda4F2EaC3fc2FdE78B3DFFe1A1A1Eff88c68c5; - uint256 public MAX_INFLUENCES = 5; + uint256 public MAX_INFLUENCES = 5; uint256 public COOLDOWN_RECRUITMENT = 10 minutes; uint256 public COOLDOWN_INFLUENCE = 5 minutes; uint256 public INFLUENCE_WEIGHT_PERCENT = 9; @@ -45,7 +44,6 @@ contract UpgradeV2 is Script { uint256 public s1EndDate = 1_734_350_400; // Dec 16th 2024, noon UTC uint256 public S1_LOCK_DURATION = (s1EndDate - block.timestamp); - function setUp() public { utils = new UtilsScript(); utils.setUp(); @@ -64,7 +62,7 @@ contract UpgradeV2 is Script { TrailblazersBadgesS2 s2Token; BadgeRecruitment badgeRecruitment; - if (block.chainid == 167_000) { + if (block.chainid == 167_000) { // mainnet, use existing contract s1Token = TrailblazersBadgesV4(s1TokenAddress); badgeRecruitment = BadgeRecruitment(badgeRecruitmentAddress); @@ -76,7 +74,8 @@ contract UpgradeV2 is Script { new ERC1967Proxy( impl, abi.encodeCall( - TrailblazersBadges.initialize, (deployerAddress, baseURI, claimMintSigner, blacklist) + TrailblazersBadges.initialize, + (deployerAddress, baseURI, claimMintSigner, blacklist) ) ) ); @@ -93,31 +92,30 @@ contract UpgradeV2 is Script { // deploy recruitment contract BadgeRecruitment.Config memory config = BadgeRecruitment.Config( - COOLDOWN_RECRUITMENT, - COOLDOWN_INFLUENCE, - INFLUENCE_WEIGHT_PERCENT, - MAX_INFLUENCES, - MAX_INFLUENCES_DIVIDER, - DEFAULT_CYCLE_DURATION - ); + COOLDOWN_RECRUITMENT, + COOLDOWN_INFLUENCE, + INFLUENCE_WEIGHT_PERCENT, + MAX_INFLUENCES, + MAX_INFLUENCES_DIVIDER, + DEFAULT_CYCLE_DURATION + ); impl = address(new BadgeRecruitment()); - proxy = address( - new ERC1967Proxy( - impl, - abi.encodeCall( - BadgeRecruitment.initialize, - (address(s1Token), address(s2Token), recruitmentSigner, config) + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall( + BadgeRecruitment.initialize, + (address(s1Token), address(s2Token), recruitmentSigner, config) + ) ) - ) - ); + ); badgeRecruitment = BadgeRecruitment(proxy); } // upgrade token contract s1Token.upgradeToAndCall( - address(new TrailblazersBadgesV5()), - abi.encodeCall(TrailblazersBadgesV5.version, ()) + address(new TrailblazersBadgesV5()), abi.encodeCall(TrailblazersBadgesV5.version, ()) ); tokenV5 = TrailblazersBadgesV5(address(s1Token)); @@ -125,8 +123,7 @@ contract UpgradeV2 is Script { // upgrade recruitment contract badgeRecruitment.upgradeToAndCall( - address(new BadgeRecruitmentV2()), - abi.encodeCall(BadgeRecruitmentV2.version, ()) + address(new BadgeRecruitmentV2()), abi.encodeCall(BadgeRecruitmentV2.version, ()) ); badgeRecruitmentV2 = BadgeRecruitmentV2(address(badgeRecruitment)); @@ -136,9 +133,9 @@ contract UpgradeV2 is Script { tokenV5.setRecruitmentContractV2(address(badgeRecruitmentV2)); console.log("Set recruitment contract to:", address(badgeRecruitmentV2)); -/* + /* token.upgradeToAndCall( - address(new TrailblazersBadgesS2()), abi.encodeCall(TrailblazersBadgesS2.version, ()) + address(new TrailblazersBadgesS2()), abi.encodeCall(TrailblazersBadgesS2.version, ()) ); token = TrailblazersBadgesS2(address(token)); @@ -147,7 +144,7 @@ contract UpgradeV2 is Script { // update uri token.setUri( - "https://taikonfts.4everland.link/ipfs/bafybeief7o4u6f676e6uz4yt4cv34ai4mesd7motoq6y4xxaoyjfbna5de" + "https://taikonfts.4everland.link/ipfs/bafybeief7o4u6f676e6uz4yt4cv34ai4mesd7motoq6y4xxaoyjfbna5de" ); console.log("Updated token URI");*/ } From 42153253cfce4defecf26e1031b71c01b1bbe646 Mon Sep 17 00:00:00 2001 From: bearni95 Date: Wed, 11 Dec 2024 21:48:14 +0100 Subject: [PATCH 09/12] subgraph changes --- .../BadgeRecruitmentV2.sol | 14 +++++++++++-- .../trailblazers-season-2/hekla.json | 6 +++--- .../RecruitmentUpgradeV2.s.sol | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol index eb9d3fc1c5..50d93209d5 100644 --- a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol +++ b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol @@ -5,7 +5,9 @@ import "./BadgeRecruitment.sol"; contract BadgeRecruitmentV2 is BadgeRecruitment { /// @notice Events - event RecruitmentReset(address indexed user, uint256 indexed s1TokenId, uint256 s1BadgeId); + event RecruitmentReset( + uint256 indexed cycleId, address indexed user, uint256 indexed s1TokenId, uint256 s1BadgeId + ); /// @notice Errors error RECRUITMENT_ALREADY_COMPLETED(); @@ -43,6 +45,14 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { /// @dev Bypasses the default date checks function forceDisableAllRecruitments() external virtual onlyRole(DEFAULT_ADMIN_ROLE) { forceDisableRecruitments(); + // emit disabled badges + emit RecruitmentCycleToggled( + recruitmentCycleId, + recruitmentCycles[recruitmentCycleId].startTime, + recruitmentCycles[recruitmentCycleId].endTime, + recruitmentCycles[recruitmentCycleId].s1BadgeIds, + false + ); } /// @notice Get the active recruitment for a user @@ -90,6 +100,6 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Migration] = false; - emit RecruitmentReset(_user, _s1TokenId, s1BadgeId_); + emit RecruitmentReset(recruitmentCycleId, _user, _s1TokenId, s1BadgeId_); } } diff --git a/packages/nfts/deployments/trailblazers-season-2/hekla.json b/packages/nfts/deployments/trailblazers-season-2/hekla.json index 0b70fbc0bf..58c86249e2 100644 --- a/packages/nfts/deployments/trailblazers-season-2/hekla.json +++ b/packages/nfts/deployments/trailblazers-season-2/hekla.json @@ -1,6 +1,6 @@ { - "BadgeRecruitment": "0xBd368C65Cb354eBAd6c1429b551bD0197f19C2B8", + "BadgeRecruitment": "0x731E8687AF8e98207B1eaE76fEAbDd08fBf01e6a", "Owner": "0x4100a9B680B1Be1F10Cb8b5a57fE59eA77A8184e", - "TrailblazersBadges": "0x9E14C357E964BeE012bA82Ce9d6513dAec6ea961", - "TrailblazersBadgesS2": "0xc84B76a5836Cb0CeF094808af445F7E98504ED5B" + "TrailblazersBadges": "0xF38805C75F9E58F8766AB7585E3EBFc0C5F863ec", + "TrailblazersBadgesS2": "0x7aBB3b49e539081A43f1d6bd8aC7462187379E5a" } diff --git a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol index ef1e4e9f76..40a32cecbd 100644 --- a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol +++ b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol @@ -90,6 +90,8 @@ contract UpgradeV2 is Script { s1Token = TrailblazersBadgesV4(address(s1TokenV2)); + s1Token.setRecruitmentLockDuration(S1_LOCK_DURATION); + // deploy recruitment contract BadgeRecruitment.Config memory config = BadgeRecruitment.Config( COOLDOWN_RECRUITMENT, @@ -111,6 +113,25 @@ contract UpgradeV2 is Script { ); badgeRecruitment = BadgeRecruitment(proxy); + + // s2 token + impl = address(new TrailblazersBadgesS2()); + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall(TrailblazersBadgesS2.initialize, (deployerAddress, baseURI)) + ) + ); + + s2Token = TrailblazersBadgesS2(proxy); + + // overwrite json deployment data + string memory jsonRoot = "root"; + vm.serializeAddress(jsonRoot, "TrailblazersBadges", address(s1Token)); + vm.serializeAddress(jsonRoot, "TrailblazersBadgesS2", address(s2Token)); + vm.serializeAddress(jsonRoot, "BadgeRecruitment", address(badgeRecruitment)); + string memory finalJson = vm.serializeAddress(jsonRoot, "Owner", s2Token.owner()); + vm.writeJson(finalJson, jsonLocation); } // upgrade token contract From 49e9812620a6d5a8758b84db9d92f02c2fab3ab8 Mon Sep 17 00:00:00 2001 From: bearni95 Date: Thu, 12 Dec 2024 22:45:11 +0100 Subject: [PATCH 10/12] hekla deployment --- .../BadgeRecruitment.sol | 3 - .../BadgeRecruitmentV2.sol | 58 +++++++++++++------ .../TrailblazersS1BadgesV5.sol | 25 ++++---- .../trailblazers-season-2/hekla.json | 6 +- packages/nfts/package.json | 2 +- .../RecruitmentUpgradeV2.s.sol | 26 +++++---- .../BadgeRecruitmentV2.t.sol | 31 ++++++++-- 7 files changed, 96 insertions(+), 55 deletions(-) diff --git a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitment.sol b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitment.sol index 4b2e1f57a9..44e93329ad 100644 --- a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitment.sol +++ b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitment.sol @@ -37,9 +37,6 @@ contract BadgeRecruitment is TrailblazersBadgesS2 public s2Badges; /// @notice Wallet authorized to sign as a source of randomness address public randomSigner; - /// @notice Recruitment-enabled badge IDs per cycle - //mapping(uint256 cycle => mapping(uint256 s1BadgeId => bool enabled)) public enabledBadgeIds; - // uint256[] public currentCycleEnabledRecruitmentIds; /// @notice Current recruitment cycle uint256 public recruitmentCycleId; diff --git a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol index 50d93209d5..ccefcac56b 100644 --- a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol +++ b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol @@ -11,6 +11,7 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { /// @notice Errors error RECRUITMENT_ALREADY_COMPLETED(); + error RECRUITMENT_NOT_FOUND(); /// @notice Updated version function function version() external pure virtual returns (string memory) { @@ -45,7 +46,7 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { /// @dev Bypasses the default date checks function forceDisableAllRecruitments() external virtual onlyRole(DEFAULT_ADMIN_ROLE) { forceDisableRecruitments(); - // emit disabled badges + emit RecruitmentCycleToggled( recruitmentCycleId, recruitmentCycles[recruitmentCycleId].startTime, @@ -67,39 +68,62 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { /// @notice Reset a recruitment that hasn't been completed /// @param _user The user address - /// @param _recruitmentIdx The recruitment index /// @param _s1TokenId The s1 token ID + /// @param _s1BadgeId The s1 badge ID + /// @param _recruitmentCycle The recruitment index /// @dev Must be called from the s1 badges contract function resetRecruitment( address _user, - uint256 _recruitmentIdx, - uint256 _s1TokenId + uint256 _s1TokenId, + uint256 _s1BadgeId, + uint256 _recruitmentCycle ) public virtual onlyRole(S1_BADGES_ROLE) { - Recruitment memory recruitment_ = recruitments[_user][_recruitmentIdx]; - if (recruitment_.s1TokenId != _s1TokenId) { - revert RECRUITMENT_NOT_STARTED(); + if ( + !recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType + .Migration] + && !recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Claim] + && !recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType + .Undefined] + ) { + revert RECRUITMENT_NOT_FOUND(); } - if (recruitment_.s2TokenId != 0) { - revert RECRUITMENT_ALREADY_COMPLETED(); + bool found = false; + + for (uint256 i = 0; i < recruitments[_user].length; i++) { + if ( + recruitments[_user][i].recruitmentCycle == _recruitmentCycle + && recruitments[_user][i].s1TokenId == _s1TokenId + && recruitments[_user][i].s2TokenId == 0 + ) { + delete recruitments[_user][i]; + found = true; + break; + } } - // reset - uint256 s1BadgeId_ = recruitment_.s1BadgeId; + if (!found) { + revert RECRUITMENT_NOT_FOUND(); + } - //delete - delete recruitments[_user][_recruitmentIdx]; - recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Undefined] + recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Undefined] = false; - recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Claim] = + recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Claim] = false; - recruitmentCycleUniqueMints[recruitmentCycleId][_user][s1BadgeId_][RecruitmentType.Migration] + recruitmentCycleUniqueMints[_recruitmentCycle][_user][_s1BadgeId][RecruitmentType.Migration] = false; - emit RecruitmentReset(recruitmentCycleId, _user, _s1TokenId, s1BadgeId_); + emit RecruitmentReset(_recruitmentCycle, _user, _s1TokenId, _s1BadgeId); + } + + /// @notice Set the s2 badges contract + /// @param _s2Badges The s2 badges contract address + /// @dev Must be called from the admin account + function setS2BadgesContract(address _s2Badges) external virtual onlyRole(DEFAULT_ADMIN_ROLE) { + s2Badges = TrailblazersBadgesS2(_s2Badges); } } diff --git a/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol b/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol index a3b1ad0558..9dbf894206 100644 --- a/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol +++ b/packages/nfts/contracts/trailblazers-season-2/TrailblazersS1BadgesV5.sol @@ -10,20 +10,24 @@ contract TrailblazersBadgesV5 is TrailblazersBadgesV4 { error RECRUITMENT_ALREADY_COMPLETED(); error NOT_OWNER(); error NOT_IMPLEMENTED(); + error RECRUITMENT_NOT_FOUND(); /// @notice Updated version function /// @return Version string function version() external pure virtual override returns (string memory) { return "V5"; } + /// @notice Recruitment contract BadgeRecruitmentV2 public recruitmentContractV2; + /// @notice Setter for recruitment contract function setRecruitmentContractV2(address _recruitmentContractV2) public onlyOwner { recruitmentContractV2 = BadgeRecruitmentV2(_recruitmentContractV2); } /// @notice Start recruitment for a badge + /// @param _badgeId Badge ID /// @param _tokenId Token ID function startRecruitment(uint256 _badgeId, uint256 _tokenId) public { if (recruitmentLockDuration == 0) { @@ -48,24 +52,15 @@ contract TrailblazersBadgesV5 is TrailblazersBadgesV4 { /// @notice Reset an ongoing migration /// @param _tokenId Token ID - function resetMigration(uint256 _tokenId) public virtual { + /// @param _badgeId Badge ID + /// @param _cycleId Cycle ID + /// @dev Only the owner of the token can reset the migration + function resetMigration(uint256 _tokenId, uint256 _badgeId, uint256 _cycleId) public virtual { if (ownerOf(_tokenId) != _msgSender()) { revert NOT_OWNER(); } - BadgeRecruitment.Recruitment[] memory recruitments_ = - recruitmentContractV2.getActiveRecruitmentsFor(_msgSender()); - - for (uint256 i = 0; i < recruitments_.length; i++) { - // check if the recruitment is ongoing - // AND if it isn't finished (s2TokenId == 0) - if (recruitments_[i].s1TokenId == _tokenId && recruitments_[i].s2TokenId == 0) { - unlockTimestamps[_tokenId] = 0; - recruitmentContractV2.resetRecruitment(_msgSender(), i, _tokenId); - return; - } - } - - revert RECRUITMENT_ALREADY_COMPLETED(); + recruitmentContractV2.resetRecruitment(_msgSender(), _tokenId, _badgeId, _cycleId); + unlockTimestamps[_tokenId] = 0; } } diff --git a/packages/nfts/deployments/trailblazers-season-2/hekla.json b/packages/nfts/deployments/trailblazers-season-2/hekla.json index 58c86249e2..23b05d5ecb 100644 --- a/packages/nfts/deployments/trailblazers-season-2/hekla.json +++ b/packages/nfts/deployments/trailblazers-season-2/hekla.json @@ -1,6 +1,6 @@ { - "BadgeRecruitment": "0x731E8687AF8e98207B1eaE76fEAbDd08fBf01e6a", + "BadgeRecruitment": "0xcb00B57e8F5fCFffE87bb65f3047b6e4e5A73cA9", "Owner": "0x4100a9B680B1Be1F10Cb8b5a57fE59eA77A8184e", - "TrailblazersBadges": "0xF38805C75F9E58F8766AB7585E3EBFc0C5F863ec", - "TrailblazersBadgesS2": "0x7aBB3b49e539081A43f1d6bd8aC7462187379E5a" + "TrailblazersBadges": "0x3a7d7c963EF905FCdb6CefAA21b52497fae3EFC4", + "TrailblazersBadgesS2": "0xDE43b7b9A485d76bc8D48a69DdE6b89540b27DdD" } diff --git a/packages/nfts/package.json b/packages/nfts/package.json index 3c3238129d..9fa2c30638 100644 --- a/packages/nfts/package.json +++ b/packages/nfts/package.json @@ -37,7 +37,7 @@ "pfp:deploy:hekla": "forge clean && pnpm compile && forge script script/profile/Deploy.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200", "pfp:deploy:mainnet": "forge clean && pnpm compile && forge script script/profile/Deploy.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 200", "tbz:airdrop:hekla": "forge clean && pnpm compile && forge script script/trailblazers-airdrop/Deploy.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200", - "tbz:airdrop:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-airdrop/Deploy.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", + "tbz:airdrop:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-airdrop/Deploy.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --verify --broadcast --gas-estimate-multiplier 100", "tbz:upgradeV3:hekla": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/UpgradeV3.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200", "tbz:upgradeV3:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/UpgradeV3.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", "tbz:upgradeV4:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-badges/UpgradeV4.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", diff --git a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol index 40a32cecbd..d298f8df31 100644 --- a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol +++ b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol @@ -92,6 +92,17 @@ contract UpgradeV2 is Script { s1Token.setRecruitmentLockDuration(S1_LOCK_DURATION); + // deploy s2 badges + impl = address(new TrailblazersBadgesS2()); + proxy = address( + new ERC1967Proxy( + impl, + abi.encodeCall(TrailblazersBadgesS2.initialize, (deployerAddress, baseURI)) + ) + ); + + s2Token = TrailblazersBadgesS2(proxy); + // deploy recruitment contract BadgeRecruitment.Config memory config = BadgeRecruitment.Config( COOLDOWN_RECRUITMENT, @@ -114,17 +125,6 @@ contract UpgradeV2 is Script { badgeRecruitment = BadgeRecruitment(proxy); - // s2 token - impl = address(new TrailblazersBadgesS2()); - proxy = address( - new ERC1967Proxy( - impl, - abi.encodeCall(TrailblazersBadgesS2.initialize, (deployerAddress, baseURI)) - ) - ); - - s2Token = TrailblazersBadgesS2(proxy); - // overwrite json deployment data string memory jsonRoot = "root"; vm.serializeAddress(jsonRoot, "TrailblazersBadges", address(s1Token)); @@ -132,6 +132,10 @@ contract UpgradeV2 is Script { vm.serializeAddress(jsonRoot, "BadgeRecruitment", address(badgeRecruitment)); string memory finalJson = vm.serializeAddress(jsonRoot, "Owner", s2Token.owner()); vm.writeJson(finalJson, jsonLocation); + + // further setup + s1Token.setRecruitmentContract(address(badgeRecruitment)); + s2Token.setMinter(address(badgeRecruitment)); } // upgrade token contract diff --git a/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol b/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol index 17fde02d9a..e48c71a285 100644 --- a/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol +++ b/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol @@ -257,6 +257,21 @@ contract BadgeRecruitmentV2Test is Test { function test_resetRecruitment() public { test_full_recruitment(); + uint256 initialCycleId = recruitment.recruitmentCycleId(); + + assertEq(initialCycleId, 2); + // roll forward the cycle + vm.startPrank(owner); + + uint256[] memory enabledBadgeIds = new uint256[](1); + enabledBadgeIds[0] = BADGE_ID; + recruitment.forceDisableAllRecruitments(); + recruitment.enableRecruitments(enabledBadgeIds); + vm.stopPrank(); + + uint256 cycleId = recruitment.recruitmentCycleId(); + assertEq(cycleId, 3); + address minter = minters[0]; uint256 ongoingTokenId = s1BadgesV5.tokenOfOwnerByIndex(minter, 1); uint256 completedTokenId = s1BadgesV5.tokenOfOwnerByIndex(minter, 0); @@ -266,11 +281,17 @@ contract BadgeRecruitmentV2Test is Test { vm.startPrank(minter); // expect revert from the completed migration - vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_ALREADY_COMPLETED.selector); - s1BadgesV5.resetMigration(completedTokenId); + vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_NOT_FOUND.selector); + s1BadgesV5.resetMigration(completedTokenId, BADGE_ID, initialCycleId); + // on both cycles + vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_NOT_FOUND.selector); + s1BadgesV5.resetMigration(completedTokenId, BADGE_ID, cycleId); + // as well as for the ongoing migration on the current cycle + vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_NOT_FOUND.selector); + s1BadgesV5.resetMigration(ongoingTokenId, BADGE_ID, cycleId); // reset the ongoing recruitment - s1BadgesV5.resetMigration(ongoingTokenId); + s1BadgesV5.resetMigration(ongoingTokenId, BADGE_ID, initialCycleId); // start over the ongoing migration wait(100); @@ -300,7 +321,7 @@ contract BadgeRecruitmentV2Test is Test { assertEq(s2Badges.balanceOf(minter, 2), 1); // fail to reset the ongoing migration - vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_ALREADY_COMPLETED.selector); - s1BadgesV5.resetMigration(ongoingTokenId); + vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_NOT_FOUND.selector); + s1BadgesV5.resetMigration(ongoingTokenId, BADGE_ID, cycleId); } } From 3de9e1ef18c041887a751e2a4aea1612c9ddd43c Mon Sep 17 00:00:00 2001 From: bearni95 Date: Fri, 13 Dec 2024 15:15:57 +0100 Subject: [PATCH 11/12] added check to not allow starting recruitments too close to end cycle time --- .../BadgeRecruitmentV2.sol | 12 ++++++++++ .../BadgeRecruitmentV2.t.sol | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol index ccefcac56b..cb14278a37 100644 --- a/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol +++ b/packages/nfts/contracts/trailblazers-season-2/BadgeRecruitmentV2.sol @@ -12,6 +12,17 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { /// @notice Errors error RECRUITMENT_ALREADY_COMPLETED(); error RECRUITMENT_NOT_FOUND(); + error NOT_ENOUGH_TIME_LEFT(); + + modifier recruitmentHasTimeLeft(address _user) { + uint256 endCycleTime = recruitmentCycles[recruitmentCycleId].endTime; + uint256 potentialRecruitmentEndTime = block.timestamp + this.getConfig().cooldownRecruitment; + + if (potentialRecruitmentEndTime > endCycleTime) { + revert NOT_ENOUGH_TIME_LEFT(); + } + _; + } /// @notice Updated version function function version() external pure virtual returns (string memory) { @@ -35,6 +46,7 @@ contract BadgeRecruitmentV2 is BadgeRecruitment { recruitmentOpen(_s1BadgeId) isNotMigrating(_user) hasntMigratedInCycle(_s1BadgeId, _user, RecruitmentType.Migration) + recruitmentHasTimeLeft(_user) { if (s1Badges.ownerOf(_s1TokenId) != _user) { revert TOKEN_NOT_OWNED(); diff --git a/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol b/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol index e48c71a285..9113a5fa12 100644 --- a/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol +++ b/packages/nfts/test/trailblazers-season-2/BadgeRecruitmentV2.t.sol @@ -324,4 +324,26 @@ contract BadgeRecruitmentV2Test is Test { vm.expectRevert(TrailblazersBadgesV5.RECRUITMENT_NOT_FOUND.selector); s1BadgesV5.resetMigration(ongoingTokenId, BADGE_ID, cycleId); } + + function test_revertStartTooCloseToCycleEnd() public { + test_upgradeV2(); + address minter = minters[0]; + vm.startPrank(minter); + + // mint the badge + bytes32 _hash = s1BadgesV5.getHash(minter, BADGE_ID); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mintSignerPk, _hash); + bool canMint = s1BadgesV5.canMint(abi.encodePacked(r, s, v), minter, BADGE_ID); + assertTrue(canMint); + + s1BadgesV5.mint(abi.encodePacked(r, s, v), BADGE_ID); + uint256 tokenId = s1BadgesV5.tokenOfOwnerByIndex(minter, 0); + + // wait until almost the end + wait(DEFAULT_CYCLE_DURATION - COOLDOWN_RECRUITMENT + 1); + vm.expectRevert(BadgeRecruitmentV2.NOT_ENOUGH_TIME_LEFT.selector); + s1BadgesV5.startRecruitment(BADGE_ID, tokenId); + assertEq(recruitment.isRecruitmentActive(minter), false); + vm.stopPrank(); + } } From dc770a75fc57fe0696ccf327c9e302c72067d07e Mon Sep 17 00:00:00 2001 From: bearni95 Date: Mon, 16 Dec 2024 13:26:02 +0100 Subject: [PATCH 12/12] mainnet deployment --- packages/nfts/package.json | 3 +- .../RecruitmentUpgradeV2.s.sol | 29 +++++-------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/nfts/package.json b/packages/nfts/package.json index 9fa2c30638..9203661d0a 100644 --- a/packages/nfts/package.json +++ b/packages/nfts/package.json @@ -42,7 +42,8 @@ "tbz:upgradeV3:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/UpgradeV3.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", "tbz:upgradeV4:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-badges/UpgradeV4.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", "tbz-s2:upgradeV2:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-season-2/UpgradeV2.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100", - "tbz-s2:upgradeRecruitmentV2:hekla": "forge clean && pnpm compile && forge script script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200" + "tbz-s2:upgradeRecruitmentV2:hekla": "forge clean && pnpm compile && forge script script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200", + "tbz-s2:upgradeRecruitmentV2:mainnet": "forge clean && pnpm compile && forge script script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --gas-estimate-multiplier 100" }, "devDependencies": { "@types/node": "^20.11.30", diff --git a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol index d298f8df31..50af179fe7 100644 --- a/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol +++ b/packages/nfts/script/trailblazers-season-2/RecruitmentUpgradeV2.s.sol @@ -26,7 +26,7 @@ contract UpgradeV2 is Script { // mainnet config address public s1TokenAddress = 0xa20a8856e00F5ad024a55A663F06DCc419FFc4d5; address public badgeRecruitmentAddress = 0xa9Ceb04F3aF71fF123409d426A92BABb5124970C; - +/* // hekla config string baseURI = "https://taikonfts.4everland.link/ipfs/bafybeiatuzeeeznd3hi5qiulslxcjd22ebu45t4fra2jvi3smhocr2c66a"; @@ -35,7 +35,7 @@ contract UpgradeV2 is Script { address claimMintSigner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; address recruitmentSigner = 0x3cda4F2EaC3fc2FdE78B3DFFe1A1A1Eff88c68c5; - uint256 public MAX_INFLUENCES = 5; + uint256 public MAX_INFLUENCES = 5; uint256 public COOLDOWN_RECRUITMENT = 10 minutes; uint256 public COOLDOWN_INFLUENCE = 5 minutes; uint256 public INFLUENCE_WEIGHT_PERCENT = 9; @@ -43,7 +43,7 @@ contract UpgradeV2 is Script { uint256 public DEFAULT_CYCLE_DURATION = 7 days; uint256 public s1EndDate = 1_734_350_400; // Dec 16th 2024, noon UTC uint256 public S1_LOCK_DURATION = (s1EndDate - block.timestamp); - +*/ function setUp() public { utils = new UtilsScript(); utils.setUp(); @@ -56,10 +56,10 @@ contract UpgradeV2 is Script { function run() public { vm.startBroadcast(deployerPrivateKey); - address impl; - address proxy; + // address impl; + // address proxy; TrailblazersBadgesV4 s1Token; - TrailblazersBadgesS2 s2Token; + // TrailblazersBadgesS2 s2Token; BadgeRecruitment badgeRecruitment; if (block.chainid == 167_000) { @@ -67,6 +67,7 @@ contract UpgradeV2 is Script { s1Token = TrailblazersBadgesV4(s1TokenAddress); badgeRecruitment = BadgeRecruitment(badgeRecruitmentAddress); } else { + /* // non-mainnet, deploy contract chain impl = address(new TrailblazersBadges()); blacklist = new MockBlacklist(); @@ -136,6 +137,7 @@ contract UpgradeV2 is Script { // further setup s1Token.setRecruitmentContract(address(badgeRecruitment)); s2Token.setMinter(address(badgeRecruitment)); + */ } // upgrade token contract @@ -157,20 +159,5 @@ contract UpgradeV2 is Script { // set upgraded recruitment contract tokenV5.setRecruitmentContractV2(address(badgeRecruitmentV2)); console.log("Set recruitment contract to:", address(badgeRecruitmentV2)); - - /* - token.upgradeToAndCall( - address(new TrailblazersBadgesS2()), abi.encodeCall(TrailblazersBadgesS2.version, ()) - ); - - token = TrailblazersBadgesS2(address(token)); - - console.log("Upgraded TrailblazersBadgesV3 to:", address(token)); - - // update uri - token.setUri( - "https://taikonfts.4everland.link/ipfs/bafybeief7o4u6f676e6uz4yt4cv34ai4mesd7motoq6y4xxaoyjfbna5de" - ); - console.log("Updated token URI");*/ } }