diff --git a/packages/nfts/contracts/trailblazers-badges/TrailblazersBadgesV3.sol b/packages/nfts/contracts/trailblazers-badges/TrailblazersBadgesV3.sol new file mode 100644 index 0000000000..3d6c0e511b --- /dev/null +++ b/packages/nfts/contracts/trailblazers-badges/TrailblazersBadgesV3.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "./TrailblazersBadges.sol"; + +contract TrailblazersBadgesV3 is TrailblazersBadges { + function version() external pure returns (string memory) { + return "V3"; + } + + function _update( + address to, + uint256 tokenId, + address auth + ) + internal + virtual + override + returns (address) + { + if (blacklist.isBlacklisted(_msgSender())) revert ADDRESS_BLACKLISTED(); + return super._update(to, tokenId, auth); + } +} diff --git a/packages/nfts/script/profile/Deploy.s.sol b/packages/nfts/script/profile/Deploy.s.sol index 1e4911e62f..f032a7a69b 100644 --- a/packages/nfts/script/profile/Deploy.s.sol +++ b/packages/nfts/script/profile/Deploy.s.sol @@ -28,20 +28,15 @@ contract DeployScript is Script { // deploy token with empty root address impl = address(new RegisterProfilePicture()); - address proxy = address( - new ERC1967Proxy( - impl, - abi.encodeCall( - RegisterProfilePicture.initialize, () - ) - ) - ); + address proxy = + address(new ERC1967Proxy(impl, abi.encodeCall(RegisterProfilePicture.initialize, ()))); RegisterProfilePicture profile = RegisterProfilePicture(proxy); console.log("Deployed Trailblazers PFP to:", address(profile)); - string memory finalJson = vm.serializeAddress(jsonRoot, "RegisterProfilePicture", address(profile)); + string memory finalJson = + vm.serializeAddress(jsonRoot, "RegisterProfilePicture", address(profile)); vm.writeJson(finalJson, jsonLocation); vm.stopBroadcast(); diff --git a/packages/nfts/script/trailblazers-badges/sol/UpgradeV3.sol b/packages/nfts/script/trailblazers-badges/sol/UpgradeV3.sol new file mode 100644 index 0000000000..d51919abf5 --- /dev/null +++ b/packages/nfts/script/trailblazers-badges/sol/UpgradeV3.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import { UtilsScript } 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 { TrailblazersBadgesV3 } from + "../../../contracts/trailblazers-badges/TrailblazersBadgesV3.sol"; + +contract UpgradeV2 is Script { + UtilsScript public utils; + string public jsonLocation; + uint256 public deployerPrivateKey; + address public deployerAddress; + + address tokenV2Address = 0xa20a8856e00F5ad024a55A663F06DCc419FFc4d5; + TrailblazersBadges public tokenV2; + TrailblazersBadgesV3 public tokenV3; + + function setUp() public { + utils = new UtilsScript(); + utils.setUp(); + + jsonLocation = utils.getContractJsonLocation(); + deployerPrivateKey = utils.getPrivateKey(); + deployerAddress = utils.getAddress(); + } + + function run() public { + vm.startBroadcast(deployerPrivateKey); + tokenV2 = TrailblazersBadges(tokenV2Address); + + tokenV2.upgradeToAndCall( + address(new TrailblazersBadgesV3()), abi.encodeCall(TrailblazersBadgesV3.version, ()) + ); + + tokenV3 = TrailblazersBadgesV3(tokenV3); + + console.log("Upgraded TrailblazersBadgesV3 to:", address(tokenV3)); + } +} diff --git a/packages/nfts/test/trailblazers-badges/TrailblazersBadgesV3.t.sol b/packages/nfts/test/trailblazers-badges/TrailblazersBadgesV3.t.sol new file mode 100644 index 0000000000..d4f4bc3400 --- /dev/null +++ b/packages/nfts/test/trailblazers-badges/TrailblazersBadgesV3.t.sol @@ -0,0 +1,87 @@ +// 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 { TrailblazersBadgesV3 } from "../../contracts/trailblazers-badges/TrailblazersBadgesV3.sol"; + +contract TrailblazersBadgesV3Test is Test { + UtilsScript public utils; + + TrailblazersBadges public tokenV2; + TrailblazersBadgesV3 public tokenV3; + + address public owner = vm.addr(0x5); + + address[3] public minters = [vm.addr(0x1), vm.addr(0x2), vm.addr(0x3)]; + + uint256 constant BADGE_ID = 5; + + MockBlacklist public blacklist; + + Merkle tree = new Merkle(); + + address mintSigner; + uint256 mintSignerPk; + + function setUp() public { + utils = new UtilsScript(); + utils.setUp(); + blacklist = new MockBlacklist(); + // create whitelist merkle tree + vm.startBroadcast(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) + ) + ) + ); + + tokenV2 = TrailblazersBadges(proxy); + // upgrade to v3 + tokenV2.upgradeToAndCall( + address(new TrailblazersBadgesV3()), abi.encodeCall(TrailblazersBadgesV3.version, ()) + ); + + tokenV3 = TrailblazersBadgesV3(address(proxy)); + vm.stopBroadcast(); + } + + function test_mint() public { + bytes32 _hash = tokenV3.getHash(minters[0], BADGE_ID); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mintSignerPk, _hash); + + bool canMint = tokenV3.canMint(abi.encodePacked(r, s, v), minters[0], BADGE_ID); + assertTrue(canMint); + + vm.startPrank(minters[0]); + tokenV3.mint(abi.encodePacked(r, s, v), BADGE_ID); + vm.stopPrank(); + + assertEq(tokenV3.balanceOf(minters[0]), 1); + } + + function test_blacklist_mint_revert() public { + test_mint(); + assertEq(tokenV3.balanceOf(minters[0]), 1); + blacklist.add(minters[0]); + + vm.prank(minters[0]); + vm.expectRevert(); + tokenV3.transferFrom(minters[0], minters[1], BADGE_ID); + } +} diff --git a/packages/nfts/test/util/Blacklist.sol b/packages/nfts/test/util/Blacklist.sol index 890bc15aff..f438812694 100644 --- a/packages/nfts/test/util/Blacklist.sol +++ b/packages/nfts/test/util/Blacklist.sol @@ -24,4 +24,8 @@ contract MockBlacklist is IMinimalBlacklist { } return false; } + + function add(address _address) public { + blacklist.push(_address); + } }