From eff2ea9b359efb49f42931495b57dda15f9bd4b0 Mon Sep 17 00:00:00 2001 From: Jonathan Diep Date: Tue, 30 Apr 2024 23:58:17 -0700 Subject: [PATCH] feat: Create `BlastQuestFactory` to configure operator --- contracts/BlastQuestFactory.sol | 16 ++++++++ script/QuestFactory.s.sol | 18 +++++++++ test/BlastQuestFactory.t.sol | 70 +++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 contracts/BlastQuestFactory.sol create mode 100644 test/BlastQuestFactory.t.sol diff --git a/contracts/BlastQuestFactory.sol b/contracts/BlastQuestFactory.sol new file mode 100644 index 00000000..f3dbbe99 --- /dev/null +++ b/contracts/BlastQuestFactory.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {QuestFactory} from "./QuestFactory.sol"; + +interface IBlastPoints { + function configurePointsOperator(address operator) external; + function configurePointsOperatorOnBehalf(address contractAddress, address operator) external; +} + +contract BlastQuestFactory is QuestFactory { + function configurePointsOperator(address operatorAddress) external onlyOwner { + address BlastPointsAddress = 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800; // BlastPoints Mainnet address + IBlastPoints(BlastPointsAddress).configurePointsOperator(operatorAddress); + } +} \ No newline at end of file diff --git a/script/QuestFactory.s.sol b/script/QuestFactory.s.sol index 69a050f0..dac9d431 100644 --- a/script/QuestFactory.s.sol +++ b/script/QuestFactory.s.sol @@ -5,6 +5,7 @@ import {Script} from "forge-std/Script.sol"; import {Quest} from "../contracts/Quest.sol"; import {Quest1155} from "../contracts/Quest1155.sol"; import {QuestFactory} from "../contracts/QuestFactory.sol"; +import {BlastQuestFactory} from "../contracts/BlastQuestFactory.sol"; import {QuestContractConstants as C} from "../contracts/libraries/QuestContractConstants.sol"; import {ProxyAdmin, ITransparentUpgradeableProxy} from "openzeppelin-contracts/proxy/transparent/ProxyAdmin.sol"; @@ -25,6 +26,23 @@ contract QuestFactoryUpgrade is Script { } } +// To upgrade QuestFactory.sol on Blast, run this command below +// ! important: make sure storage layouts are compatible first: +// forge clean && forge build && npx @openzeppelin/upgrades-core validate --contract QuestFactory +// forge script script/QuestFactory.s.sol:BlastQuestFactoryUpgrade --rpc-url blast --broadcast --verify -vvvv +contract BlastQuestFactoryUpgrade is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("MAINNET_PRIVATE_KEY"); + ITransparentUpgradeableProxy questfactoryProxy = ITransparentUpgradeableProxy(C.QUEST_FACTORY_ADDRESS); + + vm.startBroadcast(deployerPrivateKey); + + ProxyAdmin(C.PROXY_ADMIN_ADDRESS).upgrade(questfactoryProxy, address(new BlastQuestFactory())); + + vm.stopBroadcast(); + } +} + contract QuestFactoryDeploy is Script { function run() external { uint256 deployerPrivateKey = vm.envUint("MAINNET_PRIVATE_KEY"); diff --git a/test/BlastQuestFactory.t.sol b/test/BlastQuestFactory.t.sol new file mode 100644 index 00000000..f0e7ce93 --- /dev/null +++ b/test/BlastQuestFactory.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.19; + +// solhint-disable no-global-import, no-console +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BlastQuestFactory} from "contracts/BlastQuestFactory.sol"; +import {QuestFactory} from "contracts/QuestFactory.sol"; +import {IQuestFactory} from "contracts/interfaces/IQuestFactory.sol"; +import {Quest} from "contracts/Quest.sol"; +import {Quest1155} from "contracts/Quest1155.sol"; +import {LibClone} from "solady/utils/LibClone.sol"; +import {LibString} from "solady/utils/LibString.sol"; +import {ECDSA} from "openzeppelin-contracts/utils/cryptography/ECDSA.sol"; +import {LibZip} from "solady/utils/LibZip.sol"; +import {JSONParserLib} from "solady/utils/JSONParserLib.sol"; +import {Errors} from "./helpers/Errors.sol"; +import {Events} from "./helpers/Events.sol"; +import {TestUtils} from "./helpers/TestUtils.sol"; +import {QuestContractConstants as C} from "../contracts/libraries/QuestContractConstants.sol"; +import {ProxyAdmin, ITransparentUpgradeableProxy} from "openzeppelin-contracts/proxy/transparent/ProxyAdmin.sol"; + +interface IBlastPoints { + function configurePointsOperator(address operator) external; + function configurePointsOperatorOnBehalf(address contractAddress, address operator) external; + function operators(address operator) external returns (address); +} + +// forge test --fork-url https://rpc.blast.io --match-path test/BlastQuestFactory.t.sol +contract TestQuestFactory is Test, Errors, Events, TestUtils { + using LibClone for address; + using LibString for address; + using LibString for string; + using JSONParserLib for string; + using LibString for uint256; + + QuestFactory questFactory; + BlastQuestFactory blastQuestFactory; + address owner; + address random = makeAddr("random"); + address blastPointsAddress = 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800; // BlastPoints Mainnet address + + function setUp() public { + questFactory = QuestFactory(C.QUEST_FACTORY_ADDRESS); + owner = questFactory.owner(); + + vm.startPrank(owner); + // upgrade QuestFactory to BlastQuestFactory + ITransparentUpgradeableProxy questfactoryProxy = ITransparentUpgradeableProxy(C.QUEST_FACTORY_ADDRESS); + ProxyAdmin(C.PROXY_ADMIN_ADDRESS).upgrade(questfactoryProxy, address(new BlastQuestFactory())); + blastQuestFactory = BlastQuestFactory(C.QUEST_FACTORY_ADDRESS); + vm.stopPrank(); + } + + function test_configurePointsOperator() public { + vm.deal(random, 1 ether); + + vm.prank(random); + vm.expectRevert(); + blastQuestFactory.configurePointsOperator(random); + + vm.prank(owner); + blastQuestFactory.configurePointsOperator(owner); + + address operatorAddress = IBlastPoints(blastPointsAddress).operators(C.QUEST_FACTORY_ADDRESS); + assertEq(operatorAddress, owner); + } + +} \ No newline at end of file